Gin + Gorilla Websocket

3,893 阅读2分钟

Gin + Gorilla Websocket

之前经过和添耀的交流,已经知道了在 go-gqlgel 的 subscription 怎么做了,不得不说资料确实有点匮乏,graphql 在国内的应用程度还是不高,不过 graphql 仍然是一门十分优秀的技术,我还是十分喜爱的。

不过这个博客是说 Gin 和 Gorilla怎么做 websocket 的,简单测试一下

安装

$ go get github.com/gorilla/websocket
$ go get -u github.com/gin-gonic/gin

使用

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"log"
	"net/http"
)

var upgrade = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

func main() {
	r := gin.Default()
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "pong",
		})
	})
	r.GET("/ws", func(c *gin.Context) {
    // 升级成 websocket 连接
		ws, err := upgrade.Upgrade(c.Writer, c.Request, nil)
		if err != nil {
			log.Fatalln(err)
		}
    // 完成时关闭连接释放资源
		defer ws.Close()
		go func() {
      // 监听连接“完成”事件,其实也可以说丢失事件
			<-c.Done()
      // 这里也可以做用户在线/下线功能
			fmt.Println("ws lost connection")
		}()
		for {
      // 读取客户端发送过来的消息,如果没发就会一直阻塞住
			mt, message, err := ws.ReadMessage()
			if err != nil {
				fmt.Println("read error")
				fmt.Println(err)
				break
			}
			if string(message) == "ping" {
				message = []byte("pong")
			}
			err = ws.WriteMessage(mt, message)
			if err != nil {
				fmt.Println(err)
				break
			}
		}
	})
	r.Run() // listen and serve on 0.0.0.0:8080
}
  • 读取信息失败时,我们需要关注异常状态去解决异常,这里附带源码中的异常值
// Close codes defined in RFC 6455, section 11.7.
const (
	CloseNormalClosure           = 1000
	CloseGoingAway               = 1001
	CloseProtocolError           = 1002
	CloseUnsupportedData         = 1003
	CloseNoStatusReceived        = 1005
	CloseAbnormalClosure         = 1006
	CloseInvalidFramePayloadData = 1007
	ClosePolicyViolation         = 1008
	CloseMessageTooBig           = 1009
	CloseMandatoryExtension      = 1010
	CloseInternalServerErr       = 1011
	CloseServiceRestart          = 1012
	CloseTryAgainLater           = 1013
	CloseTLSHandshake            = 1015
)
 
func (e *CloseError) Error() string {
	s := []byte("websocket: close ")
	s = strconv.AppendInt(s, int64(e.Code), 10)
	switch e.Code {
	case CloseNormalClosure:
		s = append(s, " (normal)"...)
	case CloseGoingAway:
		s = append(s, " (going away)"...)
	case CloseProtocolError:
		s = append(s, " (protocol error)"...)
	case CloseUnsupportedData:
		s = append(s, " (unsupported data)"...)
	case CloseNoStatusReceived:
		s = append(s, " (no status)"...)
	case CloseAbnormalClosure:
		s = append(s, " (abnormal closure)"...)
	case CloseInvalidFramePayloadData:
		s = append(s, " (invalid payload data)"...)
	case ClosePolicyViolation:
		s = append(s, " (policy violation)"...)
	case CloseMessageTooBig:
		s = append(s, " (message too big)"...)
	case CloseMandatoryExtension:
		s = append(s, " (mandatory extension missing)"...)
	case CloseInternalServerErr:
		s = append(s, " (internal server error)"...)
	case CloseTLSHandshake:
		s = append(s, " (TLS handshake error)"...)
	}
	if e.Text != "" {
		s = append(s, ": "...)
		s = append(s, e.Text...)
	}
	return string(s)
}

测试

在 chrome 中安装插件:WebSocket King Client,这个名字非常霸气,确实也很好用,方便。

官网地址:websocketking.com/

image-20220531110753397

测试成功,我们点击 Disconnection之后,服务端也监听到了连接的关闭。

总结

以上只是练习代码,实际生产环境中的要求和需求不会这么简单,所以一定会遇到像 http 请求那样的不同业务的 websocket 请求,这时就需要去做websocket中的路由分发了,这部分我们下章节再讲述。