Go 实现WebSocket,初步实操

什么是长连接和短连接?

在HTTP/1.0中默认使用短连接。也就是说,客户端和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。当客户端浏览器访问的某个HTML或其他类型的Web页中包含有其他的Web资源(如JavaScript文件、图像文件、CSS文件等),每遇到这样一个Web资源,浏览器就会重新建立一个HTTP会话。

而从HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头加入这行代码:

Connection:keep-alive

在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现长连接需要客户端和服务端都支持长连接。

HTTP协议的长连接和短连接,实质上是TCP协议的长连接和短连接。


Go WebSocket

今天我们会用Go语言使用WebSocket,经过多方收集资料,整理了后端的服务端+客户端,然后服务端和前端的链接两个示例,下面是代码,供大家研究。

今天我们用的是Gin框架实现一个长链接。


1、服务端+客户端(后端)

服务端:

package main

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

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

var upgrader = websocket.Upgrader{}



func main() {
	// 使用gin框架,和普通的http协议的服务器没什么不一样
	s := gin.Default()
	s.GET("/sendMsg", echo)
	_ = s.Run("127.0.0.1:8090")
}

func echo(c *gin.Context) {
	//服务升级,对于来到的http连接进行服务升级,升级到ws
	cn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
	defer cn.Close()
	if err != nil {
		panic(err)
	}
	for {
		mt, message, err := cn.ReadMessage()
		fmt.Println("接受到消息", message)
		if err != nil {
			log.Println("server read:", err)
			break
		}
		log.Printf("server recv msg: %s", message)
		msg := string(message)
		fmt.Println(msg, "msg")
		if msg == "clent" {
		    message = []byte("客户端来了")
		}
		err = cn.WriteMessage(mt, message)






		if err != nil {
			log.Println(" server write err:", err)
			break
		}
	}
}

客户端:

package main

import (
	"log"
	"net/url"
	"os"
	"os/signal"
	"time"

	"github.com/gorilla/websocket"
)

func main() {

	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt)

	u := url.URL{Scheme: "ws", Host: "127.0.0.1:8090", Path: "/sendMsg"}
	log.Printf("client1 connecting to %s", u.String())

	c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
	if err != nil {
		log.Fatal("dial server:", err)
	}
	defer c.Close()

	done := make(chan struct{})

	go func() {
		defer close(done)
		for {
			_, message, err := c.ReadMessage()
			if err != nil {
				log.Println("client read err:", err)
				return
			}
			log.Printf("client recv msg: %s", message)
		}
	}()


	for {
		select {
		// if the goroutine is done , all are out
		case <-done:
			return
		case <-time.Tick(time.Second * 5):
			err := c.WriteMessage(websocket.TextMessage, []byte("clent"))
			if err != nil {
				log.Println("client write:", err)
				return
			}
		case <-interrupt:
			log.Println("client interrupt")


			err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
			if err != nil {
				log.Println("client1 write close:", err)
				return
			}
			select {
			case <-done:
			case <-time.After(time.Second):
			}
			return
		}
	}
}

分别启动服务端和客户端:服务端会根据客户端发来的消息回复

2、服务端+前端(前后端)

接下来,我们用前后端交互一下websocket

前端页面:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>websocket测试</title>
    <script src="http://www.alingfeng.cn/js/jquery.min.js"></script>
  </head>
  <body>
    <button id="clickButton">点击测试websocket</button>

    <script>
      $("#clickButton").click(function () {
        var name = $("#name").val();
        initWebpack(name);
      });
    </script>

    <script>
      var planWebsocket = null;
      var planIP = "127.0.0.1"; // IP地址
      var planPort = "8090"; // 端口号
      function initWebpack(clickName) {
        //初始化websocket
        if ("WebSocket" in window) {
          planWebsocket = new WebSocket(
            "ws://" + planIP + ":" + planPort + "/sendMsg"
          ); // 通信地址
          setInterval(
            (planWebsocket.onopen = function (event) {
              console.log("建立连接");
              let sendData = {
                command: "sendMsg",
                data: [{ msg: "服务端,我发送了消息,注意查收!" }],
              };
              planWebsocket.send(JSON.stringify(sendData)); // 发送获取数据的接口
            }),
            2000
          );

          planWebsocket.onmessage = function (event) {
            // console.log('收到消息:' + event.data)
            let data = JSON.parse(event.data);
            if (data.command == "sendMsg") {
              var planData = data.data; //返回的数据
              console.log(planData);
            } else if (data.command == "getscenes") {
              // 其他命令
            }
          };

          planWebsocket.onclose = function (event) {
            console.log("连接关闭");
          };

          planWebsocket.onerror = function () {
            alert("websocket通信发生错误!");
          };
        } else {
          alert("该浏览器不支持websocket!");
        }
      }

      // initWebpack(); //调用
    </script>
  </body>
</html>

服务:

package main

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

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

// var upgrader = websocket.Upgrader{}

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	// 解决跨域问题
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

func main() {
	// 使用gin框架,和普通的http协议的服务器没什么不一样
	s := gin.Default()
	s.GET("/sendMsg", echo)
	_ = s.Run("127.0.0.1:8090")
}

func echo(c *gin.Context) {
	//服务升级,对于来到的http连接进行服务升级,升级到ws
	cn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
	defer cn.Close()
	if err != nil {
		panic(err)
	}
	for {
		mt, message, err := cn.ReadMessage()
		fmt.Println("接受到消息", message)
		if err != nil {
			log.Println("server read:", err)
			break
		}
		log.Printf("server recv msg: %s", message)
		msg := string(message)
		fmt.Println(msg, "msg")
		// if msg == "clent1" {
		// 	message = []byte("客户端1来了")

		// } else if msg == "clent2" {
		// 	message = []byte("你好客户端2")
		// }

		message = []byte("客户端,我已接收")

		for i := 0; i < 100; i++ {
			time.Sleep(1000000000)
			err = cn.WriteMessage(mt, message)
		}

		if err != nil {
			log.Println(" server write err:", err)
			break
		}
	}
}

测试:


我们初步了解Go语言做websocket就可以了。

点赞8
点击评论0
收藏1
浏览 107
 

还没有评论,快来发表第一个评论吧

免责声明:凡在本网站出现的信息,均仅供参考。本网站将尽力确保所提供信息的准确性及可靠性,但不保证有关资料的准确性及可靠性,注册用户和一般页面游览者在使用前请进一步核实,并对任何自主决定的行为负责。本网站对有关资料所引致的错误、不确或遗漏,概不负任何法律责任(包括侵权责任、合同责任和其它责任)
*尊重作者,转载请注明出处!

创作内容

开启你的爱凌峰创作之旅

发布首篇内容,开通创作中心
快来成为爱凌峰创作者吧~

写文章

板块热门【Gin】