1. 项目地址:
    https://github.com/gorilla/websocket

  2. websocket 在线调试工具:
    https://www.lddgo.net/network/websocket

  3. 示例代码:

    1. 目录结构:

      |—— gorilla/   // 根目录
      ****|—— client/
      ********|—— main.go  // 客户端
      ****|—— server/
      ********|—— main.go  // 服务端
    2. server/main.go:

       package main
      
       import (
           "context"
           "fmt"
           "github.com/gin-gonic/gin"
           "github.com/gorilla/websocket"
           "net/http"
           "os"
           "os/signal"
           "syscall"
           "time"
       )
      
       const (
           Address = "localhost:8091"
       )
      
       func checkToken(token string) bool {
           return true
       }
      
       func Chat(ctx *gin.Context) {
           token := ctx.GetHeader("token")
           // 校验 token 是否合法
           islegal := checkToken(token)
      
           conn, err := (&websocket.Upgrader{
               CheckOrigin: func(r *http.Request) bool {
                   return islegal
               },
           }).Upgrade(ctx.Writer, ctx.Request, nil)
      
           if err != nil {
               fmt.Println("upgrade error:", err)
               return
           }
      
           // 发送消息
           go func() {
               for {
                   err := conn.WriteMessage(websocket.TextMessage, []byte("hello from server!"))
                   if err != nil {
                       fmt.Println(err.Error())
                       return
                   }
                   time.Sleep(3 * time.Second)
               }
           }()
           // 接收消息
           for {
               _, data, err := conn.ReadMessage()
               if err != nil {
                   fmt.Println(err.Error())
                   return
               }
               fmt.Println("message from client:", string(data))
           }
       }
      
       func main() {
           quit := make(chan os.Signal, 1) // 退出信号
           signal.Notify(quit, syscall.SIGKILL, syscall.SIGQUIT, syscall.SIGINT, syscall.SIGTERM)
           r := gin.Default()
           r.GET("/chat", Chat)
           srv := &http.Server{
               Addr:    Address,
               Handler: r,
           }
           go shutdown(quit, srv)
           fmt.Printf("server running at:%s \n", Address)
           if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
               panic(err)
           }
       }
      
       func shutdown(quit chan os.Signal, srv *http.Server) {
           <-quit
           ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
           defer cancel()
           fmt.Println("server shutdown...")
           if err := srv.Shutdown(ctx); err != nil {
               fmt.Println("shutdown error:", err)
           }
           os.Exit(0)
       }
    3. 启动 server/main.go(略)

    4. client/main.go:

       package main
      
       import (
           "fmt"
           "github.com/gorilla/websocket"
           "net/http"
           "os"
           "os/signal"
           "syscall"
           "time"
       )
      
       var msgChan = make(chan string)
      
       func wsClient() {
           quit := make(chan os.Signal, 1)
           signal.Notify(quit, syscall.SIGKILL, syscall.SIGQUIT, syscall.SIGINT, syscall.SIGTERM)
      
           token := ""
      
           socketUrl := "ws://localhost:8091" + "/chat"
           header := http.Header{}
           header.Add("token", token)
           conn, _, err := websocket.DefaultDialer.Dial(socketUrl, header)
           if err != nil {
               panic(err)
           }
           defer conn.Close()
      
           // 发送消息
           go func() {
               for {
                   select {
                   case msg := <-msgChan:
                       err := conn.WriteMessage(websocket.TextMessage, []byte(msg))
                       if err != nil {
                           fmt.Println("write error:", err)
                       }
                   case <-quit:
                       fmt.Println("control + c pressed!")
                       err := conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
                       if err != nil {
                           fmt.Println("send close message error:", err)
                           os.Exit(0)
                       }
                       if err := conn.Close(); err != nil {
                           fmt.Println("conn close error:", err)
                           os.Exit(0)
                       }
                       os.Exit(0)
                   }
               }
           }()
           // 接收消息
           for {
               _, msg, err := conn.ReadMessage()
               if err != nil {
                   fmt.Println("Error in receive:", err)
                   return
               }
               fmt.Println("message from server:", string(msg))
           }
       }
      
       func main() {
           go wsClient()
           // 将要发送到服务端的消息传递到消息管道
           for i := 0; i < 3; i++ {
               msgChan <- "hello from client!"
               time.Sleep(3 * time.Second)
           }
           select {}
       }
    5. 启动 client/main.go(略)

文档更新时间: 2024-04-20 10:57   作者:lee