# Socket 通讯

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。所以建立网络通信连接至少要一对socket。

socket 是做长连接的,连接通道建立之后,消耗较少的资源就可以实现客户端与服务器的数据通信了,而ajax实现长连接,消耗的资源太大。

ajax的过程只能是客户端主动向服务器发数据;而socket即可以客户端主动向服务器发数据,也可以服务器主动向客户端发数据。

# net 模块

socket 的核心对象是 net

var net = require("net");
net.createServer( function(){} );
1
2

我们使用net模块操作socket有些繁琐,所以我们会选择网络上别人写好的socket框架来完成我们的项目,这些框架的底层代码其实还是net模块,只不过做了一层封装,提供了一些功能,方便我们的使用。

这类的socket框架有很多,比如 websocket、socket.io 等等,学会一个即可。

# WebSocket

# 服务器

// cnpm i ws -S
// 获取 websocket 的 server 对象
var WebSocketServer = require('ws').Server;

// 创建 websocket 服务,监听了8181端口
var wss = new WebSocketServer({port:8181});

// 用一个变量来保存所有的socket端(即把所有的访客记录下来)
var clients = [];

// 每次有人连接到服务器时,都会自动触发connection事件
wss.on('connection', ws=>{
    // ws 就是这个访问者的socket对象
    // 如果想获取该访问者的IP,使用 ws._socket.remoteAddress
    // 把每一个连接到服务器的客户端,追加保存在数组中
    clients.push(ws); 
    
    // 客户端向服务器发数据时,服务器的message事件会自动触发,msg指客户端传过来的数据。
    ws.on('message', msg=>{ // 服务器得到客户端发过来的数据时
        // 比如聊天室中有10个人,这10个人的socket都保存到数组 clients 中
        // 其中1个人说话了,服务器的message事件就会被触发
        // 然后循环clients,把这个人说的内容发送给每一个人
        for( var c of clients ){ // 通过循环,广播给每一个连接的客户端
            if( c.readyState==1 ){  // 如果连接状态是正常的
                c.send( msg );  // 则向客户端发送数据(只能发送字符串)
            }
        }
    });
});
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

# 客户端

<input type="text" id="text1" />
<script>
// 原生js中提供了WebSocket方法,可以直接连接到服务器。
// http对应的协议是ws,如果做数据加密,https则对应wss。
var ws = new WebSocket("ws://localhost:8181");
// 建立通道后,客户端使用send可以向服务器端发数据,服务器端也可以使用send向客户端发数据。

// 客户端向服务器端发送数据
text1.onblur = function() {
    ws.send( this.value );  // 只能发送字符串
}

// 客户端接收服务器端传回来的数据
ws.onmessage = function(e){
    console.log( e.data );
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 事件

// 当websocket创建成功时,即会触发onopen事件
ws.onopen = function() {};

// 当客户端收到服务端发来的消息时,会触发onmessage事件,参数evt.data中包含server传输过来的数据
ws.onmessage = function(evt) { console.log(evt.data) };

// 当客户端收到服务端发送的关闭连接的请求时,触发onclose事件
ws.onclose = function(evt) {};

// 如果出现连接,处理,接收,发送数据失败的时候就会触发onerror事件
ws.onerror = function(evt) {}; 
1
2
3
4
5
6
7
8
9
10
11

# ws.readyState 状态

状态码 状态 描述
0 CONNECTING 连接尚未建立
1 OPEN WebSocket的链接已经建立
2 CLOSING 连接正在关闭
3 CLOSED 连接已经关闭或不可用

# 断开后重新连接的策略

如果两个socket断开了,前端会自动触发 onclose 事件,如果想重新连接,那么在这个事件中,重新发起socket连接即可。

# 心跳机制

两个socket之间,如果长时间没有数据交互,服务器及客户端都有安全策略,会自动断开连接,为了保证连接,所以我们可以每隔一定的时间,发送一些数据给对方,这个数据唯一的作用就是告诉对方“我还活着,别把我断了”,这就是心跳机制。前后端都可以实现,用setTimeout每隔一段时间,发数据即可。

# Socket.io

socket.io由两部分组成,分别是服务器端和客户端。

# 服务器端

下载该模块

npm install socket.io
1

编写 socket 服务

var socketio = require("socket.io");  // npm install socket.io
var io = socketio.listen(8080);

io.on("connection", socket=>{
	var ip = socket.request.connection.remoteAddress.substr(7);
	console.log(ip, " 有客户端连接到服务器了..");
	// 监听来自客户端的数据
	socket.on("a", data=>{
		console.log(ip, " 说 ", data);
		// 给自己广播
		//socket.emit("b", data);
		// 给所有的客户端广播
		io.sockets.emit("b", data);		
	});
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 客户端

引入该文件

socket.io-1.4.5.js

http://socket.io/download/

https://cdnjs.com/libraries/socket.io

编写客户端代码:

<script src="socket.io-1.4.5.js"></script>
<div id="div1"></div>
<input id="input1" type="text" />
<script>
//建立长连接
var socket=io.connect('http://localhost:8080');
//接收由服务器传递给客户端的data
socket.on('b', function (data) {
	div1.innerHTML=""+data.x.replace(/\n/,"<br>")+"<hr>";
});
//对按钮绑定事件
input1.onblur=function(){
	var str=this.value;//获取按钮内容
	//由客户端向服务器端发送数据(数据名叫做AAA,值为一个对象)
	socket.emit('a', {
		'str':str
	});
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19