全双工
因为websocket定义了二进制帧,相对于http,可以更加轻松的处理二进制文件
因为全双工协议,更强的实时性
websocket可以定义扩展,可以更好的支持扩展
通过 HTTP/1.1 协议的 101 状态码进行握手。为了创建 WebSocket 连接,需要通过浏览器发出请求,之后服务器进行回应,这个过程通常称为 “握手”(Handshaking)。
客户端请求
1 | GET ws://echo.websocket.org/ HTTP/1.1 |
服务端响应
1 | HTTP/1.1 101 Web Socket Protocol Handshake |
1 | package main |
使用curl进行简单的测试
1 | test curl -i -N \ |
1 | 0 1 2 3 |
位 名称 说明
0 FIN 结束标志:1=最后一帧,0=后续还有帧
1 RSV1 保留位1:默认为0,扩展协议可使用
2 RSV2 保留位2:默认为0,扩展协议可使用
3 RSV3 保留位3:默认为0,扩展协议可使用
4-7 Opcode 操作码:定义帧类型
值 名称 说明
0x0 连续帧 分片消息的后续帧
0x1 文本帧 UTF-8文本数据
0x2 二进制帧 二进制数据
0x8 连接关闭 关闭连接请求
0x9 Ping 心跳检测请求
0xA Pong 心跳检测响应
0xB-F 保留 用于未来控制帧
位 名称 说明
8 MASK 掩码标志:1=负载被掩码处理
9-15 Payload len 基础负载长度
值范围 实际负载长度表示方式
0-125 直接表示负载长度
126 后续2字节表示长度
127 后续8字节表示长度
####### 扩展负载长度
当基础负载长度(Payload len)为126时:
接下来的2字节(16位)表示实际负载长度
最大长度:65,535字节 (2^16 - 1)
当基础负载长度(Payload len)为127时:
接下来的8字节(64位)表示实际负载长度
最大长度:18,446,744,073,709,551,615字节 (2^64 - 1)
注意:最高位必须为0(防止长度值过大)
当MASK位为1时出现
固定4字节(32位)长度
用于对负载数据进行掩码处理
客户端到服务器:必须使用掩码(MASK=1)
服务器到客户端:禁止使用掩码(MASK=0)
可以简单写一个服务端的解析
1 | 简单保持连接(实际需要处理帧数据) |
应用端
1 | ➜ test websocat ws://localhost:9999/ws |
服务端
1 | 服务器运行在 http://localhost:9999 |
这样就可以实现简单websocket协议了
从三个角度进行websocket的解析
感知何时需要重新连接
快速断开连接
快速发起新连接
(1)首先:通过定时发送心跳包的方式检测当前连接是否可用,同时监测网络恢复事件,在恢复后立即发送一次心跳,快速感知当前状态,判断是否需要重连;
(2)其次:正常情况下由服务器断开旧连接,与服务器失去联系时直接弃用旧连接,上层模拟断开,来实现快速断开;
(3)最后:发起新连接时使用退避算法延迟一段时间再发起连接,同时考虑到资源浪费和重连速度,可以在网络离线时调大重连间隔,在网络正常或网络由offline变为online时缩小重连间隔,使之尽可能快地重连上。
对于go websocket,进行源码解析,分析这个websocket库
1 | //main.go |
1 | //hub.go |
1 | //client.go |
home.html
1 |
|
整体代码比较简单
1.hub有三个事件,注册,注销,广播
2.用户get这个ws的时候,使用hub进行注册
3.运行两个协程,一个监听消息,消息转发给广播,一个发送消息同时发送ping,如果不回应就结束
我想知道以下细节
ping pong 是控制帧,OpCode。不会读取数据
在这个websocket的库中pong的函数需要自己设置,在
pong没有实现这个函数
1 | func (c *Conn) SetPongHandler(h func(appData string) error) { |
ping 函数有pong进行处理
1 | func (c *Conn) SetPingHandler(h func(appData string) error) { |
在大部分的情况下,都是服务端ping客户端,少部分的情况下客户端ping服务端
1 | func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) { |
可以看出来,跟我们写的websocket的简单解析,有一点点的相似,不过它定义一个conn struct,进行通信的安排
可以关注一下这个
1 | var brw *bufio.ReadWriter |
使用br复用http的缓冲读取器
查看一下conn struct
中有一些比较有意思的
比如
writePool BufferPool
可以自己定义一个writePool,来优化读写性能