基于Socket.IO的node聊天室,支持私聊

源码我放在了github上面。【地址在这儿】。首先可以看下【上篇】博客,把环境先搭建好和熟悉socket.io基本用法。整个可以实现下面的这些功能:

  1. 用户登陆进行聊天。
  2. 用户登陆后,即可发送消息。
  3. 对刚登录用户来说,罗列所有在线用户列表
  4. 对已在线的用户来说,通知该用户有新用户登陆或者离开
  5. 支持对所有人广播聊天内容,即群聊
  6. 支持私聊当前在线用户,即私聊
  7. 用户即时获得消息列表
  8. 当服务器断开连接时,提醒所有用户

整个代码量非常少,可以根据自己的需求修改逻辑。

没有进行太多的限制,比如没有限制重名登陆,主要是关注逻辑代码。

没有写任何样式,所以很难看额,以后有时间慢慢的补起来吧,现在考试略多。。。。

运行:切换到此目录,运行node index.js即可。打开端口3000,localhost:3000


服务器端代码

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
var http = require('http');
var sio = require('socket.io');
var express = require('express');

var app = express();
var server = http.createServer(app);
var io = sio.listen(server);

app.get('/',function(req,res){
res.sendFile(__dirname+'/index.html');
})

server.listen('3000',function(){
console.log('listening on port 3000');
})

var onlineUsers = {};
var onlineCount = 0;
var userSockets = {};

io.on('connection',function(socket){
//当客户端有用户登陆的时候
socket.on('login',function(userObj){
socket.uid = userObj.uid;
userSockets[userObj.uid] = socket;

//检查是否在当前的用户列表中,如果不在count++
if(!onlineUsers.hasOwnProperty(userObj.uid)){
onlineUsers[userObj.uid] = userObj.username;
onlineCount++;
}

//向客户端发送用户登陆的信息
io.emit('login',{onlineUsers:onlineUsers,onlineCount:onlineCount,userObj:userObj});

console.log(userObj.username + " 进入了聊天室");
});

//当服务器接收到消息时
socket.on('message',function(data){
if(data.to !== "所有人"){
if(data.to in userSockets){
userSockets[data.to].emit('to'+data.to,data);
userSockets[data.uid].emit('to'+data.uid,data);
}
}else{
io.emit('message',data);
console.log(data.username+"对"+data.to+"说:"+data.message);
}
});

//当客户端退出的时候
socket.on('disconnect',function(){
if(onlineUsers.hasOwnProperty(socket.uid)){
//记录要退出用户的信息,发送给客户端
var userObj = {uid:socket.uid,username:onlineUsers.uid};

delete onlineUsers[socket.uid];
delete userSockets[socket.uid];
onlineCount--;

io.emit('logout',{onlineUsers:onlineUsers,onlineCount:onlineCount,userObj:userObj});
console.log(userObj.username+" 退出了聊天室");
}
})
})

客户端代码

DOM结构部分
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
<header>
<h3>基于socket.io的聊天室</h3>
<p id="notice"></p>
</header>
<div class="login">
<h3>聊天前请先登陆</h3>
<input type="text" placeholder="输入用户名" id="username">
<input type="submit" value="登陆" onclick="CHAT.submit()">
</div>
<div id="main" style="display:none;">
<aside>
<p>在线用户列表</p>
<ul id="onlineUsers">
</ul>
</aside>
<section>
<p>消息列表</p>
<ul id="messageList">
</ul>
</section>
</div>
<footer id="footer" style="display:none">
<textarea name="" id="message" cols="30" rows="10"></textarea>
<select name="" id="selectUserList">
<option value="">所有人</option>
</select>
<input type="submit" value="提交" onclick="CHAT.sendMessage()">
</footer>
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
<script src="/socket.io/socket.io.js"></script>
<script>
(function(){
window.CHAT = {
uid:null,
username:null,
socket:null,
submit:function(){
var username = document.getElementById('username').value;
if(username!==""){
this.init(username);
this.socket.emit('login',{uid:this.uid,username:this.username});

this.initStyle();
}
},
initStyle:function(){
var main = document.getElementById('main');
var footer = document.getElementById('footer');

main.style.display = 'block';
footer.style.display = 'block';
},
getUid:function(){
return +new Date();
},
sendMessage:function(){
var message = document.getElementById('message').value;
var select = document.getElementById('selectUserList');
var selectIndex = select.selectedIndex;
var optionValue = select.options[selectIndex].value;
var optionText = select.options[selectIndex].text;
if(message != ""){
this.socket.emit('message',{uid:this.uid,username:this.username,message:message,to:optionValue,toUser:optionText});
}
},
createEleNode:function(ele,text){
var eleNode = document.createElement(ele);
var text = document.createTextNode(text);
eleNode.appendChild(text);
return eleNode;
},
updateUlist:function(data){
var onlineLists = document.getElementById("onlineUsers");
var str = "";
for(var i in data.onlineUsers){
str += "<li id='"+i+"'>"+data.onlineUsers[i]+"</li>";
}
onlineLists.innerHTML = str;
},
updateSelect:function(data){
var selectUserList = document.getElementById('selectUserList');
var str = "<option value='所有人'>所有人</option>";
for(var key in data.onlineUsers){
str += "<option value='"+key+"'>"+data.onlineUsers[key]+"</option>"
}
selectUserList.innerHTML = str;
},
updateNotice:function(msg){
var notice = document.getElementById('notice');
var node = this.createEleNode('li',msg)
notice.appendChild(node);
},
appendMessage:function(data){
var messageList = document.getElementById("messageList");
var node = CHAT.createEleNode('li','用户'+data.username+"对"+data.toUser+"说:"+data.message);
console.log(node);
messageList.appendChild(node);
},
init:function(username){
this.uid = this.getUid();
this.username = username;
this.socket = io.connect();

this.socket.on('login',function(data){
//当用户登陆的时候,更新列表
CHAT.updateUlist(data);
CHAT.updateSelect(data);
CHAT.updateNotice(data.userObj.username+"进入了聊天室");
});

this.socket.on('logout',function(data){
//当用户退出时,更新列表
CHAT.updateUlist(data);
CHAT.updateSelect(data);
CHAT.updateNotice(CHAT.username+"退出了聊天室");
});

this.socket.on('message',function(data){
CHAT.appendMessage(data);
});
this.socket.on('to'+CHAT.uid,function(data){
CHAT.appendMessage(data);
})

this.socket.on('disconnect',function(){
CHAT.updateNotice("服务器断开连接");
});
}
}
})()
</script>

这里注意下有个地方,有的时候我们调用方法的时候,不能够直接用this.method调用该方法,因为比如我们使用:this.socket.on的时候,这个内部的this已经不是当前对象了。所以我们要直接使用CHAT.method或者CHAT.属性的方法来调用。


截图结果

结果截图


总结

socket.io很好用,开发也很简单,花了比较少的时间。但是还有一些功能没有实现,比如如何实现群组,也就是房间聊天?如何给不在线的用户发送聊天记录?如何利用redis和聊天室结合起来?