TCP 服务器收到比预期更多的数据
TCP server receiving more data than expected
我有一个服务器-客户端应用程序,客户端将图像数据流式传输到服务器。我有以下结构:
客户:
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的数据
这显然会导致服务器上的缓冲区填满而无法反序列化发送的包。我错过了什么?我该怎么做才能实现上述工作原理?
TCP 是一种流式传输协议。如果您在客户端依次执行多个发送操作,TCP 将在尝试填充 mtu 时将它们组合成一个段。
如果 mtu 已满,或者连续 50 毫秒计时器超时,或者客户端本身必须确认它从服务器收到的数据包,TCP 将发送。
TCP 是一个非常复杂的协议。那里还有一个计算 window 大小的算法。此 window 大小也会影响在客户端接收的段的大小。
底线是因为 TCP 是流式协议,没有通过套接字接收的数据包的概念。您收到任意数量的字节,您必须自己将这些字节附加到某种接收缓冲区,具体取决于您在做什么。如果你想要数据包,那么你必须在你发送的数据前面加上一个长度字段,并在服务器上考虑长度。这当然会使代码复杂化。或者,如果您想保持简单,只需使用 UDP。 UDP 确实支持数据包,如果数据包没有在某处丢失,您发送的内容将在接收方以相同的大小接收。
但 UDP 不可靠,数据包可能会丢失。 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的数据
这显然会导致服务器上的缓冲区填满而无法反序列化发送的包。我错过了什么?我该怎么做才能实现上述工作原理?
TCP 是一种流式传输协议。如果您在客户端依次执行多个发送操作,TCP 将在尝试填充 mtu 时将它们组合成一个段。
如果 mtu 已满,或者连续 50 毫秒计时器超时,或者客户端本身必须确认它从服务器收到的数据包,TCP 将发送。
TCP 是一个非常复杂的协议。那里还有一个计算 window 大小的算法。此 window 大小也会影响在客户端接收的段的大小。
底线是因为 TCP 是流式协议,没有通过套接字接收的数据包的概念。您收到任意数量的字节,您必须自己将这些字节附加到某种接收缓冲区,具体取决于您在做什么。如果你想要数据包,那么你必须在你发送的数据前面加上一个长度字段,并在服务器上考虑长度。这当然会使代码复杂化。或者,如果您想保持简单,只需使用 UDP。 UDP 确实支持数据包,如果数据包没有在某处丢失,您发送的内容将在接收方以相同的大小接收。 但 UDP 不可靠,数据包可能会丢失。 TCP 可靠,面向连接,但更复杂。
一般的套接字编程不是初学者的话题。