c# - TCP Socket ReceiveAsync 有时需要很长时间
问题描述
我必须使用一个设备,它使用 TCP 连接来控制它。它每 30 毫秒发送 1 个字节的数据,我必须尽快对其做出反应。通常,一切正常,但有时 Socket.ReceiveAsync() 函数会卡住长达 400-800 毫秒的时间,然后返回一些接收到的字节数。
我使用这样的代码:
_socket.ReceiveTimeout = 5;
var sw = Stopwatch.StartNew();
var len = await _socket.ReceiveAsync(new ArraySegment<byte>(Buffer, Offset, Count),
SocketFlags.None)
.ConfigureAwait(false);
_logger.Info($"Reading took {sw.ElapsedMilliseconds}ms"); // usually 0-6ms, but sometimes up to 800ms
我用Wireshark记录了这个过程,我可以看到所有的数据都及时收到了,大约30ms的间隔。
我还注意到,当您在计算机上执行某些操作时,发生这种延迟的可能性更高。就像打开开始菜单或资源管理器一样。我认为,切换到另一个进程或垃圾收集应该更快。
解决方案
它每 30 毫秒发送 1 个字节的数据,我必须尽快对其做出反应。
这在不是实时操作系统的 Windows 上是极其困难的。确实,您所能做的就是尽力而为,了解意外的防病毒扫描可能会使其失败。
我曾经被派到一个客户站点调查仅在晚上发生的 TCP/IP 通信超时。飞机飞行,酒店住宿,整个shebang。原来夜班在玩《毁灭战士》。真实的故事。
所以,你不能真正保证这种应用程序会一直运行;你只需要尽力而为。
首先,我建议保持连续阅读。一直在阅读。从字面上看,一旦读取完成,将该字节推入生产者/消费者队列并尽快再次开始读取。不要为超时而烦恼;继续阅读。
您可以做的第二件事是减少开销。在这种情况下,您正在调用一个返回;的Socket API 。Task<T>
最好调用一个返回ValueTask<T>
:
var len = await _socket.ReceiveAsync(
new ArraySegment<byte>(Buffer, Offset, Count).AsMemory(), SocketFlags.None)
.ConfigureAwait(false);
有关如何减少内存使用的更多信息,请参阅此博客文章ValueTask<T>
,尤其是使用套接字。
或者,您可以使您的读取循环在单独的线程上同步运行,如评论中所述。单独线程的一个很好的方面是您可以提高它的优先级 - 甚至进入“实时”范围。但是有龙 - 你真的不想这样做,除非你真的别无选择。如果该线程上有任何错误,您可以死锁整个操作系统。
一旦你使你的读取循环尽可能紧凑(从不等待任何类型的处理),并减少你的内存分配(防止不必要的 GC),这就是你所能做的。总有一天,有人会启动《DOOM》,而你的应用程序将不得不尽力而为。
推荐阅读
- php - Opencart 的 REST API 生成
- ios - oauth请求中的快速额外参数中的Yahoo Weather API
- java - 我们如何在 MarkLogic 中获取森林数据目录
- java - 如何将用户添加到团队到管理页面
- visual-studio-code - 是否可以通过命令行在 Rider 中打开 C# 解决方案?
- oauth-2.0 - 混合网关环境中的 WSO2 始终路由到沙盒 URL,即使与生产访问令牌一起使用也是如此
- r - 如何查找与我的数据框间接链接的值?
- php - Laravel Policy - 检查实体的状态
- angular - 错误:未捕获(承诺):HttpErrorResponse: {"headers":{"normalizedNames":[],"statusText":"Bad Request"} 仅在 Safari 浏览器上
- vue.js - 无法更改 v-dialog 样式