golang中websocket编程
WebSocket协议解读
websocket和http协议的关联:
- 都是应用层协议,都基于tcp传输协议。
- 跟http有良好的兼容性,ws和http的默认端口都是80,wss和https的默认端口都是443。
- websocket在握手阶段采用http发送数据。
websocket和http协议的差异:
- http是半双工,而websocket通过多路复用实现了全双工。
- http只能由client主动发起数据请求,而websocket还可以由server主动向client推送数据。在需要及时刷新的场景中,http只能靠client高频地轮询,浪费严重。
- http是短连接(也可以实现长连接, HTTP1.1 的连接默认使用长连接),每次数据请求都得经过三次握手重新建立连接,而websocket是长连接。
- http长连接中每次请求都要带上header,而websocket在传输数据阶段不需要带header。
WebSocket是HTML5下的产物,能更好的节省服务器资源和带宽,websocket应用场景举例:
- html5多人游戏
- 聊天室
- 协同编辑
- 基于实时位置的应用
- 股票实时报价
- 弹幕
- 视频会议
websocket握手协议:
Request Header
1 | Sec-Websocket-Version:13 |
Response Header
1 | Upgrade:websocket |
- Upgrade:websocket和Connection:Upgrade指明使用WebSocket协议。
- Sec-WebSocket-Version 指定Websocket协议版本。
- Sec-WebSocket-Key是一个Base64 encode的值,是浏览器随机生成的。
- 服务端收到Sec-WebSocket-Key后拼接上一个固定的GUID,进行一次SHA-1摘要,再转成Base64编码,得到Sec-WebSocket-Accept返回给客户端。客户端对本地的Sec-WebSocket-Key执行同样的操作跟服务端返回的结果进行对比,如果不一致会返回错误关闭连接。如此操作是为了把websocket header跟http header区分开。
WebSocket CS架构实现
首先需要安装gorilla的websocket包。
1 | go get github.com/gorilla/websocket |
- 将http升级到WebSocket协议。
1 | func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*websocket.Conn, error) |
- 客户端发起握手,请求建立连接。
1 | func (*websocket.Dialer) Dial(urlStr string, requestHeader http.Header) (*websocket.Conn, *http.Response, error) |
- 基于connection进行read和write。
ws_server.go
1 | package main |
ws_client.go
1 | package main |
websocket发送的消息类型有5种:TextMessag,BinaryMessage, CloseMessag,PingMessage,PongMessage。TextMessag和BinaryMessage分别表示发送文本消息和二进制消息。CloseMessage关闭帧,接收方收到这个消息就关闭连接
PingMessage和PongMessage是保持心跳的帧,发送方接收方是PingMessage,接收方发送方是PongMessage,目前浏览器没有相关api发送ping给服务器,只能由服务器发ping给浏览器,浏览器返回pong消息。
聊于室实现
gorilla的websocket项目中有一个聊天室的demo,此处讲一下它的设计思路。
Hub
- Hub持有每一个Client的指针,broadcast管道里有数据时把它写入每一个Client的send管道中。
- 注销Client时关闭Client的send管道。
Client
- 前端(browser)请求建立websocket连接时,为这条websocket连接专门启一个协程,创建一个client。
- client把前端发过来的数据写入hub的broadcast管道。
- client把自身send管道里的数据写给前端。
- client跟前端的连接断开时请求从hub那儿注销自己。
Front
- 当打开浏览器页面时,前端会请求建立websocket连接。
- 关闭浏览器页面时会主动关闭websocket连接。
存活监测
- 当hub发现client的send管道写不进数据时,把client注销掉。
- client给websocket连接设置一个读超时,并周期性地给前端发ping消息,如果没有收到pong消息则下一次的conn.read()会报出超时错误,此时client关闭websocket连接。
服务端
1 | package main |
客户端
go客户端
writer
1 | package main |
reader
1 | package main |
评论