前言

WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket让客户端和服务端之间的数据交换变得非常简单,且允许服务器主动向客户端推送数据,并且之后客户端和服务端所有的通信都依靠这个专用协议进行。

本文使用gin框架编写服务端应用,配置路由接收websocket请求并处理。同时实现一个websocket命令行客户端用于与服务端通信。

服务端

下面代码示例中,使用gin创建一个应用,并将自定义函数WebSocketHandler()注册到/ws路由。WebSocketHandler()功能非常简单,客户端发送什么就原样返回什么。

package main

import (
"fmt"
"time" "github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
) func WebSocketHandler(c *gin.Context) {
// 获取WebSocket连接
wsUpgrader := websocket.Upgrader{
HandshakeTimeout: time.Second * 10,
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
ws, err := wsUpgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
fmt.Println(err)
return
}
defer ws.Close() // 处理WebSocket消息
for {
messageType, p, err := ws.ReadMessage()
if err != nil {
fmt.Println(err)
return
}
switch messageType {
case websocket.TextMessage:
fmt.Printf("处理文本消息, %s\n", string(p))
ws.WriteMessage(websocket.TextMessage, p)
// c.Writer.Write(p)
case websocket.BinaryMessage:
fmt.Println("处理二进制消息")
case websocket.CloseMessage:
fmt.Println("关闭websocket连接")
return
case websocket.PingMessage:
fmt.Println("处理ping消息")
ws.WriteMessage(websocket.PongMessage, []byte("ping"))
case websocket.PongMessage:
fmt.Println("处理pong消息")
ws.WriteMessage(websocket.PongMessage, []byte("pong"))
default:
fmt.Printf("未知消息类型: %d\n", messageType)
return
}
} } func NewServer() *gin.Engine {
gin.SetMode(gin.DebugMode) // 设置运行模式
gin.DisableConsoleColor() // 禁用控制台输出的颜色
router := gin.Default()
return router
} func main() {
// 创建Gin应用
app := NewServer() // 注册WebSocket路由
app.GET("/ws", WebSocketHandler) // 启动应用
err := app.Run("127.0.0.1:8080")
if err != nil {
panic(err)
}
}

客户端

写个了命令行客户端用于连接websocket服务端,接收键盘输入,然后发送到服务端。使用flag解析命令行参数用于配置服务端连接。

package main

import (
"flag"
"fmt"
"log"
"net/http"
"net/url"
"os"
"os/signal"
"syscall"
"time" "github.com/gorilla/websocket"
) var (
Addr string
Path string
Token string
) func init() {
flag.StringVar(&Addr, "addr", "localhost:8080", "WebSocket 服务器地址")
flag.StringVar(&Path, "path", "/ws", "WebSocket接口路由")
flag.StringVar(&Token, "token", "123456", "连接 WebSocket 服务器的令牌")
flag.Parse()
} func main() {
header := make(http.Header)
header.Set("token", Token)
u := url.URL{Scheme: "ws", Host: Addr, Path: "/ws"}
conn, _, err := websocket.DefaultDialer.Dial(u.String(), header)
if err != nil {
log.Fatalf("连接 WebSocket 服务器失败:%v", err)
return
}
defer conn.Close() // 创建channel用于监听操作系统的中断信号
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM, syscall.SIGINT) response := make(chan string, 8)
defer close(response)
// 启动一个 goroutine 用于接收 WebSocket 服务器的响应
go func(resp chan string) {
for {
_, message, err := conn.ReadMessage()
if err != nil {
log.Printf("server> ERROR! %v\n", err)
return
}
resp <- string(message)
}
}(response) // 读取用户的键盘输入,并发送到 WebSocket 服务器
for {
select {
case <-interrupt: // 等待中断信号
log.Println("收到中断信号,关闭 WebSocket 连接 ...")
err := conn.WriteMessage(
websocket.CloseMessage,
websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil {
log.Printf("发送关闭消息失败:%v\n", err)
}
<-interrupt // 关闭websocket连接之前, 确保已经发送到服务端的消息能够被确认和处理
return
default:
var input string
fmt.Printf("%s client> ", time.Now().Format("2006-01-02 15:04:05"))
fmt.Scanln(&input)
if input == "exit" {
log.Println("用户输入 exit, 关闭 WebSocket 连接 ...")
err := conn.WriteMessage(
websocket.CloseMessage,
websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil {
log.Printf("发送关闭消息失败:%v", err)
return
}
return
}
if len(input) == 0 {
log.Println("输入消息为空")
continue
}
err := conn.WriteMessage(websocket.TextMessage, []byte(input))
if err != nil {
log.Printf("发送消息失败:%v", err)
continue
}
// 阻塞等待服务端响应
resp := <-response
log.Printf("server> %s\n", resp)
}
}
}

参考

[golang]gin框架接收websocket通信的更多相关文章

  1. golang(gin框架),基于RESTFUL的跨语言远程通信尝试

    golang(gin框架),基于RESTFUL的跨语言远程通信尝试 背景: 在今年的项目实训过程中,遇到了这样的问题: 企业老师讲课实用的技术栈是Java springboot. 实训实际给我们讲课以 ...

  2. 前端程序员学习 Golang gin 框架实战笔记之一开始玩 gin

    原文链接 我是一名五六年经验的前端程序员,现在准备学习一下 Golang 的后端框架 gin. 以下是我的学习实战经验,记录下来,供大家参考. https://github.com/gin-gonic ...

  3. golang Gin framework with websocket

    概述 golang websocket 库 示例 后端 前端 结论 概述 对于 golang 的 web 开发, 之前写过 2 篇 blog, 分别介绍了: 在 Gin 框架下, 各类 http AP ...

  4. golang gin框架中实现一个简单的不是特别精确的秒级限流器

    起因 看了两篇关于golang中限流器的帖子: Gin 开发实践:如何实现限流中间件 常用限流策略--漏桶与令牌桶介绍 我照着用,居然没效果-- 时间有限没有深究.这实在是一个很简单的功能,我的需求是 ...

  5. Golang gin框架学习

    今天开始学习gin框架,在Github上找的示例的go-gin-example, 进度 日期 进展 疑惑 进展 1.30 下拉代码,初步了解gin的介绍.搭建 .mod文件 module原理.使用方法 ...

  6. golang gin框架中实现大文件的流式上传

    一般来说,通过c.Request.FormFile()获取文件的时候,所有内容都全部读到了内存.如果是个巨大的文件,则可能内存会爆掉:且,有的时候我们需要一边上传一边处理. 以下的代码实现了大文件流式 ...

  7. [Golang] Gin框架学习笔记

    0x0 Gin简介 1.Gin 是什么? Gin 是一个用 Go (Golang) 编写的 HTTP web 框架. 它是一个类似于 martini 但拥有更好性能的 API 框架, 由于 httpr ...

  8. golang gin框架 使用swagger生成api文档

    github地址:https://github.com/swaggo/gin-swagger 1.下载swag $ go get -u github.com/swaggo/swag/cmd/swag ...

  9. golang gin框架中实现"Transfer-Encoding: chunked"方式的分块发送数据到浏览器端

    参考了这篇帖子: https://golangtc.com/t/570b403eb09ecc66b90002d9 golang web如何发送小包的chunked数据 以下是代码: r.GET(&qu ...

  10. golang gin框架设置静态目录

    router := gin.Default() 第一个参数是api 第二个静态问价的文件夹相对目录 router.StaticFS("/data", http.Dir(" ...

随机推荐

  1. 【Azure 媒体服务】Media Service的编码示例 -- 创建缩略图子画面的.NET代码调试问题

    问题描述 在中国区Azure上,使用Media Service服务,想要使用.NET的代码来对上传视频创建缩略图(Thumbnail) . 通过官网文档(https://docs.azure.cn/z ...

  2. DVWA文件上传

    Low安全级别: <?php @eval($_POST['cmd']);?> 文本编辑器写入一句话木马,保存并改为php文件. 上传 成功上传,并返回路径 打开菜刀客户端连接 成功连接 反 ...

  3. 在chatGPT的帮助下成功从Rancher中删除无效的集群

    只要你坚持,不放弃,问题总有解决的一天! 与chatgpt进行了几次沟通,成功解决历史遗留问题,成功从rancher中删除了无效的集群 chatGPT回答1 如果您在 Rancher UI 中无法删除 ...

  4. Grafana系列-统一展示-9-Jaeger数据源

    系列文章 Grafana 系列文章 配置 Jaeger data source Grafana内置了对Jaeger的支持,它提供了开源的端到端分布式跟踪.本文解释了针对Jaeger数据源的配置和查询. ...

  5. Rust如何引入源码作为依赖

    问题描述 通常我们在rust项目中引入第三方依赖包时,会直接指定包的版本,这种方式指定后,Cargo在编译时会从crates.io这个源中下载这些依赖包. [package] name = " ...

  6. Django4全栈进阶之路3 apps.py 文件

    在 Django 4 中,每个应用(app)都需要定义一个 apps.py 文件,用于配置应用的基本信息,如应用的名称.显示名称.图标.默认路径.启动时需要执行的操作等.apps.py 文件是一个 P ...

  7. vue全家桶进阶之路13:生命周期

    Vue2的生命周期是指Vue实例从创建.挂载.更新.销毁等各个阶段中所经历的一系列过程.Vue2的生命周期共有8个阶段,分别是: beforeCreate:Vue实例被创建之前的阶段,此时Vue实例的 ...

  8. 2015年蓝桥杯C/C++大学B组省赛真题(加法变乘法)

    题目描述: 我们都知道:1+2+3+ ... + 49 = 1225 现在要求你把其中两个不相邻的加号变成乘号,使得结果为2015 比如: 1+2+3+...+10*11+12+...+27*28+2 ...

  9. 2019年蓝桥杯C/C++大学B组省赛真题(特别数的和)

    题目描述: 小明对数位中含有2.0.1.9 的数字很感兴趣(不包括前导0) 在1到40中这样的数包括1.2.9.10 至32.39 和40,共28 个,他们的和是574. 请问,在1到n 中,所有这样 ...

  10. cv学习总结(SVM,softmax)10.24-10.30

    本周完成了SVM课程笔记的阅读,包括SVM的基本原理以及SVM的优化过程,以及实现了SVM的两种损失函数(svm以及softmax)的线性分类器,以及学习了反向传播以及神经网络的初步.其中:svm在测 ...