Golang长连接-基于WebSocket

Websocket简介

WebSocket可以实现客户端与服务器间双向、基于消息的文本或二进制数据传输。它是浏览器中最靠近套接字的API。但WebSocket连接远远不是一个网络套接字,因为浏览器在这个简单的API之后隐藏了所有的复杂性,而且还提供了更多服务:

  • 连接协商和同源策略;
  • 与既有HTTP基础设施的互操作;
  • 基于消息的通信和高效消息分帧;
  • 子协议协商及可扩展能力。

WebSocket资源URL采用了自定义模式:ws表示纯文本通信(如ws://example.com/socket),wss表示使用加密信道通信(TCP+TLS)。

长连接实现

本例用gin框架,引入github.com/gorilla/websocket包,项目源码可到https://github.com/shidawuhen/asap/tree/feature_pzq_longconnect查看

服务端核心代码:

  • ping函数,将请求升级为websocket。
  • 只有Get请求才能进行升级,具体原因可以查看websocket源码
  • 当客户端请求ping时,便建立了长连接,服务端读取客户端数据,如果数据为ping,则返回pong,如果不为ping,则把输入的内容返回。
package main

import (
   "net/http"

   "github.com/gin-gonic/gin"
   "github.com/gorilla/websocket"
)

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

func setupRouter() *gin.Engine {
    
    
   r := gin.Default()
   r.LoadHTMLGlob("templates/*")
   // Ping test
   r.GET("/ping", ping)
   r.GET("/longconnecthtml",longconnecthtml)

   return r
}

func longconnecthtml(c *gin.Context)  {
    
    
   c.HTML(http.StatusOK, "longconnect.tmpl",gin.H{
    
    })
}

func ping(c *gin.Context) {
    
    
   //c.String(http.StatusOK, "ok")
   //升级get请求为webSocket协议
   ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
   if err != nil {
    
    
      return
   }
   defer ws.Close()
   for {
    
    
      //读取ws中的数据
      mt, message, err := ws.ReadMessage()
      if err != nil {
    
    
         break
      }
      if string(message) == "ping" {
    
    
         message = []byte("pong")
      }
      //写入ws数据
      err = ws.WriteMessage(mt, message)
      if err != nil {
    
    
         break
      }

   }
}

func main() {
    
    
   r := setupRouter()
   // Listen and Server in 0.0.0.0:8080
   r.Run(":9090")
}

客户端核心代码:

  1. 要使用websocket,客户端必须发起websocket请求
  2. websocket的建立和使用也很方便,主要涉及
    • new WebSocket:创建websocket对象
    • onopen:连接建立时触发
    • onmessage:客户端接收服务端数据时触发
    • onerror:通信发生错误时触发
    • onclose:连接关闭时触发

该代码块的主要功能是和服务端建立连接,在文本框输入信息,将信息发送给服务端,并将服务端返回内容显示出来

<!DOCTYPE HTML>
<html>
   <head>
   <meta charset="utf-8">
   <title>长连接测试</title>
   <textarea id="inp_send" class="form-control" style="height:100px;" placeholder="发送的内容"></textarea>
   <button type="button" id="btn_send" class="btn btn-info" onclick="fun_sendto();">发送(ctrl+回车)</button>
<script type="text/javascript" src="https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/js/lib/jquery-1-edb203c114.10.2.js"></script>
      <script type="text/javascript">
        var ws = new WebSocket("ws://localhost:9090/ping");
        //连接打开时触发
        ws.onopen = function(evt) {
     
     
            console.log("Connection open ...");
            ws.send("Hello WebSockets!");
        };
        //接收到消息时触发
        ws.onmessage = function(evt) {
     
     
            console.log("Received Message: " + evt.data);
        };
        //连接关闭时触发
        ws.onclose = function(evt) {
     
     
            console.log("Connection closed.");
        };

        function fun_sendto(){
     
     
            var content = $("#inp_send").val();
            ws.send(content);
        }
      </script>

   </head>
   <body>
   </body>
</html>

服务端运行起来之后,请求http://localhost:9090/longconnecthtml即可查看效果

长连接展示

  1. 浏览器,请求ping接口,便可看到该请求为websocket请求,而且为pending状态

  2. 也可以查看到在发起请求时,request header中有很多新的参数,具体含义大家可以看我提供的参考资料

  3. console中显示的是服务端推送到客户端的数据

在这里插入图片描述
在这里插入图片描述

注意事项

  1. 长连接可以实现双向通信,但是以占用连接为前提的,如果请求量较大,需要考虑资源问题
  2. 服务部署情况对效果影响很大。例如,如果只部署北京机房供全国使用,当服务端向客户端推送数据时,不同地区可能会有很大延迟。

参考资料

  1. golang长连接和短连接的学习
  2. https://www.zhihu.com/question/22925358
  3. Golang-长连接-状态推送
  4. Golang实现单机百万长连接服务 - 美图的三年优化经验
  5. golang 长连接web socket原理
  6. golang Gin建立长连接web socket
  7. HTML5 WebSocket

最后

大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)


CDN请求过程详解

关于程序员职业发展的思考

记博客服务被压垮的历程

常用缓存技巧

如何高效对接第三方支付

Gin框架简洁版

关于代码review的思考

猜你喜欢

转载自blog.csdn.net/shida219/article/details/106762944