使用Node.js+Socket.IO搭建WebSocket 实现多人群聊

转载 2017年01月03日 16:50:44

今天我们做的就是无刷新实时多人聊天,最终效果我们可以看下:

Node.js

Node.js采用C++语言编写而成,它不是Javascript应用,而是一个Javascript的运行环境,据Node.js创始人Ryan Dahl回忆,他最初希望采用Ruby来写Node.js,但是后来发现Ruby虚拟机的性能不能满足他的要求,后来他尝试采用V8引擎,所以选择了C++语言。

Node.js支持的系统包括*nux、Windows,这意味着程序员可以编写系统级或者服务器端的Javascript代码,交给Node.js来解释执行。Node.js的Web开发框架Express,可以帮助程序员快速建立web站点,从2009年诞生至今,Node.js的成长的速度有目共睹,其发展前景获得了技术社区的充分肯定。

Socket.IO

Socket.IO是一个开源的WebSocket库,它通过Node.js实现WebSocket服务端,同时也提供客户端JS库。Socket.IO支持以事件为基础的实时双向通讯,它可以工作在任何平台、浏览器或移动设备。

Socket.IO支持4种协议:WebSocket、htmlfile、xhr-polling、jsonp-polling,它会自动根据浏览器选择适合的通讯方式,从而让开发者可以聚焦到功能的实现而不是平台的兼容性,同时Socket.IO具有不错的稳定性和性能。

编码实现:

1.安装Node.js

根据自己的操作系统,去Node.js官网下载安装即可。我的是nginx:

yum install nodejs
yum install npm 
如果成功安装。在命令行输入node -vnpm -v应该能看到相应的版本号。

node -v  
v0.10.26  
npm -v  
1.4.6

搭建WebSocket服务端

这个环节我们尽可能的考虑真实生产环境,把WebSocket后端服务搭建成一个线上可以用域名访问的服务,如果你是在本地开发环境,可以换成本地ip地址,或者使用一个虚拟域名指向本地ip。

进入到工作目录,/usr/local/nginx/html,新建一个名为package.json的文件,内容

{
  "name": "realtime-server",
  "version": "0.0.1",
  "description": "my first realtime server",
  "dependencies": {}
}

接下来使用npm命令安装expresssocket.io

1
2
npm install --save express
npm install --save socket.io

安装成功后,应该可以看到工作目录下生成了一个名为node_modules的文件夹,里面分别是expresssocket.io,接下来可以开始编写服务端的代码了,新建一个文件:index.js

1
2
3
4
5
6
7
8
9
10
11
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);

app.get('/', function(req, res){
	res.send('<h1>Welcome Realtime Server</h1>');
});

http.listen(3000, function(){
	console.log('listening on *:3000');
});

命令行运行node index.js,如果一切顺利,你应该会看到返回的listening on *:3000字样,这说明服务已经成功搭建了。此时浏览器中打开http://localhost:3000应该可以看到正常的欢迎页面。

如果你想要让服务运行在线上服务器,并且可以通过域名访问的话,可以使用Nginx做代理,在nginx.conf中添加如下配置,然后将域名(比如:realtime.plhwin.com)解析到服务器IP即可。

1
2
3
4
5
6
7
8
server
{
  listen       80;
  server_name  realtime.plhwin.com;
  location / {
    proxy_pass http://127.0.0.1:3000;
  }
}

完成以上步骤,http://realtime.plhwin.com:3000的后端服务就正常搭建了。

服务端代码实现

前面讲到的index.js运行在服务端,之前的代码只是一个简单的WebServer欢迎内容,让我们把WebSocket服务端完整的实现代码加入进去,整个服务端就可以处理客户端的请求了。完整的index.js代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);

app.get('/', function(req, res){
	res.send('<h1>Welcome Realtime Server</h1>');
});

//在线用户
var onlineUsers = {};
//当前在线人数
var onlineCount = 0;

io.on('connection', function(socket){
	console.log('a user connected');
	
	//监听新用户加入
	socket.on('login', function(obj){
		//将新加入用户的唯一标识当作socket的名称,后面退出的时候会用到
		socket.name = obj.userid;
		
		//检查在线列表,如果不在里面就加入
		if(!onlineUsers.hasOwnProperty(obj.userid)) {
			onlineUsers[obj.userid] = obj.username;
			//在线人数+1
			onlineCount++;
		}
		
		//向所有客户端广播用户加入
		io.emit('login', {onlineUsers:onlineUsers, onlineCount:onlineCount, user:obj});
		console.log(obj.username+'加入了聊天室');
	});
	
	//监听用户退出
	socket.on('disconnect', function(){
		//将退出的用户从在线列表中删除
		if(onlineUsers.hasOwnProperty(socket.name)) {
			//退出用户的信息
			var obj = {userid:socket.name, username:onlineUsers[socket.name]};
			
			//删除
			delete onlineUsers[socket.name];
			//在线人数-1
			onlineCount--;
			
			//向所有客户端广播用户退出
			io.emit('logout', {onlineUsers:onlineUsers, onlineCount:onlineCount, user:obj});
			console.log(obj.username+'退出了聊天室');
		}
	});
	
	//监听用户发布聊天内容
	socket.on('message', function(obj){
		//向所有客户端广播发布的消息
		io.emit('message', obj);
		console.log(obj.username+'说:'+obj.content);
	});
  
});

http.listen(3000, function(){
	console.log('listening on *:3000');
});

客户端代码实现

进入客户端工作目录/workspace/wwwroot/plhwin/demo.plhwin.com/chat,新建一个index.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="format-detection" content="telephone=no"/>
        <meta name="format-detection" content="email=no"/>
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0" name="viewport">
        <title>多人聊天室</title>
        <link rel="stylesheet" type="text/css" href="./style.css" />
        <!--[if lt IE 8]><script src="./json3.min.js"></script><![endif]-->
        <script src="http://realtime.plhwin.com:3000/socket.io/socket.io.js"></script>
    </head>
    <body>
        <div id="loginbox">
            <div style="width:260px;margin:200px auto;">
                请先输入你在聊天室的昵称
                <br/>
                <br/>
                <input type="text" style="width:180px;" placeholder="请输入用户名" id="username" name="username" />
				<input type="button" style="width:50px;" value="提交" onclick="CHAT.usernameSubmit();"/>
            </div>
        </div>
        <div id="chatbox" style="display:none;">
            <div style="background:#3d3d3d;height: 28px; width: 100%;font-size:12px;">
                <div style="line-height: 28px;color:#fff;">
                    <span style="text-align:left;margin-left:10px;">Websocket多人聊天室</span>
                    <span style="float:right; margin-right:10px;"><span id="showusername"></span> | 
					<a href="javascript:;" onclick="CHAT.logout()" style="color:#fff;">退出</a></span>
                </div>
            </div>
            <div id="doc">
                <div id="chat">
                    <div id="message" class="message">
<div id="onlinecount" style="background:#EFEFF4; font-size:12px; margin-top:10px; margin-left:10px; color:#666;">
</div>
                    </div>
                    <div class="input-box">
                        <div class="input">
<input type="text" maxlength="140" placeholder="请输入聊天内容,按Ctrl提交" id="content" name="content">
                        </div>
                        <div class="action">
                            <button type="button" id="mjr_send" onclick="CHAT.submit();">提交</button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <script type="text/javascript" src="./client.js"></script>
    </body>
</html>

上面的html内容本身没有什么好说的,我们主要看看里面的4个文件请求:
1、realtime.plhwin.com:3000/socket.io/socket.io.js
2、style.css
3、json3.min.js
4、client.js

第1个JS是Socket.IO提供的客户端JS文件,在前面安装服务端的步骤中,当npm安装完socket.io并搭建起WebServer后,这个JS文件就可以正常访问了。

第2个style.css文件没什么好说的,就是样式文件而已。

第3个JS只在IE8以下版本的IE浏览器中加载,目的是让这些低版本的IE浏览器也能处理json,这是一个开源的JS,详见:http://bestiejs.github.io/json3/

第4个client.js是完整的客户端的业务逻辑实现代码,它的内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
(function () {
	var d = document,
	w = window,
	p = parseInt,
	dd = d.documentElement,
	db = d.body,
	dc = d.compatMode == 'CSS1Compat',
	dx = dc ? dd: db,
	ec = encodeURIComponent;
	
	
	w.CHAT = {
		msgObj:d.getElementById("message"),
		screenheight:w.innerHeight ? w.innerHeight : dx.clientHeight,
		username:null,
		userid:null,
		socket:null,
		//让浏览器滚动条保持在最低部
		scrollToBottom:function(){
			w.scrollTo(0, this.msgObj.clientHeight);
		},
		//退出,本例只是一个简单的刷新
		logout:function(){
			//this.socket.disconnect();
			location.reload();
		},
		//提交聊天消息内容
		submit:function(){
			var content = d.getElementById("content").value;
			if(content != ''){
				var obj = {
					userid: this.userid,
					username: this.username,
					content: content
				};
				this.socket.emit('message', obj);
				d.getElementById("content").value = '';
			}
			return false;
		},
		genUid:function(){
			return new Date().getTime()+""+Math.floor(Math.random()*899+100);
		},
		//更新系统消息,本例中在用户加入、退出的时候调用
		updateSysMsg:function(o, action){
			//当前在线用户列表
			var onlineUsers = o.onlineUsers;
			//当前在线人数
			var onlineCount = o.onlineCount;
			//新加入用户的信息
			var user = o.user;
				
			//更新在线人数
			var userhtml = '';
			var separator = '';
			for(key in onlineUsers) {
		        if(onlineUsers.hasOwnProperty(key)){
					userhtml += separator+onlineUsers[key];
					separator = '、';
				}
		    }
			d.getElementById("onlinecount").innerHTML = '当前共有 '+onlineCount+' 人在线,在线列表:'+userhtml;
			
			//添加系统消息
			var html = '';
			html += '<div class="msg-system">';
			html += user.username;
			html += (action == 'login') ? ' 加入了聊天室' : ' 退出了聊天室';
			html += '</div>';
			var section = d.createElement('section');
			section.className = 'system J-mjrlinkWrap J-cutMsg';
			section.innerHTML = html;
			this.msgObj.appendChild(section);	
			this.scrollToBottom();
		},
		//第一个界面用户提交用户名
		usernameSubmit:function(){
			var username = d.getElementById("username").value;
			if(username != ""){
				d.getElementById("username").value = '';
				d.getElementById("loginbox").style.display = 'none';
				d.getElementById("chatbox").style.display = 'block';
				this.init(username);
			}
			return false;
		},
		init:function(username){
			/*
			客户端根据时间和随机数生成uid,这样使得聊天室用户名称可以重复。
			实际项目中,如果是需要用户登录,那么直接采用用户的uid来做标识就可以
			*/
			this.userid = this.genUid();
			this.username = username;
			
			d.getElementById("showusername").innerHTML = this.username;
			this.msgObj.style.minHeight = (this.screenheight - db.clientHeight + this.msgObj.clientHeight) + "px";
			this.scrollToBottom();
			
			//连接websocket后端服务器
			this.socket = io.connect('ws://realtime.plhwin.com:3000');
			
			//告诉服务器端有用户登录
			this.socket.emit('login', {userid:this.userid, username:this.username});
			
			//监听新用户登录
			this.socket.on('login', function(o){
				CHAT.updateSysMsg(o, 'login');	
			});
			
			//监听用户退出
			this.socket.on('logout', function(o){
				CHAT.updateSysMsg(o, 'logout');
			});
			
			//监听消息发送
			this.socket.on('message', function(obj){
				var isme = (obj.userid == CHAT.userid) ? true : false;
				var contentDiv = '<div>'+obj.content+'</div>';
				var usernameDiv = '<span>'+obj.username+'</span>';
				
				var section = d.createElement('section');
				if(isme){
					section.className = 'user';
					section.innerHTML = contentDiv + usernameDiv;
				} else {
					section.className = 'service';
					section.innerHTML = usernameDiv + contentDiv;
				}
				CHAT.msgObj.appendChild(section);
				CHAT.scrollToBottom();	
			});

		}
	};
	//通过“回车”提交用户名
	d.getElementById("username").onkeydown = function(e) {
		e = e || event;
		if (e.keyCode === 13) {
			CHAT.usernameSubmit();
		}
	};
	//通过“回车”提交信息
	d.getElementById("content").onkeydown = function(e) {
		e = e || event;
		if (e.keyCode === 13) {
			CHAT.submit();
		}
	};
})();

至此所有的编码开发工作全部完成了,在浏览器中打开demo.plhwin.com/chat/就可以看到效果了。上面所有的客户端和服务端的代码可以从Github上获得,点这里跳转到Github项目主页,或者在命令行将代码Clone到本地。

git clone https://github.com/plhwin/nodejs-socketio-chat.git

下载本地后有两个文件夹 client  serverclient文件夹是客户端源码,可以放在Nginx/Apache的WebServer中,也可以放在Node.js的WebServer中。后面的server文件夹里的代码是websocket服务端代码,放在Node.js环境中,使用npm安装完 express  socket.io 后,node index.js 启动后端服务就可以了。






nodejs+socket.io实现websocket通信初探

nodejs+socket.io实现websocket通信初探

nodejs之socket.io模块——实现了websocket协议

Nodejs实现websocket的4种方式:socket.io、WebSocket-Node、faye-websocket-node、node-websocket-server,这里主要使用的是so...

基于Node.js + socket.io实现WebSocket的聊天DEMO

基于Node.js + socket.io实现WebSocket的聊天DEMO
  • hacke2
  • hacke2
  • 2014年09月11日 21:23
  • 5438

用nodejs快速实现微信小程序的websocket服务端

摘要: 微信小程序服务端使用websocket方式。socket.io已作为nodejs体系中被广泛应用的websocket解决方案,却因socket.io对websocket做了高级封装,不能兼容微...

微信小程序 websocket

目的:使用Node js 搭建websocket (wss微信需要安全的websocket),演示不同群组聊天 需要知道一点点node相关操作即可 node 服务端代码 var https=requ...

spring配置websocket并实现群发/单独发送消息

spring框架中自带了websocket的jar包,利用它可以实现与H5中WebSocket的对接,甚至websocket还可以通过依赖注入与http请求一同工作,详细配置实现过程如下 文件目录结构...

菜鸟学习nodejs--Socket.IO即时通讯

动态web在html5以前,web的设计上并没有考虑过动态,他一直是围绕着文档设计的,我们看以前比较老的网站,基本上都是某一刻用来显示单一的文档的,用户请求一次web页面,获取一个页面,但是随着时间的...

nodejs搭建websocket服务器小结

对nodejs和js了解不深,就是项目上用到,临时即学即用,只学了websocket几个api而已,做个小结,因为websocket还是大有前途的 1、环境搭建 (1)安装node       ...

nodejs与websocket的库socket.io的应用

最近nodejs,html5 ,websocket等前端新技术越来越流行,特别是nodejs,让js做了服务器端的事情。 下面进入今天的正题, 一般网页都无法做真正的实时通讯。比如说新浪微博的提醒,...

node.js+socket.io+websocket使用demo

  • 2013年10月13日 23:52
  • 1.15MB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:使用Node.js+Socket.IO搭建WebSocket 实现多人群聊
举报原因:
原因补充:

(最多只允许输入30个字)