go语言websocket库Gorilla Websocket

go语言websocket库Gorilla Websocket

概览

Conn类型代表一个Websocket连接,服务端通过在HTTP请求的handler中调用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.
}

通过调用ConnWriteMessageReadMessage方法来发收消息,消息以byte切片存储。下面例子展示了如何用这两个方法实现echo:

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是int型,值可能是websocket.BinaryMessagewebsocket.TextMessage

应用程序也可以使用io.WriteCloserio.Reader接口来发收消息。要想发送消息,先调用ConnNextWriter方法得到一个io.WriteCloser,然后写消息到这个writer,写完消息后close它。要想接收消息,先调用ConnNextReader方法得到一个io.Reader,然后读直到返回io.EOF。下面的例子展示如何使用NextWriterNextReader方法实现echo:

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整型常量来标识这两种消息类型。ReadMessageNextReader方法返回正接收的消息类型。WriteMessageNextWriter方法的messageTyp参数指定要发送的消息类型。

控制类消息

Websocket协议定义了三种控制消息:Close、Ping和Pong。通过调用ConnWriteControlWriteMessageNextWriter方法向对端发送控制消息。

Conn收到了Close消息之后,调用由SetCloseHandler方法设置的handler函数,然后从NextReaderReadMessage或消息的Read 方法返回一个*CloseError。缺省的close handler会发送一个Close消息到对端。

Conn收到了Ping消息之后,调用由SetPingHandler 方法设置的handler函数。缺省的ping handler会发送一个Pong消息到对象。

Conn收到了Pong消息之后,调用由SetPongHandler 设置的handler函数。缺省的pong handler什么也不做。

控制消息的handler函数是从NextReaderReadMessage和消息的Read方法中调用的。缺省的close handler和ping handler向对端写数据时可能会短暂阻塞这些方法。

应用程序必须读取Conn,使得对端发送的close、ping、和pong消息能够得到处理。即使应用程序不关心对端发送的消息,也应该启动一个goroutine来读取对端的消息并丢弃。例如:

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

并发

Conn支持一个并发的读取和一个并发的写入。

应用程序应该保证不会有多于一个goroutine并发调用写方法(NextWriterSetWriteDeadlineWriteMessageWriteJSONEnableWriteCompressionSetCompressionLevel),而且不会有多于一个goroutine并发调用读方法(NextReaderSetReadDeadlineReadMessageReadJSONSetPongHandlerSetPingHandler)。

CloseWriteControl方法可以被并发的调用。

Origin控制

浏览器允许Javascript应用程序打开连接到任意主机的Websocket。需要由服务端负责根据浏览器请求中的Origin头部来执行一个origin策略。

Upgrader调用CheckOrigin域所指定的函数来检查origin。如果CheckOrigin函数返回false,Upgrade方法将会握手失败并响应HTTP403状态。

如果CheckOrigin字段是nil,Upgrarder将使用一个缺省的安全的策略:当请求头部出现了Origin但值不等于Host头,将会握手失败。

有一个包级别的Upgrade函数,该函数已经废弃了,该函数不会执行origin检查,应用程序需要在调用该函数之前检查Origin头部。

缓冲

Conn对网络读写进行缓冲,减少读写消息时的系统调用次数。

写缓冲也用于构建WebSocket帧。每当一个写缓冲被flush到网络时,都会写一个WebSocket帧头到网络。减少写缓冲的尺寸会增加连接上写出的帧数。

通过DialerUpgrader对象的ReadBufferSizeWriteBufferSize域来设置缓冲区的尺寸,以字节为单位。当Dialer的缓冲区尺寸被设置为0时,将使用缺省的4096。当Upgrader的缓冲区尺寸被设置为0时,将会重用HTTP server创建的缓冲区。在编写这篇文档的时候,HTTP server的缓冲区尺寸为4096。

缓冲区的尺寸并不会对连接上可读写的消息的大小起到限制。

缺省情况下,在一个连接的整个生命周期中都会持有该缓冲区。但是,如果设置了DialerUpgraderWriteBufferPool域,一个连接的写缓冲区仅仅在写消息的时候被持有。

应用程序可通过调整缓冲区大小来平衡内存使用和性能。增加缓冲区尺寸会导致使用更多的内存,但是可以减少读写网络时的系统调用次数。对于写消息,增加缓冲区尺寸可以减少写出的帧数。

一些设置缓冲区尺寸的方针:

限制缓冲区尺寸小于预期的最大消息大小。缓冲区大于最大消息不会带来什么益处。

基于消息大小的分布情况来设置。设置缓冲区尺寸小于预期的最大消息大小可以大幅的降低内存使用,且对性能仅仅有小幅影响。下面是一个例子:如果99%的消息小于256字节,最大消息大小是512字节,那么设置缓冲区为256字节时的系统调用次数是512字节时的1.01倍,但是内存却节省了50%。

当一个应用程序在大量的连接上执行数量不多的写出时,使用缓冲池将更好。当使用缓冲池时,设置大尺寸的缓冲区也不会带来大量的内存占用,同时还能减少系统调用次数和写出的帧数量。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时空旅客er

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值