给Go的Gin web框架增加 WebSocket 功能,让WebSocket 更好用

简介: 给Go的Gin web框架增加 WebSocket 功能,让WebSocket 更好用

Gin 是一个 go 的 web 框架,它具有轻量级,高性能,运行速度快,分组的路由器,良好的崩溃捕获和错误处理,非常好的支持中间件,rest api和json。


总之在 Go语言开发领域是一款值得好好研究的 Web 框架。官方地址:https://github.com/gin-gonic/gin


但是最近想尝试下websocket功能,发现Gin框架里没有。


Go 官方没有提供对 WebSocket 的支持,必须选择第三方提供的包。


常用的有两种,golang.org/x/net/websocket和 https://github.com/gorilla/websocket


《Go Web 编程》一书中的例子使用的是golang.org/x/net/websocket


其中gorilla/websocket更常用些,Apache的Paho GO mqtt client库中和go的另外一个web框架iris中,就使用的是gorilla/websocket库。


是不是直接把gorilla/websocket引入进gin框架就可以了?


但是还得需要自己完成封装,若不花功夫封装好,不是很好用,稳定性更难保证。


网上找到gin中使用gorilla的websocket库的例子,都只是一些简单的测试,一点儿都不好用。


接下来,为了让websocket在gin中更好用些,移植iris框架中的websocket功能到gin框架中,使用起来就简单啦,使用如下:


github 地址: 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())
  })
}


附,网上找到的一些测试代码如下:


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
}


相关文章
|
6月前
|
算法 Java Go
【GoGin】(1)上手Go Gin 基于Go语言开发的Web框架,本文介绍了各种路由的配置信息;包含各场景下请求参数的基本传入接收
gin 框架中采用的路优酷是基于httprouter做的是一个高性能的 HTTP 请求路由器,适用于 Go 语言。它的设计目标是提供高效的路由匹配和低内存占用,特别适合需要高性能和简单路由的应用场景。
539 4
|
9月前
|
人工智能 测试技术 Go
Go 语言的主流框架
本文全面解析了 Go 语言主流技术生态,涵盖 Web 框架、微服务、数据库工具、测试与部署等多个领域。重点介绍了 Gin、Echo、Beego 等高性能框架,以及 gRPC-Go、Go-Micro 等微服务组件。同时分析了 GORM、Ent 等 ORM 工具与测试部署方案,并结合场景提供选型建议,助力开发者构建高效稳定的 Go 应用。
2222 0
|
7月前
|
消息中间件 缓存 NoSQL
Redis各类数据结构详细介绍及其在Go语言Gin框架下实践应用
这只是利用Go语言和Gin框架与Redis交互最基础部分展示;根据具体业务需求可能需要更复杂查询、事务处理或订阅发布功能实现更多高级特性应用场景。
413 86
|
8月前
|
人工智能 监控 中间件
如何用go语言实现类似AOP的功能
本文介绍了如何在 Go 语言中借鉴 Java 的 AOP(面向切面编程)思想,通过 Gin 框架的中间件和函数包装机制实现日志记录、权限校验等横切关注点与业务逻辑的解耦。内容涵盖 AOP 的优点、Go 中的实现方式、Gin 中间件与 AOP 的异同,帮助开发者提升代码模块化与可维护性。
303 0
|
6月前
|
JavaScript 前端开发 Java
【GoWails】Go做桌面应用开发?本篇文章带你上手Wails框架!一步步带你玩明白前后端双端的数据绑定!
wails是一个可以让你使用Go和Web技术编写桌面应用的项目 可以将它看作Go的快并且轻量级的Electron替代品。可以使用Go的功能,并结合现代化UI完成桌面应用程序的开发
1218 6
|
6月前
|
开发框架 前端开发 Go
【GoGin】(0)基于Go的WEB开发框架,GO Gin是什么?怎么启动?本文给你答案
Gin:Go语言编写的Web框架,以更好的性能实现类似Martini框架的APInet/http、Beego:开源的高性能Go语言Web框架、Iris:最快的Go语言Web框架,完备的MVC支持。
562 1
|
10月前
|
开发框架 JSON 中间件
Go语言Web开发框架实践:路由、中间件、参数校验
Gin框架以其极简风格、强大路由管理、灵活中间件机制及参数绑定校验系统著称。本文详解其核心功能:1) 路由管理,支持分组与路径参数;2) 中间件机制,实现全局与局部控制;3) 参数绑定,涵盖多种来源;4) 结构体绑定与字段校验,确保数据合法性;5) 自定义校验器扩展功能;6) 统一错误处理提升用户体验。Gin以清晰模块化、流程可控及自动化校验等优势,成为开发者的优选工具。
|
10月前
|
开发框架 安全 前端开发
Go Web开发框架实践:模板渲染与静态资源服务
Gin 是一个功能强大的 Go Web 框架,不仅适用于构建 API 服务,还支持 HTML 模板渲染和静态资源托管。它可以帮助开发者快速搭建中小型网站,并提供灵活的模板语法、自定义函数、静态文件映射等功能,同时兼容 Go 的 html/template 引擎,具备高效且安全的页面渲染能力。
|
10月前
|
开发框架 JSON 中间件
Go语言Web开发框架实践:使用 Gin 快速构建 Web 服务
Gin 是一个高效、轻量级的 Go 语言 Web 框架,支持中间件机制,非常适合开发 RESTful API。本文从安装到进阶技巧全面解析 Gin 的使用:快速入门示例(Hello Gin)、定义 RESTful 用户服务(增删改查接口实现),以及推荐实践如参数校验、中间件和路由分组等。通过对比标准库 `net/http`,Gin 提供更简洁灵活的开发体验。此外,还推荐了 GORM、Viper、Zap 等配合使用的工具库,助力高效开发。
|
10月前
|
缓存 JavaScript 前端开发
鸿蒙5开发宝藏案例分享---Web开发优化案例分享
本文深入解读鸿蒙官方文档中的 `ArkWeb` 性能优化技巧,从预启动进程到预渲染,涵盖预下载、预连接、预取POST等八大优化策略。通过代码示例详解如何提升Web页面加载速度,助你打造流畅的HarmonyOS应用体验。内容实用,按需选用,让H5页面快到飞起!