浅析websocket

浏览: 150 发布日期: 2016-12-04 分类: nginx

Websocket

背景

历史上,如果一个web应用程序需要和server进行双向通信(例如,实时聊天和游戏),
需要大量的http请求来检查server的内容是否有更新, 同时也需要很多http请求来发送
通知,这种方式有许多问题:

  • server端必须使用大量的tcp连接来服务每一个client:一类连接用来发送信息给
    client, 另一类连接用来接收新的消息。

  • 有大量不必要网络开销,因为每一次消息通讯都都包含http header。

  • client需要维护一个map来映射发送数据的连接和接受数据的连接。

一个简单的解决方案就是使用一个tcp连接来进行双向通讯,这就是websocket的作用。

websocket使用80和443端口,对HTTP代理和网关友好。

连接握手过程

client发送

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

server响应

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

连接握手的目的是兼容基于HTTP的server和代理,所以一个端口可以同时服务HTTP请求和
websocket请求。

请求中Upgrage: websocket表示这是一个websocket请求。服务端收到该请求后会根据
请求中携带的Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==通过特定算法生成
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=响应给客户端,客户端校验
通过握手成功。

握手结束后,双方就可以进行双向通讯了,所有的通讯内容都以数据帧格式来传输。

数据帧

FIN: 1比特

表示一个message中的最后一个frame。

RSV1, RSV2, RSV3: 每个1比特

必须为0,后续扩展使用。

Optcode: 4比特

定义负载数据的解析方式。

  • %x0 表示一个连续帧。

  • %x1 表示一个文本帧。

  • 0x2 表示一个二进制帧。

  • 0x3-7 保留,以后给非控制帧使用。

  • 0x8 表示连接关闭。

  • 0x9 表示ping。

  • 0xA 表示pong。

  • 0xB-F 保留,以后给控制帧使用。

Mask: 1比特

表示负载数据是否masked。如果为1,masking-key包含一个key,用来unmask负载
数据。所有从客户端方向发送的数据,该位必须置1。

*Playload length: * 7比特,7+16比特, 或者7+64比特

  • 0-125表示负载数据的长度。

  • 126,接下来的两个字节为16位无符号整型表示负载数据长度。

  • 127, 接下来的8个字节为64为无符号整型表示负载数据长度。

Masking key: 0或者4字节

如果mask标志为1时存在,保存mask key。

Payload data: 负载数据。

关闭握手过程

关闭握手比连接握手简单的多,连接的任何一端都可以发送一个close frame,
开始关闭握手,收到这个control frame的端发送一个close frame响应,
另一端收到这个close frame后关闭连接。

一端发送close frame后就表示这个连接将会被关闭并且不会再发送任何数据到另一端, 对端
收到close frame后知道这个连接将会被关闭,后续收到的数据都会被丢弃。

websocket关闭握手的目的是完善TCP的关闭握手,因为在端到端的情景TCP的关闭握手并不
总是可信赖的,尤其端到端中间有代理或者防火墙的情况下。

通过发送一个close frame然后等待响应,这种方式避免了不必要的数据丢失。

websocket & nginx

nginx并没有支持websocket,只是支持websocket的代理。

http {
    map $http_upgrade $connection_upgrade {
        default upgrade;
        '' close;
    }

    upstream websocket {
        server 192.168.100.10:8010;
    }

    server {
        listen 8020;
        location / {
            proxy_pass http://websocket;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
        }
    }
}

nginx也有第三方支持websocket的模块nchan,但
不是为流媒体设计的。

哪些服务端支持websocket

参考资料

http://blog.teamtreehouse.com/an-introduction-to-websockets

http://www.websocket.org/aboutwebsocket.html

https://www.html5rocks.com/en/tutorials/websockets/basics

https://os.alfajango.com/websockets-slides/#/2

The WebSocket Protocol

返回顶部