cst同步使用”golang.org/x/net/websocket”和gorilla区别
[TOC]
心跳处理方面
gorilla
- 从源码可以看出每当遇到ping、pong的数据帧的时候,都会有一个处理函数,gorilla提供了以下的接口去设置这一个处理函数,这样就可以在后端来发ping,然后查看客户端是否有回复pong
//-----pong func (c *Conn) SetPongHandler(h func(appData string) error) { if h == nil { h = func(string) error { return nil } } c.handlePong = h } //-----ping func (c *Conn) SetPingHandler(h func(appData string) error) { if h == nil { h = func(message string) error { err := c.WriteControl(PongMessage, []byte(message), time.Now().Add(writeWait)) if err == ErrCloseSent { return nil } else if _, ok := err.(net.Error); ok { return nil } return err } } c.handlePing = h }
- 因此后端可以这样处理pong
- 说明:当客户端没有任何操作的时候,在读操作的阻塞中,收到pong或者ping是不会接触阻塞的,因此无法重新开始循环更新读超时,但是通过这样的操作可以一直用心跳来更新读超时
//设置心跳处理函数 conn.SetPongHandler(func(appData string) error { client.Conn.SetReadDeadline(time.Now().Add(PongWait)) // fmt.Println("Pong") return nil }) //-----读操作 for { //设置读超时 conn.SetReadDeadline(time.Now().Add(PongWait)) //这里在消息来到之前会阻塞 _, msg, err := client.Conn.ReadMessage() if err != nil { if netErr, ok := err.(net.Error); ok && netErr.Timeout() { client.logger.Info("读超时,error:" + err.Error()) return nil } else if websocket.IsCloseError(err, websocket.CloseGoingAway) { client.logger.Info("client of id:" + client.UserID + " close going away") return nil } else if websocket.IsCloseError(err, websocket.CloseNormalClosure) { client.logger.Info("client of id:" + client.UserID + " closed normally") return nil } client.logger.Error("Client of id:" + client.UserID + " ReadMessage Error: " + err.Error()) return fmt.Errorf("Client of id:%s ReadMessage Error: %v", client.UserID, err) } }
- gorilla源码
switch frameType { case PongMessage: if err := c.handlePong(string(payload)); err != nil { return noFrame, err } case PingMessage: if err := c.handlePing(string(payload)); err != nil { return noFrame, err } case CloseMessage: closeCode := CloseNoStatusReceived closeText := "" if len(payload) >= 2 { closeCode = int(binary.BigEndian.Uint16(payload)) if !isValidReceivedCloseCode(closeCode) { return noFrame, c.handleProtocolError("bad close code " + strconv.Itoa(closeCode)) } closeText = string(payload[2:]) if !utf8.ValidString(closeText) { return noFrame, c.handleProtocolError("invalid utf8 payload in close frame") } } if err := c.handleClose(closeCode, closeText); err != nil { return noFrame, err } return noFrame, &CloseError{Code: closeCode, Text: closeText} }
golang.org/x/net/websocket
- 从源吗我们可以看出,这个库是定死了收到pong帧或者ping帧的操作,而且收到pong和ping帧后它也是不会解除读操作的阻塞的
- 因此我想到的方法就是前端来发ping来维持前后端的联系
- golang.org/x/net/websocket源码
switch frame.PayloadType() { case ContinuationFrame: frame.(*hybiFrameReader).header.OpCode = handler.payloadType case TextFrame, BinaryFrame: handler.payloadType = frame.PayloadType() case CloseFrame: return nil, io.EOF case PingFrame, PongFrame: b := make([]byte, maxControlFramePayloadLength) n, err := io.ReadFull(frame, b) if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF { return nil, err } io.Copy(io.Discard, frame) if frame.PayloadType() == PingFrame { if _, err := handler.WritePong(b[:n]); err != nil { return nil, err } } return nil, nil }
关闭连接的处理
gorilla
- gorilla提供了检测错误码的接口,因此我们可以很容易根据状态码来查看用户的退出是否是正常的
func IsCloseError(err error, codes ...int) bool { if e, ok := err.(*CloseError); ok { for _, code := range codes { if e.Code == code { return true } } } return false }
golang.org/x/net/websocket
- 这个库并没有提供检测状态码的操作,并且它也没有将状态码暴露出来,遇到关闭帧的时候马上就会返回io.EOF,因此假如想要查看状态码做一些更细节的操作,就不能把状态码放到关闭帧里面。
switch frame.PayloadType() { case ContinuationFrame: frame.(*hybiFrameReader).header.OpCode = handler.payloadType case TextFrame, BinaryFrame: handler.payloadType = frame.PayloadType() case CloseFrame: return nil, io.EOF case PingFrame, PongFrame: b := make([]byte, maxControlFramePayloadLength) n, err := io.ReadFull(frame, b) if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF { return nil, err } io.Copy(io.Discard, frame) if frame.PayloadType() == PingFrame { if _, err := handler.WritePong(b[:n]); err != nil { return nil, err } } return nil, nil }
Comments are closed