panic: concurrent write to websocket connection【golang、websocket】

文章目录

    • 异常信息
    • 原由
        • 代码
        • 错误点
    • 解决办法

异常信息

panic: concurrent write to websocket connection

原由

golang 编写 websocket

go版本:1.19

使用了第三方框架: https://github.com/gorilla/websocket/tree/main

代码

server.go

// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.package mainimport ("flag""fmt""html/template""log""net/http""github.com/gorilla/websocket"
)var addr = flag.String("addr", "localhost:8080", "http service address")var upgrader = websocket.Upgrader{} // use default optionsfunc echo(w http.ResponseWriter, r *http.Request) {c, err := upgrader.Upgrade(w, r, nil)if err != nil {log.Print("upgrade:", err)return}defer c.Close()for {mt, message, err := c.ReadMessage()if err != nil {log.Println("read:", err)break}fmt.Println(string(message))fmt.Println(mt)//log.Printf("recv: %s, type: %s", message, websocket.FormatMessageType(mt))err = c.WriteMessage(mt, message)if err != nil {log.Println("write:", err)break}}
}func home(w http.ResponseWriter, r *http.Request) {homeTemplate.Execute(w, "ws://"+r.Host+"/echo")
}func main() {flag.Parse()log.SetFlags(0)http.HandleFunc("/echo", echo)http.HandleFunc("/", home)log.Fatal(http.ListenAndServe(*addr, nil))
}var homeTemplate = template.Must(template.New("").Parse(`
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>  
window.addEventListener("load", function(evt) {var output = document.getElementById("output");var input = document.getElementById("input");var ws;var print = function(message) {var d = document.createElement("div");d.textContent = message;output.appendChild(d);output.scroll(0, output.scrollHeight);};document.getElementById("open").onclick = function(evt) {if (ws) {return false;}ws = new WebSocket("{{.}}");ws.onopen = function(evt) {print("OPEN");}ws.onclose = function(evt) {print("CLOSE");ws = null;}ws.onmessage = function(evt) {print("RESPONSE: " + evt.data);}ws.onerror = function(evt) {print("ERROR: " + evt.data);}return false;};document.getElementById("send").onclick = function(evt) {if (!ws) {return false;}print("SEND: " + input.value);ws.send(input.value);return false;};document.getElementById("close").onclick = function(evt) {if (!ws) {return false;}ws.close();return false;};});
</script>
</head>
<body>
<table>
<tr><td valign="top" width="50%">
<p>Click "Open" to create a connection to the server, 
"Send" to send a message to the server and "Close" to close the connection. 
You can change the message and send multiple times.
<p>
<form>
<button id="open">Open</button>
<button id="close">Close</button>
<p><input id="input" type="text" value="Hello world!">
<button id="send">Send</button>
</form>
</td><td valign="top" width="50%">
<div id="output" style="max-height: 70vh;overflow-y: scroll;"></div>
</td></tr></table>
</body>
</html>
`))

client.go

// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.package mainimport ("flag""fmt""log""net/url""os""os/signal""time""github.com/gorilla/websocket"
)var addr = flag.String("addr", "localhost:8080", "http service address")func main() {flag.Parse()log.SetFlags(0)interrupt := make(chan os.Signal, 1)signal.Notify(interrupt, os.Interrupt)u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo"}log.Printf("connecting to %s", u.String())c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)if err != nil {log.Fatal("dial:", err)}defer c.Close()done := make(chan struct{})go func() {// 发送Ping帧,检查连接是否活跃for {if err := c.WriteMessage(websocket.PingMessage, []byte{}); err != nil {log.Println("Failed to send Ping: ", err)return}fmt.Println("Ping success")time.Sleep(10 * time.Second)}}()go func() {defer close(done)for {mt, message, err := c.ReadMessage()if err != nil {log.Println("read:", err)return}fmt.Println(mt)fmt.Println(string(message)) // 时间//log.Printf("recv: %s, type: %s", message, websocket.FormatMessageType(mt))}}()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}}
}
错误点

我希望在连接过程中,通信双方一直检测,也就使用了 PING,检测活跃。

	go func() {// 发送Ping帧,检查连接是否活跃for {if err := c.WriteMessage(websocket.PingMessage, []byte{}); err != nil {log.Println("Failed to send Ping: ", err)return}fmt.Println("Ping success")time.Sleep(10 * time.Second)}}()

出现了开始的错误信息:panic: concurrent write to websocket connection,错误信息说:不能并发的给 socket 发消息。

在这里插入图片描述

错误 “concurrent write to websocket connection” 指的是有多个goroutine尝试同时向同一个WebSocket连接写入数据,这是不被允许的。gorilla/websocket 库并不是为并发写操作设计的,因此你需要确保对每个WebSocket连接的写操作在任何时候只由一个goroutine执行。

解决这个问题的方法是使用同步机制,比如互斥锁(sync.Mutex),来同步对WebSocket连接的写操作。下面是一个修改后的示例,展示如何使用互斥锁来避免并发写的问题:

解决办法

在这个示例中,我们定义了一个WebSocketConnection结构体,它包含一个websocket.Conn和一个sync.Mutex。在发送Ping消息的goroutine中,我们在写操作之前获取互斥锁,并在写操作完成后释放锁。这样可以确保在任何时候只有一个goroutine能够执行写操作。

请注意,如果还有其他goroutine需要写入WebSocket连接,它们也需要在执行写操作前获取互斥锁,并在完成后释放锁。这样可以避免并发写入的问题,并确保WebSocket连接的正确使用。

示例代码

package mainimport ("log""net/http""sync""github.com/gorilla/websocket"
)var upgrader = websocket.Upgrader{CheckOrigin: func(r *http.Request) bool {return true},
}// 定义一个结构体来包含WebSocket连接和互斥锁
type WebSocketConnection struct {Conn *websocket.ConnLock sync.Mutex
}func handleConnections(ws *websocket.Conn) {defer ws.Close()log.Println("Connection established")// 创建WebSocketConnection实例conn := &WebSocketConnection{Conn: ws,Lock: sync.Mutex{},}// Ping goroutinego func() {for {// 使用互斥锁来同步写操作conn.Lock()if err := ws.WriteMessage(websocket.PingMessage, nil); err != nil {log.Println("Failed to send Ping: ", err)return}conn.Unlock()time.Sleep(10 * time.Second)}}()// 消息处理goroutinego func() {// 这里可以处理接收到的消息等// ...}()// 这里可以添加更多的goroutine来处理不同的任务// ...
}func main() {http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {ws, err := upgrader.Upgrade(w, r, nil)if err != nil {log.Println("upgrade:", err)return}go handleConnections(ws)})log.Fatal(http.ListenAndServe(":8080", nil))
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/16629.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【版本标记 | Github】Github 中常见的版本标记(Tags)有哪一些?分别在什么情况下使用?

背景 无论是自己自由开发项目还是公司协同合作&#xff0c;随着软件的迭代升级&#xff0c;都需要一个比较规范&#xff08;好区分&#xff09;的标记来区分不同的软件版本。通常&#xff0c;我们使用不同的数字来表示不同的版本&#xff0c;例如大版本号加上小版本号等&#…

Java核心:注解处理器

Java提供了一个javac -processor命令支持处理标注有特定注解的类&#xff0c;来生成新的源文件&#xff0c;并对新生成的源文件重复执行。执行的命令大概是这样的: javac -XprintRounds -processor com.keyniu.anno.processor.ToStringProcessor com.keyniu.anno.processor.Po…

基于微信小程序的在电影线订票小程序+web管理 uniapp,vue,ssm

基于微信小程序的在电影线订票小程序web管理 uniapp&#xff0c;vue&#xff0c;ssm 相关技术 javassmuniapp微信开发者工具hbuildervueelementui前后端分离 -mysql

PointCloudLib 点云半径滤波实现 C++版本

0.展示效果 滤波之前 1.算法原理 半径滤波原理非常直观,主要用于平滑三维点云数据并去除离群点。 设定搜索半径:首先,为每个点设定一个搜索半径r。这个半径定义了该点周围的一个球形区域。计算邻域点数:接着,计算每个点在其搜索半径r内的邻近点的数量。判断与过滤:根据…

集合框架有哪些

ava集合框架是一个架构&#xff0c;提供了一组接口和类&#xff0c;用于存储和操作一组数据。集合框架主要包含以下几种类型的集合&#xff1a; 1. 集合接口 集合框架中的主要接口包括&#xff1a; Collection&#xff1a;所有集合接口的根接口。List&#xff1a;有序集合&a…

xcode按下delete键不能删除不能使用,解决办法

有可能是按键冲突导致的问题&#xff0c;就是你不小心把delete键绑定了不同的快捷键&#xff0c;所以需要恢复所有的偏好设置和快捷键才可以&#xff0c;我这里就是这样的提示内容&#xff0c;在xcode中按delete键完全无效&#xff1a; 而且还会报红色提示&#xff1a;意思是不…

521源码-免费教程-经常用到的Vue.js的Vue@Cli入门指导

更多网站源码学习教程&#xff0c;请点击&#x1f449;-521源码-&#x1f448;获取最新资源&#xff1a;521源码-网站源码-资源素材-免费下载 Vue.js是一款流行的JavaScript框架&#xff0c;它使得构建交互式的Web界面变得简单和快捷。VueCli是Vue.js官方提供的脚手架工具&…

系统架构师必考题:Redis知识点

系统架构师必考题&#xff1a;Redis知识点 系统架构师案例分析题必考的缓存题目&#xff1a;Redis相关知识点。 1.安装与介绍 安装教程&#xff1a;https://blog.csdn.net/WeiHao0240/article/details/100030637 特点&#xff1a; 性能极高、丰富的数据类型、原子性操作、持…

【安卓12源码】Input子系统(1) - input启动流程

Android Input 体系中&#xff0c;大致有两种类型的事件&#xff1a;实体按键 key 事件&#xff0c;屏幕点击触摸事件。 如果根据事件类型的不同我们还能细分为基础实体按键的 key(power&#xff0c;volume up/down&#xff0c;recents&#xff0c;back&#xff0c;home)&#…

KingbaseES数据库union的用法

数据库版本&#xff1a;KingbaseES V008R006C008B0014 文章目录如下 1. union的概念 2. union的语法 3. union的用法 3.1. 去重&#xff08;union&#xff09; 3.2. 不去重&#xff08;union all&#xff09; 3.3. 聚合运算 3.4. 异常案例 1. union的概念 UNION 是结构…

冷冻式压缩空气干燥机常见几种系统原理图

冷冻式压缩空气干燥机 我们以两种典型的设计流程图为例 1.干式蒸发型&#xff0c;这类冷干机是我们最为常见的设计型式。下图为deltech公司的典型流程图 此类设备各家设计不同的最大区别基本就是在换热器的结构型式上有比较大的区别。换热器主要有&#xff1a;管壳式、铝板换、…

typescript 配置精讲 | moduleResolution

大家好&#xff0c;我是17。 moduleResolution 是 typescript 模块配置中最重要的一个配置&#xff0c;所以 17 单拿出来讲一下。如果你去看文档还是挺复杂的&#xff0c;但如果不去深究细节&#xff0c;只想知道如何配置还是很简单的。3 分钟就能学会。 moduleResolution 的…

STM32无源蜂鸣器播放音乐

开发板&#xff1a;野火霸天虎V2 单片机&#xff1a;STM32F407ZGT6 开发软件&#xff1a;MDKSTM32CubeMX 文章目录 前言一、找一篇音乐的简谱二、确定音调三、确定节拍四、使用STM32CubeMX生成初始化代码五、代码分析 前言 本实验使用的是低电平触发的无源蜂鸣器 无源蜂鸣器是…

【模拟面试问答】深入解析力扣163题:缺失的区间(线性扫描与双指针法详解)

❤️❤️❤️ 欢迎来到我的博客。希望您能在这里找到既有价值又有趣的内容&#xff0c;和我一起探索、学习和成长。欢迎评论区畅所欲言、享受知识的乐趣&#xff01; 推荐&#xff1a;数据分析螺丝钉的首页 格物致知 终身学习 期待您的关注 导航&#xff1a; LeetCode解锁100…

嵌入式0基础开始学习 ⅠC语言(1)数据类型

想学好嵌入式&#xff0c;那么就从c语言开始学习&#xff01; 0.问题引入 面对过程&#xff08;c语言&#xff09; 程序组成&#xff1a; 程序 算法 数据结构 计算机首先要解决数据保存的问题&#xff0c;在数据保存之前…

软考高级架构师:再工程、正向工程、设计恢复的区别

再工程&#xff08;Re-engineering&#xff09;、正向工程&#xff08;Forward Engineering&#xff09;、设计恢复&#xff08;Design Recovery&#xff09;是软件工程中的三个不同概念&#xff0c;各自有不同的目的和过程。以下是它们的区别&#xff1a; 再工程&#xff08;…

华为OD机试 - 寻找最富裕的小家庭(Java 2024 C卷 100分)

华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测试…

C语言对一阶指针 二阶指针的本质理解

代码&#xff1a; #include <stdio.h>char a 2; char* p &a; char** d &p;int main(){printf("a -> %d, &a -> %p\n", a, &a);printf("*p -> %d, p -> %p, &p -> %p\n", *p, p, &p);printf(&qu…

【JavaEE初阶】网络初识|局域网和广域网|交换机和路由器|IP地址|端口号

&#x1f4a1;推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击跳转到网站】 关键概念 1.局域网LAN和广域网WAN &#xff08;1&#xff09;局域⽹&#xff0c;即Local Area Network&#xff0…

嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和…