Nodejs_SocketIO_Redis搭建IM系统

前言
最近有人要我实现一个实时数据广播系统,希望能够支持多种web浏览器同时还要支持TCP链接。Socket.IO 支持4种协议:WebSocket、htmlfile、xhr-polling、jsonp-polling,它会自动根据浏览 器选择适合的通讯方式,可以方便的完成web端的实时数据传输。而nodejs本身也可以方便的实现tcp协议,同时nodejs针对redis的接口非常友好。本文通过redis数据库来发布数据。web端,和tcp客户的同时实时接受数据。

常用命令:

1
2
3
4
5
6
7
8
9
10
TCP 测试工具
nc -l 127.10.0.2 33333 < index.txt #监听端
nc 127.10.0.2 33333 > index.txt #客户端
启动nodejs服务
/usr/bin/nodejs /home/admin/index.js
启动redis服务
/etc/init.d/redis-server start

发布消息
redis-cli publish root messageTobeSend

环境搭建

本文使用docker的ubuntu:latest 作为基础镜像进行环境搭建,依赖软件安装步骤如下:

1
2
3
4
5
6
7
8
9
10
11
root@d90b262833ab:/home/admin# uname -a
Linux d90b262833ab 3.16.0-59-generic #79~14.04.1-Ubuntu SMP Mon Jan 18 15:41:27 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
root@d90b262833ab:/home/admin# apt-get install redis-server
root@d90b262833ab:/home/admin# /etc/init.d/redis-server start
root@d90b262833ab:/home/admin# apt-get install nodejs
root@d90b262833ab:/home/admin# apt-get install npm
root@d90b262833ab:/home/admin# npm install --save express
root@d90b262833ab:/home/admin# npm install --save socket.io
root@d90b262833ab:/home/admin# npm install redis
root@d90b262833ab:/home/admin# ls
node_modules

创建服务器nodejs脚本

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
 root@d90b262833ab:/home/admin# touch index.js
添加如下内容
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var redis = require('redis');
var net = require('net');

function getIPAdress(){
var interfaces = require('os').networkInterfaces();
for(var devName in interfaces){
var iface = interfaces[devName];
for(var i=0;i<iface.length;i++){
var alias = iface[i];
if(alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal){
return alias.address;
}
}
}
}
app.get('/', function(req, res){
var sp="<script src='http://";
sp=sp + getIPAdress();
sp=sp + ":3003/socket.io/socket.io.js'></script>";
sp=sp + "<script> var socket = io('http://";
sp=sp + getIPAdress();
sp=sp + ":3003');socket.on('message', function (data) {document.getElementById('demo').innerHTML=data;});</script>"
res.send(sp+'<h1 id="demo">Welcome Realtime Server Hucd</h1>');
});

io.on('connection', function(socket){
console.log('a user connected');
//监听用户发布聊天内容
socket.on('message', function(obj){
//向所有客户端广播发布的消息
io.emit('message', obj);
console.log(obj);
});

});

http.listen(3003, function(){
console.log('listening on *:3003');
});
//------------------------------tcp socket---------------------------------------------------------
clientList=[];
var tcp_server = net.createServer(function (socket) {
console.log('客户端: ' + "连接成功");
clientList.push(socket);
socket.on('data', function (data) {
console.log('DATA '+ data);
//socket.write(data);
});

socket.on('close', function (data) {
console.log('客户端: ' + "断开连接");
});

socket.on('error', function (exc) {
console.log("ignoring exception: " + exc);
});
});
function broadcast(message) {
var cleanup = []
for(var i=0;i<clientList.length;i+=1) {
if(clientList[i].writable) { // 先检查 sockets 是否可写
clientList[i].write(message)
} else {
cleanup.push(clientList[i]) // 如果不可写,收集起来销毁。销毁之前要 Socket.destroy() 用 API 的方法销毁。
clientList[i].destroy()
}
} //Remove dead Nodes out of write loop to avoid trashing loop index
for(i=0;i<cleanup.length;i+=1) {
clientList.splice(clientList.indexOf(cleanup[i]), 1)
}
}
tcp_server.listen(3004);

var redisclient = redis.createClient();
redisclient.on('connect',function(){
redisclient.set('author', 'testauthor', redis.print);
redisclient.get('author', redis.print);
redisclient.get('hello', redis.print);
redisclient.subscribe("root");
});
redisclient.on("message", function (channel, message) {
io.emit('message', message);
broadcast(message);
console.log(channel + ": " + message);
});

启动服务

1
2
3
4
5
root@d90b262833ab:/home/admin# /usr/bin/nodejs index.js &
listening on *:3003
Reply: OK
Reply: testauthor
Reply: null

此时,服务器3003端口在监听web请求。3004端口在监听TCP请求。

客户端测试

对于web端可创建如下页面进行测试

1
2
3
4
5
6
7
8
<script src="http://127.17.0.2:3003/socket.io/socket.io.js"></script>
<script>
var socket = io('http://127.17.0.2:3003');
socket.on('message', function (data) {
alert(data);
console.log(data);
});
</script>

保存为html文件即可通过浏览器打开进行测试。其中ip可根据服务器ip自行设置。或者直接在浏览器中输入’http://127.17.0.2:3003'即可;

tcp客户端测试

可利用nc工具来模拟测试tcp端

1
2
3
root@d90b262833ab:/home/admin# nc 172.17.0.2 3004 &
[2] 1561
root@d90b262833ab:/home/admin# 客户端: 连接成功

实时发布消息

本文通过redis来publsh数据

1
2
3
root@d90b262833ab:/home/admin# redis-cli publish root hudsagfdsag    
root: hudsagfdsag
(integer) 1

即可在浏览器和tcp端同时看到输出的内容。