Add WebSocket function to Go's Gin web framework to make WebSocket easier to use

 

Gin is a go web framework. It has lightweight, high performance, fast running speed, grouped routers, good crash capture and error handling, and very good support for middleware, rest api and json.

In short, in the field of Go language development, it is a web framework worthy of careful study. Official address: https://github.com/gin-gonic/gin

But recently I wanted to try the websocket function, and found that there is no Gin framework.

Go official does not provide support for WebSocket, you must choose a package provided by a third party.

There are two commonly used ones, golang.org/x/net/websocket and  https://github.com/gorilla/websocket

The examples in the book "Go Web Programming" use golang.org/x/net/websocket

Among them, gorilla/websocket is more commonly used. In the Paho GO mqtt client library of Apache and iris, another web framework of Go, the gorilla/websocket library is used.

Is it enough to directly introduce gorilla/websocket into the gin framework?

But you have to complete the packaging yourself. If you don't spend time and effort in packaging, it will not be very useful and stability is more difficult to guarantee.

The examples of using gorilla's websocket library in gin found on the Internet are just some simple tests, which are not easy to use at all.

Next, in order to make websocket more useful in gin, transplant the websocket function in iris framework to gin framework, it is easy to use, use as follows:

github address:  https://github.com/yangyongzhen/gin-websocket.git

package main

import (
	"fmt"
	//"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
	"net/http"
	"strings"
	"websockTest/websocket"
)

func main() {
	ws := websocket.New(websocket.Config{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
	})

	ws.OnConnection(handleConnection)

	r := gin.Default()
	//允许跨域
	//config := cors.DefaultConfig()
	//config.AllowOrigins = []string{"http://127.0.0.1:9090"}
	//r.Use(Cors())
	//静态资源
	r.Static("/static", "./static")
	r.LoadHTMLGlob("views/*")
	r.GET("/api/v3/device", ws.Handler())
	r.GET("/test", func(c *gin.Context) {
		c.HTML(http.StatusOK, "test.html", gin.H{
			"title": "this is a test",
		})
	})
	r.Run(":9090")
}

func handleConnection(c websocket.Connection) {
	fmt.Println("client connected,id=", c.ID())
	c.Write(1, []byte("welcome client"))
	// 测试从浏览器中读取事件
	c.On("chat", func(msg string) {
		// 将消息打印到控制台
		fmt.Printf("%s sent: %s\n", c.Context().ClientIP(), msg)
		// 将消息写回客户端消息所有者:
		// c.Emit("chat", msg)
		c.To(websocket.All).Emit("chat", msg)
	})

	c.OnMessage(func(msg []byte) {
		fmt.Println("received msg:", string(msg))
		c.Write(1, []byte("hello aa"))
		c.To(websocket.All).Emit("chat", msg)
	})

	c.OnDisconnect(func() {
		fmt.Println("client Disconnect,id=", c.ID())
	})
}

Attached, some test codes found on the Internet are as follows: 

package server

import (
	"fmt"
	"net"
	"net/http"
	"time"

	"github.com/gorilla/websocket"
)

const (
	// Time allowed to write a message to the peer.
	writeWait = 10 * time.Second
	// Maximum message size allowed from peer.
	maxMessageSize = 8192
	// Time allowed to read the next pong message from the peer.
	pongWait = 60 * time.Second
)

type WsServer struct {
	listener net.Listener
	addr     string
	upgrade  *websocket.Upgrader
}

func NewWsServer() *WsServer {
	ws := new(WsServer)
	ws.addr = "0.0.0.0:9090"
	ws.upgrade = &websocket.Upgrader{
		ReadBufferSize:  4096,
		WriteBufferSize: 1024,
		CheckOrigin: func(r *http.Request) bool {
			if r.Method != "GET" {
				fmt.Println("method is not GET")
				return false
			}
			if r.URL.Path != "/ws" {
				fmt.Println("path error")
				return false
			}
			return true
		},
	}
	return ws
}

func (self *WsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	if r.URL.Path != "/ws" {
		httpCode := http.StatusInternalServerError
		reasePhrase := http.StatusText(httpCode)
		fmt.Println("path error ", reasePhrase)
		fmt.Println("path error,url=", r.URL.Path)
		http.Error(w, reasePhrase, httpCode)
		return
	}

	conn, err := self.upgrade.Upgrade(w, r, nil)
	if err != nil {
		fmt.Println("websocket error:", err)
		return
	}
	fmt.Println("client connect :", conn.RemoteAddr())
	go self.connHandle(conn)

}

func (self *WsServer) connHandle(conn *websocket.Conn) {
	defer func() {
		conn.Close()
	}()
	stopCh := make(chan int)
	go self.send(conn, stopCh)
	conn.SetReadLimit(maxMessageSize)
	conn.SetReadDeadline(time.Now().Add(pongWait))
	conn.SetPongHandler(func(string) error {
		fmt.Println("this is PongHandler")
		conn.SetReadDeadline(time.Now().Add(pongWait))
		if err := conn.WriteMessage(websocket.PongMessage, nil); err != nil {
			fmt.Println("resp pong error", err)
		} else {
			fmt.Println("resp pong ok")
		}
		return nil
	})
	for {
		conn.SetReadDeadline(time.Now().Add(time.Millisecond * time.Duration(60000)))
		_, msg, err := conn.ReadMessage()
		if err != nil {
			// 判断是不是超时
			if netErr, ok := err.(net.Error); ok {
				if netErr.Timeout() {
					fmt.Printf("ReadMessage timeout remote: %v\n", conn.RemoteAddr())
					close(stopCh)
					return
				}
			}
			// 其他错误,如果是 1001 和 1000 就不打印日志
			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseNormalClosure) {
				fmt.Printf("ReadMessage other remote:%v error: %v \n", conn.RemoteAddr(), err)
			}
			//close(stopCh)
			return
		}
		fmt.Println("收到消息:", string(msg))
	}
}

//测试一次性发送 10万条数据给 client, 如果不使用 time.Sleep browser 过了超时时间会断开
func (self *WsServer) send10(conn *websocket.Conn) {
	for i := 0; i < 1; i++ {
		data := fmt.Sprintf("hello websocket test from server %v,count=%d", time.Now().UnixNano(), i+1)
		err := conn.WriteMessage(1, []byte(data))
		if err != nil {
			fmt.Println("send msg faild ", err)
			return
		}
		//time.Sleep(time.Millisecond * 1)
	}
}

func (self *WsServer) send(conn *websocket.Conn, stopCh chan int) {
	self.send10(conn)
	for {
		select {
		case <-stopCh:
			fmt.Println("connect closed")
			return
			// case <-time.After(time.Second * 1):
			// 	fmt.Println("time after....")
		}
	}
}

func (w *WsServer) Start() (err error) {
	w.listener, err = net.Listen("tcp", w.addr)
	if err != nil {
		fmt.Println("net listen error:", err)
		return
	}

	err = http.Serve(w.listener, w)
	if err != nil {
		fmt.Println("http serve error:", err)
		return
	}

	return nil
}

 

 

 

 


 

 

Guess you like

Origin blog.csdn.net/qq8864/article/details/112583300