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.rhkb.cn/news/332875.html

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

相关文章

Linux - crond任务调度、at定时任务

1 crontab 进行-定时任务的设置 1&#xff09;概述&#xff1a; 任务调度&#xff1a;是指系统在某个时间执行的特定的命令或程序。 任务调度分类&#xff1a; 系统工作&#xff1a;有些重要的工作必须周而复始地执行。如病毒扫描等个别用户工作&#xff1a;个别用户可能希…

Vue3实战笔记(38)—粒子特效终章

文章目录 前言一、怎样使用官方提供的特效二、海葵特效总结 前言 官方还有很多漂亮的特效&#xff0c;但是vue3只有一个demo&#xff0c;例如我前面实现的两个页面就耗费了一些时间&#xff0c;今天记录一下tsparticles官方内置的几个特效的使用方法&#xff0c;一般这几个就足…

SSM志愿服务管理小程序-计算机毕业设计源码97923

摘 要 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;志愿服务管理小程序被用户普遍使用&#xff0c;方便用户能…

Vue3:动态路由+子页面(新增、详情页)动态路由配置(代码全注释)

文章目录 实现思路调用后端接口获取用户权限获取页面权限动态绑定到路由对象中动态添加子页面路由 实现思路 emm&#xff0c;项目中使用动态路由实现根据后端返回的用户详情信息&#xff0c;动态将该用户能够访问的页面信息&#xff0c;动态生成并且绑定到路由对象中。但是后…

【C语言】实现贪吃蛇--项目实践(超详细)

前言&#xff1a; 贪吃蛇游戏大家都玩过吧&#xff1f;这次我们要用C语言来亲手制作一个&#xff01;这个项目不仅能让我们复习C语言的知识&#xff0c;还能了解游戏是怎么一步步做出来的。我们会一起完成蛇的移动、食物的生成&#xff0c;还有碰撞检测等有趣的部分。准备好了…

客服快捷回复话术分享:618议价话术和催发货话术

随着618活动大促的临近&#xff0c;客服小伙伴们将迎来一年中最繁忙的时刻。面对顾客的议价、催发货等需求&#xff0c;我们应该如何回复才能既满足顾客的需求&#xff0c;又能保持良好的服务形象呢&#xff1f;下面就为大家分享一些议价和催发货的快捷回复话术&#xff0c;希望…

55页PDF|人工智能通用大模型(ChatGPT)的进展、风险与应对(附下载)

&#x1f449;获取方式&#xff1a; &#x1f61d;有需要的小伙伴&#xff0c;可以保存图片到wx扫描二v码免费领取【保证100%免费】&#x1f193;

WIFI国家码设置的影响

记录下工作中关于国家码设置对WIFI的影响&#xff0c;以SKYLAB的SKW99和SDZ202模组为例进行说明。对应到日常&#xff0c;就是我们经常提及手机是“美版”“港版”等&#xff0c;它们的wifi国家码是不同的&#xff0c;各版本在wifi使用中遇到的各种情况与下面所述是吻合的。 现…

从alpine构建预装vcpkg的docker image用于gitea actions CI

动机 想要构建一个基于vcpkg的交叉编译容器平台用于cpp项目的CI(自动集成),此处仅提供最基础的image,amd64的机子上构建完成后大小为533兆(着实不小😓),各位看官可以在此基础上自行构建需要的版本。 hello world效果展示 corss_compiler.dockerfile FROM alpine:la…

【Ubuntu常用命令】终端个人常用命令总结

【Ubuntu常用命令】终端常用命令总结 查看硬盘挂载情况查看内存占用情况移动或重命名文件和目录复制文件或目录 查看硬盘挂载情况 mount 命令会列出当前系统上所有已挂载的文件系统。它会显示挂载点、文件系统类型、挂载选项等信息 mount df 命令用于显示文件系统的磁盘空间使…

springboot 集成 es--未完结

基于es7.10.x版本 一、前提知识 常见的两种方式&#xff1a;spring boot提供的API 和 ES 官方提供的API ES官方&#xff1a; RestHighLevelClient&#xff1a; 适用于复杂、更细粒度控制的Elasticsearch 操作 spring boot&#xff1a; ElasticsearchRestTemplate&#xff1a…

linux查看是否被入侵(一)

1、查看当前系统状态 [rootbastion-IDC ~]#top #一般挖矿等病毒点用CPU比较大 2、查看当前登录用户(w\who) 3、检查系统日志 检查系统错误登陆日志&#xff0c;统计IP重试次数 [rootbastion-IDC ~]# lastb 4、查看近期用户登录情况 [rootkvm01 ~]# last -n 5 #-n 5 表示…

软件3班20240527

JDK 版本与 Tomcat 的 兼容性

nginx流量监控:goAccess安装与使用

关于goAccess GoAccess 是一款实时、快速的日志分析工具&#xff0c;专门设计用于分析Web服务器日志&#xff0c;特别是Nginx日志。 安装 &#xff08;1&#xff09;准备相关依赖 # Missing development libraries for ncursesw # centOS yum install -y ncurses-devel # U…

【EI会议】2024年互联网技术与环境工程国际会议(IACITEE 2024)

【EI会议】2024年互联网技术与环境工程国际会议&#xff08;IACITEE 2024&#xff09; 2024 International Conference on Internet Technology and Environmental Engineering 互联网技术与环境工程国际会议&#xff08;IACITEE 2024&#xff09;将在重庆举行&#xff0c;主…

怎么把记事本钉在桌面上 桌面记事本固定不动的方法

我的生活&#xff0c;总是被密密麻麻的待办事项和灵感想法填得满满当当。记事本&#xff0c;就是我随身的记忆银行&#xff0c;帮我存储那些稍纵即逝的思维火花和不能错过的琐事提醒。每当翻开那一页页工整的笔记&#xff0c;心中的焦虑和压力似乎都找到了释放的出口。 但一直…

锐捷网络与您相约第七届数字中国建设峰会 共话数字未来

第七届数字中国建设峰会将于5月24日至25日在福建福州举办,本届峰会是国家数据工作体系优化调整后首次举办的数字中国建设峰会,主题是“释放数据要素价值,发展新质生产力”。作为行业领先的ICT基础设施及解决方案提供商,锐捷网络与福建省电子信息集团、星网锐捷,围绕“发展新质生…

mvc的常见注解

问文心一言的&#xff0c;记录一下。 PathVariable 路径变量注解 PathVariable 是 Spring MVC 提供的一个注解&#xff0c;它用于从 URI 模板变量中绑定值到控制器方法的参数上。当你在 RequestMapping、GetMapping、PostMapping、PutMapping、DeleteMapping 等注解的 URL 路…

基于深度学习和opencv的车牌识别系统

免费获取方式↓↓↓ 项目介绍028&#xff1a; 基于深度学习和opencv的车牌识别系统 同时利用对图片每一帧图像加入视频分析模块 图片分析模块可以依据界面按钮提示进行相应功能 视频分析模块可以根据按钮提示进行对视频的分析 &#xff08;视频模块的视频追踪处理时间较长&…

跨平台之用VisualStudio开发APK嵌入OpenCV(一)

序 本篇是杂谈以及准备工作&#xff08;此处应无掌声&#xff09; 暂时不管iOS&#xff08;因为开发hello world都要年费&#xff09; 软件&#xff1a; Visual Studio 2019&#xff08;含Android SDK和NDK编译器等&#xff09; OpenCV 这是一个女仆级的系列文章&#xf…