golang开发 gorilla websocket的使用示例详解-kb88凯时官网登录

来自:网络
时间:2024-08-28
阅读:
免费资源网,https://freexyz.cn/

很多app都需要主动向用户推送消息,这就需要用到长连接的服务,即我们通常提到的websocket,同样也是使用socket服务,通信协议是基本类似的,在go中用的最多的、也是最简单的socket服务就是gorilla/websocket,它有21.1k的star,足以说明它的受欢迎程度, 它的github地址是 

websocket的简单使用

我们使用的版本是1.3.0,首先下载websocket
go get github.com/gorilla/websocket@v1.3.0

把websocket/examples/echo下面的 client.go server.go 现在下来拷贝到项目里面。

在一个终端执行 websocket 服务
go run server.go

recv: 2024-04-16 15:09:45.805438  0800 cst m= 1.007536889
recv: 2024-04-16 15:09:46.805425  0800 cst m= 2.007517605
recv: 2024-04-16 15:09:47.806274  0800 cst m= 3.008359325
recv: 2024-04-16 15:09:48.80495  0800 cst m= 4.007028866
recv: 2024-04-16 15:09:49.805743  0800 cst m= 5.007816108
recv: 2024-04-16 15:09:50.806087  0800 cst m= 6.008153310
recv: 2024-04-16 15:09:51.805348  0800 cst m= 7.007407266

再打开一个终端执行 go run client.go,充当客户端

connecting to ws://localhost:8080/echo
recv: 2024-04-16 15:09:45.805438  0800 cst m= 1.007536889
recv: 2024-04-16 15:09:46.805425  0800 cst m= 2.007517605
recv: 2024-04-16 15:09:47.806274  0800 cst m= 3.008359325
recv: 2024-04-16 15:09:48.80495  0800 cst m= 4.007028866
recv: 2024-04-16 15:09:49.805743  0800 cst m= 5.007816108
recv: 2024-04-16 15:09:50.806087  0800 cst m= 6.008153310
recv: 2024-04-16 15:09:51.805348  0800 cst m= 7.007407266

我们看看这个简单的例子。
client.go

go func() {
		defer close(done)
		for {
			_, message, err := c.readmessage()
			if err != nil {
				log.println("read:", err)
				return
			}
			log.printf("recv: %s", message)
		}
	}()
	ticker := time.newticker(time.second)
	defer ticker.stop()
	for {
		select {
		case <-done:
			return
		case t := <-ticker.c:
			err := c.writemessage(websocket.textmessage, []byte(t.string()))
			if err != nil {
				log.println("write:", err)
				return
			}
		case <-interrupt:
			log.println("interrupt")
			// cleanly close the connection by sending a close message and then
			// waiting (with timeout) for the server to close the connection.
			err := c.writemessage(websocket.closemessage, websocket.formatclosemessage(websocket.closenormalclosure, ""))
			if err != nil {
				log.println("write close:", err)
				return
			}
			select {
			case <-done:
			case <-time.after(time.second):
			}
			return
		}
	}

go func() 的 c.readmessage 不停的从socket里面读取数据并且打印。
for 循环不停的从1秒的定时器里面读取时间,写到socket

server.go

for {
		mt, message, err := c.readmessage()
		if err != nil {
			log.println("read:", err)
			break
		}
		log.printf("recv: %s", message)
		err = c.writemessage(mt, message)
		if err != nil {
			log.println("write:", err)
			break
		}
	}

for c.readmessage 循环不停的从 socket读取数据,把数据打印之后,又写给客户端。
这是大致的websocket 客户端与服务端通信的简单例子。

改成自定义的协议

我们使用二进制数据流,自定义的协议是这样的,
先是无符号的 uint 占4个字节,表示行为逻辑,比如111-获取信息, 110- 加好友等等
后面是具体的数据,跟http请求的get或者post参数类似,具体数据格式定义好就行,比如可以使用json数据、可以使用rpc定义好的数据格式

修改完之后 client.go 代码是这样的

for {
		select {
		case <-done:
			return
		case <-ticker.c:
			buf := bytes.newbuffer([]byte{})
			binary.write(buf, binary.bigendian, uint32(110))
			binary.write(buf, binary.bigendian, []byte("我们都好"))
			err := c.writemessage(websocket.binarymessage, buf.bytes())
			if err != nil {
				log.println("write:", err)
				return
			}
		case <-interrupt:
			log.println("interrupt")
			// cleanly close the connection by sending a close message and then
			// waiting (with timeout) for the server to close the connection.
			err := c.writemessage(websocket.closemessage, websocket.formatclosemessage(websocket.closenormalclosure, ""))
			if err != nil {
				log.println("write close:", err)
				return
			}
			select {
			case <-done:
			case <-time.after(time.second):
			}
			return
		}
	}

主要是这个

buf := bytes.newbuffer([]byte{})
binary.write(buf, binary.bigendian, uint32(110))
binary.write(buf, binary.bigendian, []byte("我们都好"))

创建一个bytes buffer
写入一个uint32,占4个字节,表示行为逻辑
写入行为逻辑具体的数据
以上采用大端通信

修改之后的 server.go

for {
		mt, message, err := c.readmessage()
		if err != nil {
			log.println("read:", err)
			break
		}
		log.println("messagetype:%d", mt)
		log.println("messagelen:%d", len(message))
		log.println("messagetype:%d", binary.bigendian.uint32(message[0:4]))
		log.println("recv: %s", string(message[4:]))
		err = c.writemessage(mt, message)
		if err != nil {
			log.println("write:", err)
			break
		}
	}

mt websocket 类型
textmessage = 1 文本类型传传输
binarymessage = 2 字节类型传输

从socket里面获取到message之后
先取前4个字节,表示逻辑行为
再取剩余的字节,表示行为逻辑需要的参数

我们重新执行一下,server.go 和 client.go

go run client.go
connecting to ws://localhost:8080/echo
recv: n我们都好
recv: n我们都好
go run server.go
messagetype:%d 2
messagelen:%d 16
messagetype:%d 110
recv: %s 我们都好

messagetype=2表示二进制通信
message长度是16,前四个字节是无符号整形,四个汉字占12个字节,一共16个字节,无符号uint是110表示加好友。

这就是一个简单自定义协议的socket服务。

如果有需要登录鉴权的话,在server.go中可以从r *http.request 获取header或者cookie,进行登录鉴权验证。

生产的部署

因为是 websocket 协议,如果有代理服务器的话,需要在各代理服务器上的nginx做协议升级,将http 升级为 websocket ,比如下面的代理服务器。

map $http_upgrade $connection_upgrade {
default upgrade;
''      close;
}
server {
    listen 80;
    location /serv/ {
        proxy_set_header upgrade $http_upgrade;
        proxy_set_header connection $connection_upgrade;
        proxy_connect_timeout 3;
        proxy_redirect     off;
        proxy_read_timeout 3600;
        proxy_send_timeout 3600;
        proxy_pass http://push_web/serv/;
    }
}
免费资源网,https://freexyz.cn/
返回顶部
顶部
网站地图