WebSocket

WebSocket

TCP的三次握手与四次分手

三次握手

TCP是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接:

位码即tcp标志位,有6种标示:

  1. SYN(synchronous建立联机)

  2. ACK(acknowledgement 确认)

  3. PSH(push传送)

  4. FIN(finish结束)

  5. RST(reset重置)

  6. URG(urgent紧急)

  7. Sequence number(顺序号码)

  8. Acknowledge number(确认号码)

握手过程:

第一次握手:建立连接。客户端发送连接请求报文段,将SYN位置为1,Sequence Number为x;然后,客户端进入SYN_SEND状态,等待服务器的确认; 第二次握手:服务器收到SYN报文段。服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,设置Acknowledgment Numberx+1(Sequence Number+1);同时,自己还要发送SYN请求信息,将SYN位置为1,Sequence Number为y;服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态; 第三次握手:客户端收到服务器的SYN+ACK报文段。然后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手;

四次分手:

由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

操作:

  1. 客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。

  2. 服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。

  3. 服务器B关闭与客户端A的连接,发送一个FIN给客户端A。

  4. 客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。

参考: 1. http://www.cnblogs.com/Jessy/p/3535612.html 2. https://www.zhihu.com/question/24853633

WebSocket简介

WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——可以通俗的解释为服务器主动发送信息给客户端。 只需要经过一次HTTP请求,就可以做到源源不断的信息传送了(在程序设计中,这种设计叫做回调,即:你有信息了再来通知我)。

客户端请求:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com

Upgrade: websocketConnection: Upgrade表示希望将http协议升级到Websocket协议。 Sec-WebSocket-Key是一个经过base64编码的随机字节。 Sec-WebSocket-Protocol表示客户端支持的协议列表。 Sec-WebSocket-Version:是告诉服务器所使用的Websocket Draft(协议版本)。 Origin字段是可选的,表示在浏览器中发起此Websocket连接所在的页面。

服务端响应:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

Sec-WebSocket-Accept:是把Sec-WebSocket-Key加上一个特殊字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,然后计算SHA-1摘要,之后进行BASE-64编码,将结果做为Sec-WebSocket-Accept头的值,返回给客户端。

参考: 1.知乎 2.维基百科

实例

服务器端:

const WebSocket = require('ws');

const WebSocketServer = WebSocket.Server;

const wss = new WebSocketServer({
    port: 3000
});

//启动服务链接
wss.on('connection', function (ws) {
    console.log(`[SERVER] connection()`);
    //监听有否有请求信息
    ws.on('message', function (message) {
        console.log(`[SERVER] Received: ${message}`);
        setTimeout(() => {
            //定时给客户端发送信息
            ws.send(`What's your name?`, (err) => {
                if (err) {
                    console.log(`[SERVER] error: ${err}`);
                }
            });
        }, 1000);
    })
});

console.log('ws server started at port 3000...');

客户端:

const WebSocket = require('ws');
let count = 0;
let ws = new WebSocket('ws://localhost:3000/ws/chat');
//打开链接,给服务器发送一条信息
ws.on('open', function () {
    console.log(`[CLIENT] open()`);
    ws.send('Hello!');
});
//监听服务器是否有信息发送过来
ws.on('message', function (message) {
    console.log(`[CLIENT] Received: ${message}`);
    count++;
    if (count > 3) {
        ws.send('Goodbye!');
        ws.close();
    } else {
        setTimeout(() => {
            ws.send(`Hello, I'm Mr No.${count}!`);
        }, 1000);
    }
});

运行后:

ws server started at port 3000...
[SERVER] connection()
[CLIENT] open()
[SERVER] Received: Hello!
[CLIENT] Received: What's your name?
[SERVER] Received: Hello, I'm Mr No.1!
[CLIENT] Received: What's your name?
[SERVER] Received: Hello, I'm Mr No.2!
[CLIENT] Received: What's your name?
[SERVER] Received: Hello, I'm Mr No.3!
[CLIENT] Received: What's your name?
[SERVER] Received: Goodbye!
[SERVER] error: Error: not opened

end'

参考:websocket

WebSocket原生事件

事件

事件处理程序

描述

open

Socket.onopen

连接建立时触发

message

Socket.onmessage

客户端接收服务端数据时触发

error

Socket.onerror

通信发生错误时触发

close

Socket.onclose

连接关闭时触发

var ws = new WebSocket('ws://localhost:3000/ws/chat');
ws.onopen=function () {
    ws.send('Hello!');
};

WebSocket原生方法

方法

描述

Socket.send()

使用连接发送数据

Socket.close()

关闭连接

readyState

特性常量

取值

状态

WebSocket.CONNECTING

0

连接正在进行中,但还未建立

WebSocket.OPEN

1

连接已建立,消息可以开始传递

WebSocket.CLOSING

2

连接正在进行关闭

WebSocket.CLOSED

3

连接已关闭

连接池

每一个连接都放到一个数组中,组成连接池。

// 连接池
var clients = [];

wss.on('connection', function(ws) {
    // 将该连接加入连接池
    clients.push(ws);
    ws.on('message', function(message) {
        // 广播消息
        clients.forEach(function(ws1){
            if(ws1 !== ws) {
                ws1.send(message);
            }
       })
    });
});

为了区分每个连接,可以添加UUID ,通用唯一识别码(Universally Unique Identifier)

http://www.jianshu.com/p/ea0a9a6311cf

Last updated