TCP套接字ReceiveAsync有时会花费太长时间

时间:2019-06-26 06:47:04

标签: c# .net sockets async-await

我必须使用一个设备,该设备使用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的间隔及时接收到了。

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

1 个答案:

答案 0 :(得分:1)

  

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

在非实时操作系统的Windows上,这很难做到。确实,您所能做的就是最大的努力,要知道意外的防病毒扫描可能会使它无法正常运行。

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

因此,您不能真正地保证这样的应用程序始终可以运行;您只需要尽力而为。

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

您可以做的第二件事是减少开销。在这种情况下,您要呼叫Socket API that returns Task<T>;最好致电one that returns ValueTask<T>

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

有关ValueTask<T>如何减少内存使用(尤其是套接字)的更多信息,请参见this blog post

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

一旦使读取循环尽可能紧密(不要等待任何类型的处理),并减少内存分配(防止不必要的GC),这就是您可以做的所有事情。总有一天,有人会毁灭DOOM,而您的应用程序将不得不尽力而为。