1.使用前必须在 config/config.yml文件 websocket部分配置随项目启动。
2.控制器位置:app\http\controller\websocket\ws.go
3.事件监听、处理位置:app\service\websocket\ws.go,查看详情

JS 客户端代码

js 客户端需要开发者根据实际情况编写掉线重连逻辑,但是不需要做心跳功能,尾部处介绍为什么不需要心跳。

  1. ## websocket js 客户端
  2. ### 前言
  3. > ws地址: ws://127.0.0.1:20201/admin/ws?token=sdsdsdsdsdsdsdsdsdsdsdsdssdsd
  4. > 由于中间模拟校验了token参数,请自行随意提交超过20个字符
  5. > 以下代码保存为 `ws.html` 在浏览器直接访问即可连接服务端
  6. > ws服务默认未开启,请自行在配置文件 config/config.yml ,找到 websocket 选项,开启即可.
  7. ```html
  8. <!DOCTYPE html>
  9. <html lang="en">
  10. <head>
  11. <meta charset="UTF-8">
  12. <title>websocket client</title>
  13.  <script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
  14. </head>
  15. <body>
  16. <div>
  17. <h3>websocket client 测试代码</h3>
  18. <label>发送消息:</label>
  19. <br/>
  20. <textarea id="sendmsg">
  21. </textarea>
  22. <hr/>
  23. <label>接受到的消息 :</label>
  24. <br/>
  25. <textarea id="receivedmsg">
  26. </textarea>
  27. <button name="btn1" onclick="send_msg()">发送</button>
  28. </div>
  29. <script>
  30. var wsServer_ip = 'ws://127.0.0.1:20201/admin/ws?token=sdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsd';
  31. var websocket = new WebSocket(wsServer_ip);
  32. websocket.onopen = function (evt) {
  33. console.log("Connected to WebSocket server.");
  34. console.log(evt)
  35. };
  36. websocket.onmessage = function (evt) {
  37. console.log(evt)
  38. console.log('收到 data from server: ' + evt.data);
  39. $("#receivedmsg").text(evt.data)
  40. };
  41. websocket.onclose = function (evt) {
  42. console.log("与服务器连接断开");
  43. console.log(evt);
  44. alert("close事件发生")
  45. };
  46. websocket.onerror = function (evt, e) {
  47. console.log(e);
  48. console.log('Error occured: ' + evt.data);
  49. alert("error事件发生")
  50. };
  51. function send_msg() {
  52. //发送消息
  53. $user_send_msg=$("#sendmsg").val()
  54. console.log("发送消息:"+$user_send_msg)
  55. websocket.send($user_send_msg)
  56. }
  57. </script>
  58. </body>
  59. </html>
  1. <a name="zk7aL"></a>
  2. ### 服务端代码
  3. <a name="pNiNo"></a>
  4. #### 1.基本用法
  5. [在线代码预览](https://gitee.com/daitougege/GinSkeleton/blob/master/app/service/websocket/ws.go)
  6. ```code
  7. package websocket
  8. import (
  9. "fmt"
  10. "github.com/gin-gonic/gin"
  11. "github.com/gorilla/websocket"
  12. "go.uber.org/zap"
  13. "goskeleton/app/global/my_errors"
  14. "goskeleton/app/global/variable"
  15. "goskeleton/app/utils/websocket/core"
  16. )
  17. /**
  18. websocket模块相关事件执行顺序:
  19. 1.onOpen
  20. 2.OnMessage
  21. 3.OnError
  22. 4.OnClose
  23. */
  24. type Ws struct {
  25. WsClient *core.Client
  26. }
  27. // onOpen 基本不需要做什么
  28. func (w *Ws) OnOpen(context *gin.Context) (*Ws, bool) {
  29. if client, ok := (&core.Client{}).OnOpen(context); ok {
  30. w.WsClient = client
  31. go w.WsClient.Heartbeat() // 一旦握手+协议升级成功,就为每一个连接开启一个自动化的隐式心跳检测包
  32. return w, true
  33. } else {
  34. return nil, false
  35. }
  36. }
  37. // OnMessage 处理业务消息
  38. func (w *Ws) OnMessage(context *gin.Context) {
  39. go w.WsClient.ReadPump(func(messageType int, receivedData []byte) {
  40. //参数说明
  41. //messageType 消息类型,1=文本
  42. //receivedData 服务器接收到客户端(例如js客户端)发来的的数据,[]byte 格式
  43. tempMsg := "服务器已经收到了你的消息==>" + string(receivedData)
  44. // 回复客户端已经收到消息;
  45. if err := w.WsClient.SendMessage(messageType, tempMsg); err != nil {
  46. variable.ZapLog.Error("消息发送出现错误", zap.Error(err))
  47. }
  48. }, w.OnError, w.OnClose)
  49. }
  50. // OnError 客户端与服务端在消息交互过程中发生错误回调函数
  51. func (w *Ws) OnError(err error) {
  52. w.WsClient.State = 0 // 发生错误,状态设置为0, 心跳检测协程则自动退出
  53. variable.ZapLog.Error("远端掉线、卡死、刷新浏览器等会触发该错误:", zap.Error(err))
  54. //fmt.Printf("远端掉线、卡死、刷新浏览器等会触发该错误: %v\n", err.Error())
  55. }
  56. // OnClose 客户端关闭回调,发生onError回调以后会继续回调该函数
  57. func (w *Ws) OnClose() {
  58. w.WsClient.Hub.UnRegister <- w.WsClient // 向hub管道投递一条注销消息,由hub中心负责关闭连接、删除在线数据
  59. }
  60. //获取在线的全部客户端
  61. func (w *Ws) GetOnlineClients() {
  62. fmt.Printf("在线客户端数量:%d\n", len(w.WsClient.Hub.Clients))
  63. }
  64. // (每一个客户端都有能力)向全部在线客户端广播消息
  65. func (w *Ws) BroadcastMsg(sendMsg string) {
  66. for onlineClient := range w.WsClient.Hub.Clients {
  67. //获取每一个在线的客户端,向远端发送消息
  68. if err := onlineClient.SendMessage(websocket.TextMessage, sendMsg); err != nil {
  69. variable.ZapLog.Error(my_errors.ErrorsWebsocketWriteMgsFail, zap.Error(err))
  70. }
  71. }
  72. }

2.在本项目骨架任意位置,向所有在线的 websocet 客户端广播消息

核心原理:每一个 websocket 客户端都有一个 Hub 结构体,而这个结构体是本项目骨架设置的全局值,因此在任意位置创建一个 websocket 客户端,只要将 Hub 值赋予全局初始化的:variable.WebsocketHub,就可以在任意位置进行广播消息.

  1. package demo1
  2. import (
  3. serviceWs "goskeleton/app/service/websocket"
  4. )
  5. // 省略其他无关代码,相关的核心代码如下
  6. if WsHub, ok := variable.WebsocketHub.(*core.Hub); ok {
  7. // serviceWs 为 app/service/websocket 的别名
  8. ws := serviceWs.Ws{WsClient: &core.Client{Hub: WsHub}}
  9. ws.BroadcastMsg("本项目骨架任意位置,使用本段代码对在线的 ws 客户端广播消息")
  10. }

websocket 其他说明

1.我们严格按照 websocket 协议封装了服务端,如果 客户端 是 js 创建的,那么您不需要考虑心跳包,服务端会自动发包,js 客户端会自动响应。
2.关于隐式自动维护心跳抓包图,其中Server_ping 为服务器端向浏览器发送的ping格式数据包,F12 不可见,只有抓包可见。

websocket - 图1