首页 > 编程语言 >C# WebSocket高并发通信阻塞问题

C# WebSocket高并发通信阻塞问题

时间:2024-09-04 18:16:09浏览次数:10  
标签:WebSocket stream C# 发送 并发 message 服务端 客户端

项目上遇到使用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()。

 

标签:WebSocket,stream,C#,发送,并发,message,服务端,客户端
From: https://www.cnblogs.com/kybs0/p/18395504

相关文章

  • 点分治与 cdq 分治
    声明:本文仅供阅读参考,请严格按照学校相关规定下使用希沃一体机。阅读本文,你需要知道一下几点:什么是希沃管家希沃管家,就是一个集成了学校管理、防护功能和系统保护于一体的东西。在希沃管家页面右上角,你可以看到你的学校名称,这就代表此设备已经接入学校的管理系统。在管理系统......
  • Centos7.9安装Docker和Docker compose
    什么是docker环境Docker环境是指在计算机中安装和配置了Docker引擎的运行环境。Docker是一种容器化平台,它提供了一种轻量级的虚拟化技术,能够将应用程序及其依赖项打包成一个独立的容器,以实现快速部署、可移植性和易于管理的优势。(Docker环境提供了一种方便、可移植和隔离的方式来......
  • 聚焦新能源未来,望繁信科技邀您共赴CNDS 2024中国新能源产业数智峰会
    在全球能源转型的关键时刻,新能源产业正以迅猛的速度向前发展,而数字化技术则为这一产业注入了前所未有的动力。为进一步推动新能源产业与数字化的深度融合,CNDS2024中国新能源产业数智峰会即将于2024年9月13日在北京盛大举行。作为大数据流程智能领域的领先企业,上海望繁信科技有限公......
  • SCHNEIDER ATV630C22N4 220KW-380-480 变频器
    关于SCHNEIDERATV630C22N4220KW-380-480变频器,以下是一些详细的信息:一、基本信息品牌与型号:施耐德(Schneider)ATV630C22N4功率:220KW电压范围:380V-480V电源相数:三相二、产品特性应用范围:这款变频器广泛应用于水泵和电机等工业领域,其高性能和稳定性使其成为许多工业应用的理......
  • SCHNEIDER TSXTIOAS170BDI35600 24VDC IN模块的优缺点
        SCHNEIDERTSXTIOAS170BDI3560024VDCIN模块作为工业自动化控制系统中的关键组件,具有多个显著的优点,这些优点使得它在各种工业应用场合中脱颖而出。以下是对该模块优点的详细阐述:高精度与稳定性:该模块能够高精度地接收和转换24V直流电压信号,确保信号的准确性。同......
  • PLC结构化文本(ST)——指针和引用(Pointer&Reference)
    PLCStructuredTextObjectOrientedProgrammingPLC结构化文本(ST)——指针和引用(Pointer&Reference)指针的定义指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。---C++指针|菜鸟教程......
  • 《黑神话:悟空》游戏启动时崩溃弹窗“找不到ig7icd32.dll”该怎么办?黑神话悟空游戏闪退
    在启动《黑神话:悟空》时,崩溃弹窗显示“找不到ig7icd32.dll”,这严重影响了游戏体验。现在为您详细介绍应对办法。可能需要重新安装相关驱动、检查系统文件或修复游戏安装等。按照这些步骤去操作,有望解决问题。本篇将为大家带来《黑神话:悟空》游戏启动时崩溃弹窗“找不到ig7icd32......
  • 《守望先锋2》游戏崩溃提示缺少libcef.dll文件该怎么解决?守望先锋2游戏闪退弹窗“找不
    在玩《守望先锋2》时,游戏崩溃并提示缺少libcef.dll文件,这让人很是苦恼。现在为您详细说明解决方法。您或许需要重新安装相关组件、检查游戏文件完整性或者更新游戏版本等。按照这些步骤操作,有望解决此难题。本篇将为大家带来《守望先锋2》游戏崩溃提示缺少libcef.dll文件该怎么......
  • 《守望先锋2》游戏启动时闪退提示“缺失api-ms-win-crt-runtime-l1-1-0.dll”的该怎么
    在启动《守望先锋2》时,闪退并提示“缺失api-ms-win-crt-runtime-l1-1-0.dll”,着实让人烦恼。现在为您详细介绍解决办法。可能需要安装系统更新补丁、修复相关组件等。按照这些步骤进行操作,有望解决此问题。本篇将为大家带来《守望先锋2》游戏启动时闪退提示“缺失api-ms-win-cr......
  • 【春秋云境】CVE-2020-26048(文件上传漏洞)
    点击路径,进入页面,尝试攻克。admin/admin弱口令成功登录寻找能进行文件上传的地方,点击“文件管理”尝试上传php后缀的一句话木马文件,发现失败上传png后缀的一句话木马文件,发现成功对文件重命名前打开bp,进行抓包,修改后缀名删除.htaccess文件(该文件具有重定向URL、......