教你从零开始用WebSocket打造一个IM聊天室

之前我们在 IM即时聊天室(一):WebSocket 和 IM即时聊天室(二): Socket.io + Node.js
两篇文章中介绍了搭建一个IM的所需的技术栈和通信原理。那在这篇文章里我们就来详细说一下具体的应用并提供完整源码。
个人Blog地址:IM即时聊天室(三):项目详解及源码分析

聊天室在线地址:点此进入


PS: 这是我网络编程课的一个小作业,因为博主比较懒所以这篇文章的大部分由我的课程报告组成(逃)


实现目标

应用实例是一个多人聊天室程序,用户可以通过指定服务器IP进入聊天室和聊天室内其他用户进行聊天,聊天室的功能主要有文本、表情和图片传输,并在Web界面中显示。

需求分析

  1. 应用采用客户/服务器模式,分为客户端程序和服务器端程序。

  2. 用户通过客户端可以选择头像,发送信息、表情、图片

  3. 服务端可以同时接收多个客户端信息,并根据需求进行转发、广播或者回传给客户端特定的消息。

  4. 客户端程序和服务器程序通过网络交换聊天的字符串内容(其中表情和头像为特殊格式代码,图片为转码后获得的URL格式)

话不多说,先看程序效果展示:

技术实现

  1. 通信协议选择
    Balabalabala… 详见下面两篇文章
    IM即时聊天室(一):WebSocket
    IM即时聊天室(二):Socket.io + Node.js

  2. 客户端
    UI界面:HTML+CSS+Bootstrap自适应

界面交互:原生Javascript+jQuery

消息的发送和接收:Socket.io

PS:由于客户端代码太太太太太多了,我只挑比较有特点的的讲一下

登录啥的就不讲了,大概思路就是开始时隐藏聊天界面,登录成功后隐藏登录界面显出聊天界面,源码里有完整的注释

先贴一下客户端代码大致结构图:
教你从零开始用WebSocket打造一个IM聊天室_第1张图片

比较有意思的代码是我仿照微信做了一个回到最新消息处的功能。

当我们在聊天时,我们会希望最新的消息实时显示出来,但是当我们在看历史消息时,可不希望被强行定位到最新消息处。并且在查看历史消息时,可以通过按钮回到最新消息处。

于是我们就要判断用户在查看历史消息还是正在聊天,方法就是判断最新的消息是否出现在了用户窗口里。

关键代码:

```
/*将页面下拉到最新消息处*/
	function scrollToEnd(){
			var div = document.getElementsByTagName("div");
				div_length = div.length-4;
		
			div[div_length].scrollIntoView({behavior: "smooth"});	   
			//平滑滚动,提高了用户体验
	
	}
	
	/*判断当有新信息来时,用户是否在页面底端*/
	function isNewInWindow(){
			var div = document.getElementsByTagName("div");
			div_length = div.length-5;
	
			if(isInWindow(div[div_length])){
				return true;
			}
			return false;
	}
	
	/*判定元素是否在界面内*/
	function isInWindow(x){		
			if(x.getBoundingClientRect().top > window.innerHeight){
				console.log("down");
				return false;
			}
			else if(x.getBoundingClientRect().bottom < 0){
				console.log("up");
				return false;
			}
			return true;
	}
```
  1. 服务端
    服务端主要使用了Node.js+Socket.io来构建服务端,需要Node环境来运行。

主要的思路:请求HTTP服务 → 在此基础上使用WebSocket → 引入Socket.io模块并创建实例 → 用该实例进行socket监听 → 通过socket进行消息的发送、转发、群发等。

服务端关键代码:

```
var app = require('http').createServer();
var io = require('socket.io')(app);
var PORT = 8081;
/*定义用户数组*/
var users = [];
app.listen(PORT);

io.on('connection',function (socket) { //监听
    //发送、转发、群发消息
})
```
  1. 图片文件传输

图片传输部分我本来是想用Base64编码,先在客户端读取图片二进制信息并编成Base64格式发送给服务端,服务端转发后别的客户端接收后解码。

但是后面放弃了这一方式,因为直接传输二进制信息太慢了,于是改用了HTML5的Filereader方法。

这个方法有个优点就是其中的readAsDataURL函数可以直接将图片二进制信息转成URL格式,这样子我只用发送并转发一个简短的URL,客户端直接根据URL加载图片,省去了大量传输和解码时间。

关键代码:
```
//图片发送
document.getElementById(‘sendImage’).addEventListener(‘change’,
function() {
//检查是否有文件被选中
if (this.files.length != 0) {
//获取文件并用FileReader进行读取
var file = this.files[0],
reader = new FileReader();

		if (!reader) return;
		
		reader.onload = function(e) {
			//读取成功,发送到服务器
			socket.emit('sendImg',{
				username:uname,
				image: e.target.result,
				date:new Date().toTimeString().substr(0, 8),headnum:headnum
			});
		};
		reader.readAsDataURL(file);
	};
}, false);

```
  1. 头像选择/表情包传输
    头像和表情已经预先放置在客户端中,用户在选择以后将产生特定格式的代码,放置到要发送的信息当中。

别的客户端接收到的消息时候,先用正则表达式检索信息中是否存在该格式代码,若有则加载相应的头像/表情,以达到效果。

关键代码,以表情为例:

//emoji
document.getElementById('emojiWrapper').addEventListener('click', 
function(e) {
   	//获取被点击的对象
   	var target = e.target;
   	console.log(target);
   	if (target.nodeName.toLowerCase() == 'img') {   //如果是表情图像则发送
   		var sendtxt = document.getElementById('sendtxt');
   		sendtxt.focus();
   		sendtxt.value = sendtxt.value + '[emoji:' + target.num + ']';
   	};
}, false);

源码

Gitbub地址:https://github.com/KMKNKK/Chatroom-WebSocket/tree/homework
(欢迎fork和star)

重要文件结构:

│  
├─index.html  //客户端
│      
├─css
│      bg.jpg  //背景图
│      chat.css                
│      
├─images  //图片资源
│  │  logo.png
│  │  toNewMessage.png
│  │  
│  ├─emoji  //表情
│  │      
│  └─user   //头像
│          
│      
├─js
│  │  app.js            //服务端JS
│  │  chat.js			//客户端JS
│  │  jquery.min.js     //依赖
│  │  socket.io.js		//依赖
│  │  
│  └─.vscode
│          settings.json
│          
├─new                   //BootStrap和jQuery文件
│  │  jquery-3.2.1.min.js     
│  │  
│  └─bootstrap-3.3.7-dist
│      
│              
└─node_modules          //依赖文件

你可能感兴趣的:(前端,web)