Golang 既可以写 websocket 的 server 端也可以写 websocket 的 client 端,前者网上的资料很多后者甚少,今天遇到写 client 的需求,在此做个总结。
- 测试地址:火币网
- websocket包:golang.org/x/net/websocket
1. 建立连接。
连接成功建立后,client 和 server 均可以随时往数据通道里写数据同时也可以从中读取数据。
1 2 3 4 5 6
| var wsurl = "wss://api.huobi.pro/ws" var origin = "http://api.huobi.pro/" ws, err := websocket.Dial(wsurl, "", origin) if err != nil { panic(err) }
|
2. 写数据。
在通道已建立的前提下,写数据操作通过一行代码即可完成:
1 2 3
| func sendMessage(data []bytes) { ws.Write(msg) }
|
3.1 读数据。
最简单的方法是调用func (ws *Conn) Read(msg []byte) (n int, err error)
方法,定义一个用来接收数据的[]byte
数组当作参数传入,但是由于不知道server发来的数据长度,所以一般是定义一个足够大的字节[]byte
数组,这样读取一来浪费内存二来处理起来麻烦,不建议使用;
1 2 3 4 5 6 7
| func readMessage(ws *websocket.Conn) { data := make([]byte, 1024*10) _, err := ws.Read(data) if err != nil { log.Println(err) } }
|
3.2 读数据。
对于都是以json方式传输的数据,websocket包提供了将每条message读取到一个interface{}
中的方法,等同于json.Unmarshal
。
1 2 3 4 5 6 7
| func readJsonMessage(ws *websocket.Conn) { var data interfact{} err := websocket.Message.Receive(ws, data) if err != nil { log.Println(err) } }
|
3.3 读数据。
3.2是将接收到的数据直接unmarshal到struct里了,而我的需求比这个要麻烦一点:server发来的数据[]byte
数组是压缩过的,所以接收到数据后第一步应该解压缩然后才能unmarshal,所以不能再用3.2的方式,参照3.2的源码,实现方式如下。
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 30 31 32 33
| func readOriginMessage(ws *websocket.Conn) { again: fr, err := ws.NewFrameReader() if err != nil { log.Printf("new frame reader err %v", err) return } frame, err := ws.HandleFrame(fr) if err != nil { log.Printf("handle frame err %v", err) return } if frame == nil { goto again } bytes, err := ioutil.ReadAll(frame) if err != nil { log.Printf("read frame data err %v", err) } unzipData, err := utils.UnzipByte(bytes) if err != nil { log.Printf("unzip data err %v", err) }
var message map[string]interface{} e := json.Unmarshal(unzipData, &message) if e != nil { log.Printf("unmarshal err %v", e) } log.Printf("message content= %+v", message) }
|