C# WebSocket高并发通信阻塞问题
项目上遇到使用WebSocket超时问题,具体情况是这样的,OTA升级过程中,解压zip文件会有解压进度事件,将解压进度通过进程通信传给另一进程,通信提示超时异常
小伙伴堂园发现大文件使用Zip解压,解压进度事件间隔竟然是1ms,简直超大频率啊
但是,解压事件超频也不应该通信异常啊,于是我通过1ms定时发送通信事件,测试了下进程间通信流程。
WebSocketSharp
当前进程间通信组件是基于kaistseo/UnitySocketIO-WebSocketSharp实现,主机内设置一服务端,多个客户端连接服务端,客户端通信由服务端转发数据。客户端A发送给B后,客户端B会将执行结果反馈给客户A。
那在定位中发现,各个链路发送延时都是正常的,包括服务端发送反馈数据给到客户端A,但客户端A接收数据延时很大,下面是部分返回数据:

并且通信时间久了之后,延时会越来越大
这里是WebSocketSharp.WebSocket对外事件OnMessage:
1 private void WebSocketOnMessage(object sender, MessageEventArgs e)
2 {
3 if (!e.IsText)
4 {
5 //暂时不支持
6 return;
7 }
8 Debug.WriteLine($"{DateTime.Now.ToString("HH:mm:ss fff")},{e.Data}");
9
10 var receivedMessage = JsonConvertSlim.Decode<ChannelServerMessage>(e.Data);
11 xxxxx
12 }
我们继续往下看,OnMessage是由WebSocket.message()触发,从_messageEventQueue队列中获取数据:
1 private void message ()
2 {
3 MessageEventArgs e = null;
4 lock (_forMessageEventQueue) {
5 if (_inMessage || _messageEventQueue.Count == 0 || _readyState != WebSocketState.Open)
6 return;
7
8 _inMessage = true;
9 e = _messageEventQueue.Dequeue ();
10 }
11
12 _message (e);
13 }
循环接收数据是这样拿的:
1 private void startReceiving ()
2 {
3 xxxx
4 _receivingExited = new ManualResetEvent (false);
5 Action receive = () => WebSocketFrame.ReadFrameAsync (_stream, false,
6 frame => {
7 if (!processReceivedFrame (frame) || _readyState == WebSocketState.Closed) {
8 var exited = _receivingExited;
9 if (exited != null)
10 exited.Set ();
11 return;
12 }
13 // Receive next asap because the Ping or Close needs a response to it.
14 receive ();
15 xxxx
16 message ();
17 },
18 xxxx
19 );
20 receive ();
21 }
这里我看到了ManualResetEvent。。。数据量那么大,这里搞个同步信号锁,肯定会堵住咯
为何设置线程同步锁呢?我们往下看
WebSocketSharp数据发送是基于TCPClient实现的:
1 _tcpClient = new TcpClient (_proxyUri.DnsSafeHost, _proxyUri.Port);
2 _stream = _tcpClient.GetStream ();
初始化后通过_stream.Write (bytes, 0, bytes.Length);发送数据
接收数据,也是通过_stream读取,可以看上方的startReceiving()方法里,WebSocketFrame.ReadFrameAsync (_stream, false,...)
我们知道,TCP是面向连接,提供可靠、顺序的数据流传输。用于一对一的通信,即一个TCP连接只能有一个发送方和一个接收方。具体的可以看我之前写的文章:.NET TCP、UDP、Socket、WebSocket - 唐宋元明清2188 - 博客园 (cnblogs.com)
但接收时在高并发场景下,适当的同步措施依然是必需的。我们可以使用lock也可以用SemaphoreSlim来实现复杂的同步需求,这里使用的是信号锁ManualResetEvent
我们再看看发送端代码,也是用了lock一个object来限制并发操作:
1 private bool send (Opcode opcode, Stream stream)
2 {
3 lock (_forSend) {
4 var src = stream;
5 var compressed = false;
6 var sent = false;
7 xxxxx
8 sent = send (opcode, stream, compressed);
9 xxxxx
10 return sent;
11 }
12 }
所以WebSocketSharp在高并发场景下是存在通信阻塞问题的。当然,WebSocketSharp已经实现的很好了,正常的话几ms都不会遇到阻塞问题,如下设置10ms定时超频发送:

客户端A发送消息,由服务端转发至客户B,再将客户端B的反馈结果由服务端转发回客户端A,真正延时才0-2ms!
WebSocket
我们再看看原生的WebSocket,写个WebSocket通信Demo kybs00/WebSocketDemo (github.com)
服务端定时1ms使劲往客户端发送Message消息,结果竟然是:
System.InvalidOperationException:“There is already one outstanding 'SendAsync' call for this WebSocket instance. ReceiveAsync and SendAsync can be called simultaneously, but at most one outstanding operation for each of them is allowed at the same time.”
看来发送事件外部也要处理好高并发的场景,1ms真的是太猛了
1 private SemaphoreSlim _sendLock = new SemaphoreSlim(1);
2 private async void Timer_Elapsed(object sender, ElapsedEventArgs e)
3 {
4 var message = $"{DateTime.Now.ToString("HH:mm:ss fff")},hello from server";
5
6 await _sendLock.WaitAsync();
7 await BroadcastAsync("test", message);
8 _sendLock.Release();
9 Console.WriteLine(message);
10 }
加完信号量同步,服务端就能正常发送了。下面是客户端接收数据,传输几乎无延时:

另外,也尝试了单独在客户端接收添加信号量同步,依然是提示服务端发送不支持并行的异常。
所以原生WebSocket在发送端需要串行处理,写入完数据后再执行_stream.FlushAsync()。
C# WebSocket高并发通信阻塞问题的更多相关文章
- websocket(三) 进阶!netty框架实现websocket达到高并发
引言: 在前面两篇文章中,我们对原生websocket进行了了解,且用demo来简单的讲解了其用法.但是在实际项目中,那样的用法是不可取的,理由是tomcat对高并发的支持不怎么好,特别是tomcat ...
- websocket 进阶!netty框架实现websocket达到高并发
引言: 在前面两篇文章中,我们对原生websocket进行了了解,且用demo来简单的讲解了其用法.但是在实际项目中,那样的用法是不可取的,理由是tomcat对高并发的支持不怎么好,特别是tomcat ...
- Jackson高并发情况下,产生阻塞
情况:在高并发情况下,查看线程栈信息,有大量的线程BLOCKED. 从线程栈得知,线程栈中出现了阻塞,锁在了com.fasterxml.jackson.databind.ser.SerializerC ...
- 第15章 高并发服务器编程(1)_非阻塞I/O模型
1. 高性能I/O (1)通常,recv函数没有数据可用时会阻塞等待.同样,当socket发送缓冲区没有足够多空间来发送消息时,函数send会阻塞. (2)当socket在非阻塞模式下,这些函数不会阻 ...
- PHP解决抢购、秒杀、抢楼、抽奖等阻塞式高并发库存防控超量的思路方法
如今在电商行业里,秒杀抢购活动已经是商家常用促销手段.但是库存数量有限,而同时下单人数超过了库存量,就会导致商品超卖甚至库存变负数的问题. 又比如:抢购火车票.论坛抢楼.抽奖乃至爆红微博评论等也会引发 ...
- Websocket全讲解。跨平台的通讯协议 !!基于websocket的高并发即时通讯服务器开发。
本博文,保证不用装B的话语和太多专业的语言,保证简单易懂,只要懂JAVAEE开发的人都可以看懂. 本博文发表目的是,目前网上针对Websocket的资料太散乱,导致初学者的知识体系零零散散,学习困难加 ...
- Java多线程高并发学习笔记——阻塞队列
在探讨可重入锁之后,接下来学习阻塞队列,这边篇文章也是断断续续的写了很久,因为最近开始学ssm框架,准备做一个自己的小网站,后续可能更新自己写网站的技术分享. 请尊重作者劳动成果,转载请标明原文链接: ...
- java高并发系列 - 第25天:掌握JUC中的阻塞队列
这是java高并发系列第25篇文章. 环境:jdk1.8. 本文内容 掌握Queue.BlockingQueue接口中常用的方法 介绍6中阻塞队列,及相关场景示例 重点掌握4种常用的阻塞队列 Queu ...
- 多线程高并发编程(12) -- 阻塞算法实现ArrayBlockingQueue源码分析(1)
一.前言 前文探究了非阻塞算法的实现ConcurrentLinkedQueue安全队列,也说明了阻塞算法实现的两种方式,使用一把锁(出队和入队同一把锁ArrayBlockingQueue)和两把锁(出 ...
- java的高并发IO原理,阻塞BIO同步非阻塞NIO,异步非阻塞AIO
原文地址: IO读写的基础原理 大家知道,用户程序进行IO的读写,依赖于底层的IO读写,基本上会用到底层的read&write两大系统调用.在不同的操作系统中,IO读写的系统调用的名称可能不完 ...
随机推荐
- 如何让其他模型也能在SemanticKernel中调用本地函数
在SemanticKernel的入门例子中: // Import packages using Microsoft.SemanticKernel; using Microsoft.SemanticKe ...
- Java 表达式执行引擎 jexl
介绍 JEXL的全称是Java表达式语言(Java Expression Language),简单的说,它可以配合我们的Java程序运算一些简单的表达式. 具体可以识别哪些表达式? 包含最基本的加减乘 ...
- Vue 怎么用 vm.$set() 解决对象新增属性不能响应的问题 ?
受现代 JavaScript 的限制 ,Vue 无法检测到对象属性的添加或删除.由于 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 ...
- 全网最适合入门的面向对象编程教程:07 类和对象的Python实现-类型注解-提高代码可读性的利器
全网最适合入门的面向对象编程教程:07 类和对象的 Python 实现-类型注解-提高代码可读性的利器 摘要: 本文对类型注解的定义.使用原因进行了基本介绍,同时对使用 typing 模块实现类型提示 ...
- 【算法】用c#实现计算方法中的经典降幂优化策略,减少计算复杂度
对于给定的数组[x1,x2,x3,-,xn],计算幂的累积:x1^(x2^(x3^(-^xn))的最后一位(十进制)数字. 例如,对于数组[3,4,2],您的代码应该返回1,因为3^(4^2)=3^1 ...
- 关于Windows 10 LTSC 2019无法安装Edge的解决方案
最近新换了Windows 10 LTSC 2019系统,使用体验干净且流畅,但是在更新Edge时遇到了问题:系统内装的是9x版本的Edge浏览器,并且提示更新错误,有system level方面的问题 ...
- 痞子衡嵌入式:瑞萨RA8系列高性能MCU开发初体验
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是瑞萨RA8系列高性能MCU开发上手体验. 我们知道瑞萨半导体的通用 MCU 产品线主要包含基于自有内核 8/16bit RL78 系列以 ...
- NodeJS中Buffer与字符串相互转换时一个值得注意的问题
什么问题 如果一个Buffer的 toString() 结果为乱码或含有乱码,那么用此字符串以 Buffer.from()方法构造出来的Buffer将与原来的Buffer不相同. 这一点其实很好理解, ...
- 记录一次在欧拉(openEuler22.03LTS-SP4)系统下安装(踩坑)Freeswitch1.10.11的全过程
目录 前言 安装环境 1. 下载Freeswitch 1.1 git clone 下载freeswitch库 1.2 官网下载 2. 开始安装前的工作 2.1 安装编译时需要的环境[先安装这个!] 2 ...
- 阅读翻译Mathematics for Machine Learning之2.6 Generating Set and Basis
阅读翻译Mathematics for Machine Learning之2.6 Generating Set and Basis 关于: 首次发表日期:2024-07-19 Mathematics ...