Чересчур медленная передача данных по tcp в linux

В моем проекте я работают в основном через TcpClient и получаю его NetworkStream. Непосредственно с NetworkStream взаимодействует этот код:

          public class StreamExtractor
        {
            private static Stopwatch profiler = new Stopwatch();
            private readonly Stream _ioStream;
            private readonly IContextualSerializationToolKit _serializationToolkit;
            private const int BufferSize = ushort.MaxValue;
            private readonly Memory<byte> _buffer = new byte[BufferSize];
            private bool _manualConnectionState = true;
            private readonly IEndPointContext _context;

            public StreamExtractor(Stream ioStream, IContextualSerializationToolKit serializationToolkit,
                IEndPointContext context)
            {
                _ioStream = ioStream;
                _serializationToolkit = serializationToolkit;
                _context = context;
                profiler.Start();
            }

            public Action<NetworkMessageHeader> MessageReceived = _ => { };

            private ushort ReadMessageLength()
            {
                var firstByte = _ioStream.ReadByte();
                if (firstByte == -1)
                {
                    _manualConnectionState = false;
                    throw new EndOfStreamException();
                }

                _manualConnectionState = true;
                var secondByte = (byte)_ioStream.ReadByte();

                var len = BitConverter.ToUInt16(new[] { (byte)firstByte, secondByte });

                return len;
            }

            public async Task SingleReceive(CancellationToken token = default)
            {
                Console.WriteLine($"Receive start. Time: {profiler.ElapsedMilliseconds}ms");
                var len = ReadMessageLength();
                Console.WriteLine($"Received len {len}. Time: {profiler.ElapsedMilliseconds}ms");
                var localSpan = _buffer[..len];

                await _ioStream.ReadExactlyAsync(localSpan, cancellationToken: token);
                Console.WriteLine($"Received. Time: {profiler.ElapsedMilliseconds}ms");
                profiler.Restart();
                var message = _serializationToolkit.Deserialize<NetworkMessageHeader>(localSpan, _context);
                MessageReceived(message);
            }

            public async Task LoopedReceive(CancellationToken token = default)
            {
                while (token.IsCancellationRequested == false && IsConnected)
                {
                    await SingleReceive(token);
                }
            }

            private async Task Send(NetworkMessageHeader message, CancellationToken token = default)
            {
                var rawMessage = _serializationToolkit.Serialize(message, _context);
                var header = BitConverter.GetBytes((ushort)rawMessage.Length).AsMemory(0, sizeof(ushort));
                Console.WriteLine($"Send start. Time: {profiler.ElapsedMilliseconds}ms");

                await _ioStream.WriteAsync(header, token);
                await _ioStream.WriteAsync(rawMessage, token);
                Console.WriteLine($"Send. Time: {profiler.ElapsedMilliseconds}ms");
                profiler.Restart();
                
            }

            public async Task SendFromChannel(ChannelReader<NetworkMessageHeader> channel,
                CancellationToken token = default)
            {
                while (token.IsCancellationRequested == false && IsConnected)
                {
                    var message = await channel.ReadAsync(token);
                    await Send(message, token);
                }
            }


            public bool IsConnected => _ioStream is { CanRead: true, CanWrite: true } && _manualConnectionState;
        }

Изначально, то время, на которое я ориентируюсь это время пинг понга gRPC. Когда я запускал прогон теста в консольном приложении используя Windows 11, время отклика в моем случае было около или немного больше чем в gRPC: где то 0.1 мс. Но недавно я установил себе Pop!_OS 22.04 и вначале я ничего не заметил, до тех пор пока не прогнал тест нагрузки который просто не заканчивался. Позже выяснилось что каждый вызов в моей библиотеке занимал примерно 40!!! миллисекунд при отладке в релизном режиме, хотя по сводке теста среднее время при запуске состовляет +-85 мс. Я попробовал запустить этот же тест на виртуалке, и все опять было в норме, ожидаемое время или что то около того.

Я решил собрать доп информацию по длительности операций ввода и вывода, на сервере все было в рамках нормы, практически моментальный отклик, но на клиенте появились те самые большие цифры задержки:

Send start. Time: 11ms
Send. Time: 11ms
Received len 39. Time: 49ms
Received. Time: 90ms
Receive start. Time: 0ms
Send start. Time: 3ms
Send. Time: 3ms
Received len 39. Time: 41ms
Received. Time: 82ms
Receive start. Time: 0ms
Send start. Time: 1ms
Send. Time: 1ms
Received len 39. Time: 42ms
Received. Time: 82ms
Receive start. Time: 0ms
Send start. Time: 0ms
Send. Time: 0ms
Received len 39. Time: 41ms
Received. Time: 82ms
Receive start. Time: 0ms
Send start. Time: 0ms
Send. Time: 0ms
Received len 39. Time: 41ms
Received. Time: 81ms
Receive start. Time: 0ms
Send start. Time: 0ms
Send. Time: 0ms
Received len 39. Time: 40ms
Received. Time: 81ms
Receive start. Time: 0ms
Send start. Time: 0ms
Send. Time: 0ms
Received len 39. Time: 41ms
Received. Time: 81ms
Receive start. Time: 0ms
Send start. Time: 0ms
Send. Time: 0ms
Received len 39. Time: 41ms
Received. Time: 81ms
Receive start. Time: 0ms
Send start. Time: 0ms
Send. Time: 0ms
Received len 39. Time: 42ms
Received. Time: 82ms
Receive start. Time: 0ms
Send start. Time: 0ms
Send. Time: 0ms
Received len 39. Time: 40ms
Received. Time: 81ms
Receive start. Time: 0ms

В чем же проблема в использовании TCP напрямую? Как можно ускорить передачу данных.


Ответы (0 шт):