【问题标题】:TCP Socket ReceiveAsync sometimes takes too longTCP Socket ReceiveAsync 有时需要很长时间
【发布时间】:2019-06-27 03:40:12
【问题描述】:

我必须使用一个使用 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的间隔。

我还注意到,当您在计算机上执行某些操作时,发生这种延迟的可能性更高。就像打开开始菜单或资源管理器一样。我认为,切换到另一个进程或垃圾收集应该更快。

【问题讨论】:

  • “我认为,切换到另一个进程或垃圾收集应该快得多。” - 也许,但是:你不能影响它;您以这种方式读取了多少个套接字?如果该数字非常低(意思是:一或两个),并且如果延迟是您最关心的问题,您可能希望使用同步 API 而不是异步 API,并且只需承担几乎所有时间都被阻塞的线程的成本?
  • 当您需要一些实时处理措施时,您不能这样做,请参阅this

标签: c# .net sockets async-await


【解决方案1】:

它每 30 毫秒发送 1 个字节的数据,我必须尽快对其做出反应。

这在不是实时操作系统的 Windows 上非常难以做到。确实,您所能做的就是尽力而为,了解意外的防病毒扫描可能会导致它失败。

我曾经被派到一个客户站点调查仅在晚上发生的 TCP/IP 通信超时。飞机飞行,酒店住宿,整个shebang。原来夜班在玩《毁灭战士》。真实的故事。

所以,你不能真正保证这种应用程序会一直运行;你只需要尽力而为。

首先,我建议保持连续阅读。一直在阅读。从字面上看,一旦读取完成,将该字节推入生产者/消费者队列并尽快再次开始读取。不要为超时而烦恼;请继续阅读。

您可以做的第二件事是减少开销。在这种情况下,您调用的是Socket API that returns Task&lt;T&gt;;最好打电话给one that returns ValueTask&lt;T&gt;

var len = await _socket.ReceiveAsync(
    new ArraySegment<byte>(Buffer, Offset, Count).AsMemory(), SocketFlags.None)
    .ConfigureAwait(false);

有关ValueTask&lt;T&gt; 如何减少内存使用量的更多信息,请参阅this blog post,尤其是使用套接字时。

或者,您可以使您的读取循环在单独的线程上同步运行,如 cmets 中所述。单独线程的一个很好的方面是您可以提高它的优先级 - 甚至进入“实时”范围。但是有龙 - 你真的不想这样做,除非你真的别无选择。如果该线程有任何错误,您可以死锁整个操作系统。

一旦您使您的读取循环尽可能紧凑(从不等待任何类型的处理),并减少您的内存分配(防止不必要的 GC),这就是您所能做的。总有一天,有人会启动《DOOM》,而你的应用只能尽力而为。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-07
    • 2020-08-26
    • 2014-10-09
    • 2012-11-26
    • 2019-12-27
    • 2017-10-22
    相关资源
    最近更新 更多