利用socket.io实现多人聊天室(基于nodejs)
socket.io简介
在html5中存在着这样的一个新特性,引入了websocket,关于websocket的内部实现原理可以看这篇文章,这篇文章讲述了websocket无到有,根据协议,分析数据帧的头,进行构建websocket。虽然代码短,但可以很好地体现websocket的原理。
这个特性提供了浏览器端和服务器端的基于tcp连接的双向通道。但是并不是所有的浏览器都支持websocket特性,故为了磨平浏览器间的差异,为开发者提供统一的接口,引入了socket.io模块。在不支持websoket的浏览器中,socket.io可以降级为其他的通信方式,比如有ajax long polling ,jsonp polling等。
模块安装
新建一个package.json文件,在文件中写入如下内容:
{ name: socketiochatroom, version: 0.0.1, dependencies: { socket.io: *, express:* }}
npm install
执行完这句,node将会从npm处下载socket.io和express模块。
服务器端的实现
在文件夹中添加index.js文件,并在文件中写入如下内容:
/** * created by bamboo on 2016/3/31. */var app = require('express')();var http = require('http').server(app);var io = require('socket.io')(http);app.get('/', function (req, res) { use strict; res.end(socket server)});/*在线人员*/var onlineusers = {};/* 在线人数*/var onlinecounts = 0;/*io监听到存在链接,此时回调一个socket进行socket监听*/io.on('connection', function (socket) { console.log('a user connected'); /*监听新用户加入*/ socket.on('login', function (user) { use strict; //暂存socket.name 为user.userid;在用户退出时候将会用到 socket.name = user.userid; /*不存在则加入 */ if (!onlineusers.hasownproperty(user.userid)) { //不存在则加入 onlineusers[user.userid] = user.username; onlinecounts++; } /*一个用户新加入,向所有客户端监听login的socket的实例发送响应,响应内容为一个对象*/ io.emit('login', {onlineusers: onlineusers, onlinecounts: onlinecounts, user: user}); console.log(user.username, 加入了聊天室);//在服务器控制台中打印么么么用户加入到了聊天室 }); /*监听用户退出聊天室*/ socket.on('disconnect', function () { use strict; if (onlineusers.hasownproperty(socket.name)) { var user = {userid: socket.name, username: onlineusers[socket.name]}; delete onlineusers[socket.name]; onlinecounts--; /*向所有客户端广播该用户退出群聊*/ io.emit('logout', {onlineusers: onlineusers, onlinecounts: onlinecounts, user: user}); console.log(user.username, 退出群聊); } }) /*监听到用户发送了消息,就使用io广播信息,信息被所有客户端接收并显示。注意,如果客户端自己发送的也会接收到这个消息,故在客户端应当存在这的判断,是否收到的消息是自己发送的,故在emit时,应该将用户的id和信息封装成一个对象进行广播*/ socket.on('message', function (obj) { use strict; /*监听到有用户发消息,将该消息广播给所有客户端*/ io.emit('message', obj); console.log(obj.username, 说了:, obj.content); });});/*监听3000*/http.listen(3000, function () { use strict; console.log('listening 3000');});
运行服务器端程序node index.js
输出
listening 3000
此时在浏览器中打开localhost:3000会得到这样的结果:
原因是在代码中只对路由进行了如下设置
app.get('/', function (req, res) { use strict; res.end(socket server)});
服务器端主要是提供socketio服务,并没有设置路由。
客户端的实现
在客户端建立如下的目录和文件,其中json3.min.js可以从网上下载到。
client
- - - client.js
- - - index.html
- - - json3.min.js
- - - style.css
在index.html中 1301群聊 输入你在群聊中的昵称
1301群聊 | 退出
提交
在client.js中/** * created by bamboo on 2016/3/31. */ /*即时运行函数*/(function () { use strict; var d = document, w = window, dd = d.documentelement, db = d.body, dc = d.compatmode === css1compat, dx = dc ? dd : db, ec = encodeuricomponent, p = parseint; w.chat = { msgobj: d.getelementbyid(message), screenheight: w.innerheight ? w.innerheight : dx.innerheight, username: null, userid: null, socket: null, /*滚动条始终在最底部*/ scrolltobottom: function () { w.scrollto(0, this.msgobj.clientheight); }, /*此处仅为简单的刷新页面,当然可以做复杂点*/ logout: function () { // this.socket.disconnect(); w.top.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; }, /**客户端根据时间和随机数生成id,聊天用户名称可以重复*/ genuid: function () { return new date().gettime() + + math.floor(math.random() * 889 + 100); }, /*更新系统信息 主要是在客户端显示当前在线人数,在线人列表等,当有新用户加入或者旧用户退出群聊的时候做出页面提示。*/ updatesysmsg: function (o, action) { var onlineusers = o.onlineusers; var onlinecounts = o.onlinecounts; var user = o.user; //更新在线人数 var userhtml = ''; var separator = ''; for (var key in onlineusers) { if (onlineusers.hasownproperty(key)) { userhtml += separator + onlineusers[key]; separator = '、'; } } //插入在线人数和在线列表 d.getelementbyid('onlinecounts').innerhtml = '当前共有' + onlinecounts + 在线列表: + userhtml; //添加系统消息 var html = ''; html += ''; html += user.username; html += (action === login) ? 加入了群聊 : 退出了群聊; html += '
'; var section = d.createelement('section'); section.classname = 'system j-mjrlinkwrap j-cutmsg'; section.innerhtml = html; this.msgobj.appendchild(section); this.scrolltobottom(); }, /*用户提交用户名后,将loginbox设置为不显示,将chatbox设置为显示*/ 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);//调用init方法 } return false; }, //用户初始化 init: function (username) { //随机数生成uid this.userid = this.genuid(); this.username = username; d.getelementbyid('showusername').innerhtml = this.username;//[newpidian]|[退出] this.scrolltobottom(); //连接socketio服务器,newpidian的ip地址 this.socket = io.connect('192.168.3.155:3000'); //向服务器发送某用户已经登录了 this.socket.emit('login', {userid: this.userid, username: this.username}); //监听来自服务器的login,即在客户端socket.emit('login ')发送后,客户端就会收到来自服务器的 // io.emit('login', {onlineusers: onlineusers, onlinecounts: onlinecounts, user: user}); /*监听到有用户login了,更新信息*/ this.socket.on('login', function (o) { //更新系统信息 chat.updatesysmsg(o, 'login'); }); /*监听到有用户logout了,更新信息*/ this.socket.on('logout', function (o) { chat.updatesysmsg(o, 'logout'); }); //var obj = { // userid: this.userid, // username: this.username, // content: content //}; /*监听到有用户发送消息了*/ this.socket.on(message, function (obj) { //判断消息是不是自己发送的 var isme = (obj.userid === chat.userid); var contentdiv = '' + obj.content + '
'; var usernamediv = '' + obj.username + ''; 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(); }); } } /*控制键键码值(keycode) 按键 键码 按键 键码 按键 键码 按键 键码 backspace 8 esc 27 right arrow 39 -_ 189 tab 9 spacebar 32 dw arrow 40 .> 190 clear 12 page up 33 insert 45 /? 191 enter 13 page down 34 delete 46 `~ 192 shift 16 end 35 num lock 144 [{ 219 control 17 home 36 ;: 186 \| 220 alt 18 left arrow 37 =+ 187 ]} 221 cape lock 20 up arrow 38 ,