golang开发 gorilla websocket的使用

2024-05-31 18:20

本文主要是介绍golang开发 gorilla websocket的使用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

websocket的简单使用

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

把websocket/examples/echo下面的 client.go server.go 现在下来拷贝到项目里面。
https://github.com/gorilla/websocket/blob/v1.3.0/examples/echo/server.go
https://github.com/gorilla/websocket/blob/v1.3.0/examples/echo/client.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:returncase 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:returncase <-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/;}
}

这篇关于golang开发 gorilla websocket的使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1018691

相关文章

Qt spdlog日志模块的使用详解

《Qtspdlog日志模块的使用详解》在Qt应用程序开发中,良好的日志系统至关重要,本文将介绍如何使用spdlog1.5.0创建满足以下要求的日志系统,感兴趣的朋友一起看看吧... 目录版本摘要例子logmanager.cpp文件main.cpp文件版本spdlog版本:1.5.0采用1.5.0版本主要

Java中使用Hutool进行AES加密解密的方法举例

《Java中使用Hutool进行AES加密解密的方法举例》AES是一种对称加密,所谓对称加密就是加密与解密使用的秘钥是一个,下面:本文主要介绍Java中使用Hutool进行AES加密解密的相关资料... 目录前言一、Hutool简介与引入1.1 Hutool简介1.2 引入Hutool二、AES加密解密基础

使用Python将JSON,XML和YAML数据写入Excel文件

《使用Python将JSON,XML和YAML数据写入Excel文件》JSON、XML和YAML作为主流结构化数据格式,因其层次化表达能力和跨平台兼容性,已成为系统间数据交换的通用载体,本文将介绍如何... 目录如何使用python写入数据到Excel工作表用Python导入jsON数据到Excel工作表用

鸿蒙中@State的原理使用详解(HarmonyOS 5)

《鸿蒙中@State的原理使用详解(HarmonyOS5)》@State是HarmonyOSArkTS框架中用于管理组件状态的核心装饰器,其核心作用是实现数据驱动UI的响应式编程模式,本文给大家介绍... 目录一、@State在鸿蒙中是做什么的?二、@Spythontate的基本原理1. 依赖关系的收集2.

Python基础语法中defaultdict的使用小结

《Python基础语法中defaultdict的使用小结》Python的defaultdict是collections模块中提供的一种特殊的字典类型,它与普通的字典(dict)有着相似的功能,本文主要... 目录示例1示例2python的defaultdict是collections模块中提供的一种特殊的字

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

Java String字符串的常用使用方法

《JavaString字符串的常用使用方法》String是JDK提供的一个类,是引用类型,并不是基本的数据类型,String用于字符串操作,在之前学习c语言的时候,对于一些字符串,会初始化字符数组表... 目录一、什么是String二、如何定义一个String1. 用双引号定义2. 通过构造函数定义三、St

golang获取当前时间、时间戳和时间字符串及它们之间的相互转换方法

《golang获取当前时间、时间戳和时间字符串及它们之间的相互转换方法》:本文主要介绍golang获取当前时间、时间戳和时间字符串及它们之间的相互转换,本文通过实例代码给大家介绍的非常详细,感兴趣... 目录1、获取当前时间2、获取当前时间戳3、获取当前时间的字符串格式4、它们之间的相互转化上篇文章给大家介

Python通过模块化开发优化代码的技巧分享

《Python通过模块化开发优化代码的技巧分享》模块化开发就是把代码拆成一个个“零件”,该封装封装,该拆分拆分,下面小编就来和大家简单聊聊python如何用模块化开发进行代码优化吧... 目录什么是模块化开发如何拆分代码改进版:拆分成模块让模块更强大:使用 __init__.py你一定会遇到的问题模www.

Pydantic中Optional 和Union类型的使用

《Pydantic中Optional和Union类型的使用》本文主要介绍了Pydantic中Optional和Union类型的使用,这两者在处理可选字段和多类型字段时尤为重要,文中通过示例代码介绍的... 目录简介Optional 类型Union 类型Optional 和 Union 的组合总结简介Pyd