TCP-сервер получает больше данных, чем ожидалось
У меня есть приложение клиент-сервер, где клиент передает данные изображения на сервер. У меня есть следующая структура:
Клиент:
private void SerializeAndSendMessage(Message msg) {
BinaryFormatter formatter = new BinaryFormatter();
MemoryStream stream = new MemoryStream();
formatter.Serialize(stream, msg);
byte[] buffer = stream.ToArray();
if (clientSocket != null)
{
if (clientSocket.Connected)
{
clientSocket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, SendCallback, null);
}
}
}
private void SendCallback(IAsyncResult ar) {
try
{
clientSocket.EndSend(ar);
Debug.WriteLine("Message sent.");
}
catch (Exception ex)
{
//
}
}
Сервер:
private void ReceiveCallback(IAsyncResult ar)
{
try
{
int received = clientSocket.EndReceive(ar);
Array.Resize(ref buffer, received);
BinaryFormatter formatter = new BinaryFormatter();
MemoryStream stream = new MemoryStream(buffer);
object obj = null;
stream.Position = 0;
try
{
obj = formatter.Deserialize(stream);
}
catch (Exception ex )
{
//
}
// processing data
Array.Resize(ref buffer, clientSocket.ReceiveBufferSize);
clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, null);
}
catch (Exception ex)
{
//
}
}
Что я ожидаю, что произойдет:
- сервер начинает принимать данные от клиента
- клиент отправляет данные размером X
- сервер получает данные размером X и начинает их обработку
- клиент все еще отправляет данные
- сервер не получает эти данные
- сервер завершил обработку полученных данных и теперь начинает получать от клиента
- перейти к 2
Что происходит:
- сервер начинает принимать данные от клиента
- клиент отправляет данные размером X
- сервер получает данные размером X и начинает их обработку
- клиент все еще отправляет данные
- сервер не получает эти данные
- сервер завершил обработку полученных данных и теперь начинает получать от клиента
- клиент отправляет N-й пакет размером X
- Сервер получает данные размером M*X
Это, очевидно, может привести к тому, что буфер на сервере заполнится и не сможет десериализовать отправленные пакеты. Что мне не хватает? Что я могу сделать, чтобы достичь работы, описанной выше?
1 ответ
TCP - это потоковый протокол. Если вы выполняете несколько операций отправки на стороне клиента после каждого другого, TCP собирается объединить их в один сегмент, пытаясь заполнить mtu.
TCP будет отправлять, если mtu заполнен, или если истекает непрерывный таймер 50 мс, или если сам клиент должен подтвердить пакет, полученный от сервера.
TCP - очень сложный протокол. Там также есть алгоритм, который вычисляет размер окна. Также этот размер окна влияет на размер сегментов, которые принимаются на стороне клиента.
Суть в том, что TCP является потоковым протоколом, и нет понятия о пакете, который вы получаете через сокет. Вы получаете произвольное количество байтов, которое вам нужно добавить к какому-либо буферу приема в зависимости от того, что вы делаете. Если вам нужны пакеты, то вы должны добавить данные, которые вы отправляете, в поле длины, а также принять во внимание длину на сервере. Это, конечно, усложняет код. Или, если вы хотите сохранить простоту, просто используйте UDP. UDP поддерживает пакеты, и то, что вы отправляете, будет, если пакет где-то не потерян, будет получено с тем же размером на приемнике. Но UDP не надежен, пакет может потеряться. TCP надежен, ориентирован на соединение, но более сложен.
Сокетное программирование вообще не тема для начинающих.