websocket协议初探

436 阅读2分钟

背景

在公司的http server端代码中看到有这样的片段:

import "github.com/tmc/grpc-websocket-proxy/wsproxy"
wsProxy := wsproxy.WebsocketProxy(
  coreMux,
  wsproxy.WithLogger(logrus.StandardLogger()),
  ...
)

这里作了一个websocket的代理,将之前的grpc-gateway的流传输协议升级为ws

什么是websocket协议

HTTP协议是常用的应用层协议,是一种客户端请求服务端响应的模式,无法实现服务端想要主动向客户端推送的场景,如实现聊天室、push等。虽然可以通过轮询、长轮询、长连接去实现实时获取消息的能力,但是比如轮询需要客户端不断的发送请求,会有大量无效的请求;长轮询需要保持连接不断开一段时间,超时之后断开再重新连接,对于请求量不大的场景(如配置中心的动态更新,长轮询是不错的方式),但是假如连接数很多并且一直占用连接通道,对服务端资源是一种浪费。

websocket协议,最大的特点是服务端主动向客户端推送消息,客户端主动向服务端发送消息,是一种双向传输协议。

库使用

golang.org/x/net 相比于 github.com/gorilla ,缺少不少功能实现。

使用Upgrade将一个http请求升级为ws协议

var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
}

func handler(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Println(err)
        return
    }
    ... Use conn to send and receive messages.
}

建立好连接后,使用ReadMessage和WriteMessage进行消息的收发

for {
    messageType, p, err := conn.ReadMessage()
    if err != nil {
        log.Println(err)
        return
    }
    if err := conn.WriteMessage(messageType, p); err != nil {
        log.Println(err)
        return
    }
}

(聊天室可以用这种方式实现,本质上是一个消息的广播,维护一个聊天室所有用户连接的map, 在ReadMessage后,对map中的所有连接发送msg)

引用

websocket-百度百科

Go实现聊天室

gorilla-websocket