C# Socket.BeginReceive合并TCP数据包
是否有一个设置可用于防止将多个TCP数据包合并到C# Socket.BeginReceive合并TCP数据包,c#,.net,sockets,networking,tcp,C#,.net,Sockets,Networking,Tcp,是否有一个设置可用于防止将多个TCP数据包合并到Socket.BeginReceivecallback内的单个缓冲区中 你下意识的反应是,我无法阻止TCP拆分/合并数据,但这不是我要问的;我可以清楚地看到Wireshark中接收到的各个数据包,我唯一关心的是延迟,即在数据段到达后立即处理它。这并不意味着我不知道如何处理分割/合并段,但这意味着我希望避免延迟 我的代码如下所示: void WaitForData(ISocketInfo soc) { if (socket != null &
Socket.BeginReceive
callback内的单个缓冲区中
你下意识的反应是,我无法阻止TCP拆分/合并数据,但这不是我要问的;我可以清楚地看到Wireshark中接收到的各个数据包,我唯一关心的是延迟,即在数据段到达后立即处理它。这并不意味着我不知道如何处理分割/合并段,但这意味着我希望避免延迟
我的代码如下所示:
void WaitForData(ISocketInfo soc)
{
if (socket != null && socket.Connected)
socket.BeginReceive(buffer, 0, buffer.Length,
SocketFlags.None, OnPacketReceived, socket);
}
void OnPacketReceived(IAsyncResult asyn)
{
try
{
var socket = (ISocketInfo)asyn.AsyncState;
numberOfBytesReceived = socket.EndReceive(asyn);
if (numberOfBytesReceived > 0)
{
_queue.Enqueue(buffer, 0, numberOfBytesReceived);
OnNewDataReceived();
}
WaitForData(socketInfo);
}
catch (SocketException ex)
{
Log.Warn("Socket error while receiving packet", ex);
Close();
}
}
当我在WireShark中检查这些数据包时,我可以看到每50ms接收一个TCP数据包,每个(比如)100字节。但有时在我的应用程序中会有100毫秒的延迟,并且OnPacketReceived
方法会得到200字节
既然WireShark确认这不是操作系统/网络问题,那么这里会有什么问题?OnPacketReceived的OnPacketReceived
只在后台线程上触发,因此它不会阻止该方法,并且程序不会真正消耗太多CPU
(更新)
我似乎没有把我的问题表达得足够清楚。我的问题不是如果数据被分割成多段,如何解析数据。我的协议定义得很好(即START_COOKIE、LENGTH、DATA、CRC),我一收到数据就将数据排入字节FIFO(上面代码段中的\u queue.enqueue
调用),因此我可以轻松地异步解析它
问题是,如果我在Wireshark中看到数据包1(100字节)在+50ms,在Wireshark中看到数据包2(100字节)在+100ms,并且我的应用程序没有阻塞
OnPacketReceived
方法并且不消耗CPU,.NET为什么偶尔会,在+100ms时调用OnPacketReceived
,并将两个数据包合并为一个?您可能会收到更少或更多的数据包,但不能保证您会收到确切的发送内容
从MSDN:
无法保证您发送的数据会立即出现在网络上。为了提高网络效率,底层系统可能会延迟传输,直到收集到大量传出数据。BeginSend方法的成功完成意味着基础系统有空间为网络发送缓冲您的数据
这是一篇很好的文章,可以解释这一点以及如何解决问题。您可能会收到更少或更多的邮件,但不能保证您会收到确切的邮件 从MSDN: 无法保证您发送的数据会立即出现在网络上。为了提高网络效率,底层系统可能会延迟传输,直到收集到大量传出数据。BeginSend方法的成功完成意味着基础系统有空间为网络发送缓冲您的数据 是一篇很好的文章来解释这一点以及如何解决您的问题。是一个连续的数据流(这是它的主要功能,使它看起来好像根本没有数据包,不像UDP)。因此,由您的软件定义消息格式。你可以:
- 用消息的长度标记每个消息的开头(http会这样做)
- 在邮件末尾添加分隔标记(/n)。要让接收者知道何时开始和停止阅读单个消息,请标记消息的开头和结尾(使用无法键入的特殊符号)
- 用消息的长度标记每个消息的开头(http会这样做)
- 在邮件末尾添加分隔标记(/n)。要让接收者知道何时开始和停止阅读单个消息,请标记消息的开头和结尾(使用无法键入的特殊符号)
总之,您必须注意将流拆分为消息。或者在TCP之上使用HTTP、WebSocket或其他消息格式实现。这里会出现什么问题?这里没有问题。TCP是一个无穷无尽的流,您描述的行为是标准的和预期的。这取决于您的程序如何处理部分“消息”的传入,以及同时接收多个“消息”。可以使用字节长度前缀、消息分隔符,也可以是这些内容的组合。答案提供了更多的信息。@Idle\u Mind:我想你误解了这个问题。我将所有数据排队放入FIFO缓冲区,我可以轻松解析它,格式定义良好。但问题是:如果Wireshark显示网卡正在接收两个实际的TCP数据包,为什么.NET会创建额外的延迟并将两个数据包合并到一个缓冲区中?我关心的是延迟,而不是解析。第二,TCP并不是“无穷无尽的流”,它是一种以段的形式传输(可能)无穷无尽的数据流的协议。它很可能不是合并传入数据的.NET。这可能已经在操作系统中发生了。如果在短时间内收到包含TCP连接数据的两个数据包,则它们的内容都将被放入TCP接收缓冲区,应用程序将仅从该缓冲区读取。它看不出这是一个还是两个数据包。如果偶尔发生这种情况,则可能是您的应用程序正在忙于某些事情(垃圾收集?),并且在接收到第二个数据包时无法获取第一个数据包。TCP堆栈将缓冲数据并进行处理。它可以随意缓冲数据,并且可以组合“数据包”,或者通过滑动窗口机制停止数据流