github.com/gorilla/websocket

概述

Conn 类型表示一个 WebSocket 连接。服务器应用程序从 HTTP 请求处理程序调用 Upgrader.Upgrade 方法以获取 *Conn:

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.
}

调用连接的 WriteMessage 和 ReadMessage 方法以字节片的形式发送和接收消息。这段代码展示了如何使用这些方法来回显消息:

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
    }
}

在上面的代码片段中,p 是 []byte,messageType 是带有 valuewebsocket.BinaryMessage 或 websocket.TextMessage 的 int。

应用程序还可以使用 io.WriteClose 和 io.Reader 接口发送和接收消息。要发送消息,请调用连接 NextWriter 方法以获取 io.WriteCloser,将消息写入编写器并在完成后关闭编写器。要接收消息,请调用连接 NextReader 方法以获取 io.Reader 并读取,直到返回 io.EOF。此片段显示如何使用 NextWriter 和 NextReader 方法回显消息:

for {
    messageType, r, err := conn.NextReader()
    if err != nil {
        return
    }
    w, err := conn.NextWriter(messageType)
    if err != nil {
        return err
    }
    if _, err := io.Copy(w, r); err != nil {
        return err
    }
    if err := w.Close(); err != nil {
        return err
    }
}

数据信息

WebSocket 协议区分文本和二进制数据消息。文本消息被解释为 UTF-8 编码的文本。二进制消息的解释留给应用程序。

此包使用 TextMessage 和 BinaryMessage 整数常量来标识两种数据消息类型。ReadMessage 和 NextReader 方法返回接收到的消息的类型。WriteMessage 和 NextWriter 方法的 messageType 参数指定已发送消息的类型。

确保文本消息是有效的 UTF-8 编码文本是应用程序的责任。

控制消息

WebSocket 协议定义了三种类型的控制消息:close、ping 和 pong。调用连接 WriteControl、WriteMessage 或 NextWriter 方法向对端发送控制消息。

连接通过使用 SetCloseHandler 方法调用处理函数集并通过从 NextReader、ReadMessage 或消息 Read 方法返回 *CloseError 来处理接收到的关闭消息。默认的 closehandler 向对等方发送关闭消息。

连接通过使用 SetPingHandler 方法调用处理函数集来处理接收到的 ping 消息。默认的 ping 处理程序向对等方发送 pongmessage。

连接通过使用 SetPongHandler 方法调用处理函数集来处理接收到的 pong 消息。默认的 pong 处理程序什么都不做。如果应用程序发送 ping 消息,则应用程序应设置 apong 处理程序以接收相应的 pong。

从 NextReader、ReadMessage 和消息阅读器 Read 方法调用控制消息处理函数。当处理程序写入连接时,默认的关闭和 pinghandler 可以在短时间内阻止这些方法。

应用程序必须读取连接以处理从对等方发送的关闭、ping 和 pong 消息。如果应用程序对来自 peer 的消息不感兴趣,那么应用程序应该启动一个 goroutine 来读取和丢弃来自 peer 的消息。一个简单的例子是:

func readLoop(c *websocket.Conn) {
    for {
        if _, _, err := c.NextReader(); err != nil {
            c.Close()
            break
        }
    }
}

并发

连接支持一个并发读取器和一个并发写入器。

应用程序负责确保不超过一个 goroutine 同时调用写入方法(NextWriter、SetWriteDeadline、WriteMessage、WriteJSON、EnableWriteCompression、SetCompressionLevel),并且不超过一个 goroutine 调用读取方法(NextReader、SetReadDeadline、ReadMessage、ReadJSON、SetPongHandler、SetPingHandler ) 同时。

Close 和 WriteControl 方法可以与所有其他方法同时调用。

Origin 来源

Web 浏览器允许 Javascript 应用程序打开到任何主机的 WebSocket 连接。由服务器使用浏览器发送的 Originrequest 标头强制执行原始策略。

升级程序调用 CheckOrigin 字段中指定的函数来检查来源。如果 CheckOrigin 函数返回 false,则 Upgrade 方法将失败 WebSocket 握手,HTTP 状态为 403。

如果 CheckOrigin 字段为 nil,则升级程序使用安全默认值:如果 Origin 请求标头存在且 Origin 主机不等于 Host 请求标头,则握手失败。

已弃用的包级升级功能不执行来源检查。应用程序负责在调用 Upgrade 函数之前检查 Origin 标头。

缓冲区

连接缓冲网络输入和输出,以减少读取或写入消息时的系统调用次数。

写缓冲区也用于构造 WebSocket 帧。看RFC 6455,第 5 节讨论消息框架。每次将写入缓冲区刷新到网络时,都会将 WebSocket 帧头写入网络。减小写入缓冲区的大小会增加连接上的帧开销。

缓冲区大小(以字节为单位)由 Dialer 和 Upgrader 中的 ReadBufferSize 和 WriteBufferSize 字段指定。当缓冲区大小字段设置为零时,拨号器使用默认大小 4096。当缓冲区大小字段设置为零时,升级程序会重用 HTTP 服务器创建的缓冲区。在撰写本文时,HTTP 服务器缓冲区的大小为 4096。

缓冲区大小不限制可以由连接读取或写入的消息的大小。

默认情况下,缓冲区会在连接的整个生命周期内保留。如果设置了 Dialer 或 Upgrader WriteBufferPool 字段,则连接仅在写入消息时才保留写入缓冲区。

应用程序应调整缓冲区大小以平衡内存使用和性能。增加缓冲区大小会使用更多内存,但可以减少读取或写入网络的系统调用次数。在写入的情况下,增加缓冲区大小可以减少写入网络的帧头数量。

设置缓冲区参数的一些准则是:

将缓冲区大小限制为最大预期消息大小。大于最大消息的缓冲区没有任何好处。

根据消息大小的分布,将缓冲区大小设置为小于最大预期消息大小的值可以大大减少内存使用,而对性能的影响很小。下面是一个例子:如果 99% 的消息小于 256 字节并且最大消息大小为 512 字节,那么 256 字节的缓冲区大小将导致比 512 字节的缓冲区大小多 1.01 个系统调用。内存节省为 50%。

当应用程序在大量连接上具有适度的写入次数时,写入缓冲池很有用。当缓冲区被池化时,较大的缓冲区大小对总内存使用的影响会降低,并有利于减少系统调用和帧开销。

压缩实验

每个消息压缩扩展 ( RFC 7692 ) 由此包以有限的容量进行实验性支持。在 Dialer 或 Upgrader 中将 EnableCompression 选项设置为 true 将尝试协商每个消息 deflatesupport。

var upgrader = websocket.Upgrader{ 
    EnableCompression: true, 
}

如果与连接的对等方成功协商压缩,则以压缩形式接收的任何消息都将自动解压缩。所有读取方法都将返回未压缩的字节。

通过调用相应的 Conn 方法,可以启用或禁用写入连接的消息的每条消息压缩:

conn.EnableWriteCompression(false)

目前这个包不支持“上下文接管”的压缩。这意味着消息必须单独压缩和解压缩,不保留跨消息的滑动窗口或字典状态。有关详细信息,请参阅RFC 7692.

压缩的使用是实验性的,可能会导致性能下降。

0条评论 顺序楼层
请先登录再回复