TcpClient — клиент получает не все данные

#c# #.net #sockets #tcp

Вопрос:

У меня есть простой код tcp-сервера, который считывает команду из входящего клиентского соединения, затем отправляет ответ и немедленно закрывается:

На стороне сервера:

 private void HandleRequest(IRequest request, NetworkStream stream)
{
    .. request handling
    using (var ms = new MemoryStream())
    {
        using (var bw = new BinaryWriter(ms))
        {
            envelope.SerializeWithLengthPrefix(bw);
            ms.Position = 0;
            var data = ms.ToArray();
            Logger.Log(data.Length.ToString());
            stream.Write(data, 0, data.Length);
            // Thread.Sleep here helps
        }

    }
}
 

и клиент:

 using (var stream = client.GetStream())
{
    var writer = new BinaryWriter(stream);

    me.SerializeWithLengthPrefix(writer);
    int length = 0;
    var data = new byte[4];
    var s = stream.Read(data, 0, 4);
    if (s == 4)
        length = BitConverter.ToInt32(data, 0);

    var buffer = new byte[length];

    var bytesReaded = 0;
    while (bytesReaded < length)
    {
        var newDataReaded = stream.Read(buffer, bytesReaded, length - bytesReaded);
        bytesReaded  = newDataReaded;
        if (newDataReaded == 0)
            throw new UnexpectedEndOfDataException(length, bytesReaded);
    }
                
    var response = new MessageEnvelope(buffer);
    return response;
}
 

Моя проблема в том, что при отправке больших данных (т. е. 200 кб) не все данные принимаются клиентом.
течение.Чтение возвращает 0, когда несколько байтов (обычно от 1 до 5) все еще не получены, и поэтому возникает исключение. Кроме того, если я не закрою сокет на стороне сервера сразу после отправки данных, все будет работать нормально. Но я понятия не имею, что здесь происходит. Разве протокол TCP не должен гарантировать, что все эти данные будут отправлены?

Комментарии:

1. Написание надежной реализации клиент-сервера TCP не является прямым, существует множество степеней свободы, которые не очевидны, и вам нужно хорошо понимать, как работает TCP, как работают классы BCL и потоковая передача. Вы могли бы сэкономить себе часы головной боли, просто используя более современное решение, такое как (но не ограничиваясь этим) WebAPI, SignalR, GRPC, надежные сообщения, такие как служебная шина, или любое другое количество решений. Все проблемы, с которыми вы сталкиваетесь, и все будущие проблемы, которые вы еще не обнаружили, уже были обдуманы и решены для вас в нескольких строках кода