0

I have to work with a device, which uses TCP connection to control it. It sends 1 byte of data every 30 milliseconds and I must react to it as soon as possible. Usually, everything works fine, but sometimes Socket.ReceiveAsync() function stuck for time up to 400-800 milliseconds and then returns some number of received bytes.

I use code like this:

_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

I recorded this process with Wireshark there I could see that all the data was received in time, with about 30ms interval.

I also noticed that the probability of happening of this delay is higher when you do something on your computer. Like opening the start menu or Explorer. I think, that switching to another process or garbage collection should be much faster.

Paboka
  • 422
  • 1
  • 5
  • 15
  • "I think, that switching to another process or garbage collection should be much faster." - maybe, but: you can't influence that; how many sockets are you reading from in this way? if the number is *very low* (meaning: one or two), and if latency is your primary concern, you *might* want to use the sync API instead of the async API, and just absorb the cost of a thread being blocked almost all of the time? – Marc Gravell Jun 26 '19 at 07:07
  • You can not do it this way while you need some reatime processing measures see [this](https://stackoverflow.com/questions/38634880/achieving-realtime-1-millisecond-accurate-events-without-suffering-from-thread-s) – muaz Jun 26 '19 at 07:53

1 Answers1

3

It sends 1 byte of data every 30 milliseconds and I must react to it as soon as possible.

This is extremely difficult to do on Windows, which is not a realtime operating system. Really, all you can do is your best, understanding that an unexpected antivirus scan may throw it off.

I was once sent to a customer site to investigate TCP/IP communications timeouts that only happened at night. Plane flight, hotel stay, the whole shebang. Turns out the night shift was playing DOOM. True story.

So, you can't really guarantee this kind of app will always work; you just have to do your best.

First, I recommend keeping a continuous read going. Always Be Reading. Literally, as soon as a read completes, shove that byte into a producer/consumer queue and start reading again ASAP. Don't bother with a timeout; just keep reading.

The second thing you can do is reduce overhead. In this case, you're calling a Socket API that returns Task<T>; it would be better to call one that returns ValueTask<T>:

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

See this blog post for more about how ValueTask<T> reduces memory usage, particularly with sockets.

Alternatively, you can make your read loop run synchronously on a separate thread, as mentioned in the comments. One nice aspect to the separate thread is that you can boost its priority - even into the "realtime" range. But There Be Dragons - you really do not want to do that unless you literally have no choice. If there's any bug on that thread, you can deadlock the entire OS.

Once you make your read loop as tight as possible (never waiting for any kind of processing), and reduce your memory allocations (preventing unnecessary GCs), that's about all you can do. Someday, somebody's gonna fire up DOOM, and your app will just have to try its best.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810