异步套接字写入 - 可能导致发送操作混乱吗?(.NET套接字)

5

非常抱歉,如果此问题以前已经被问过,但我发现它很难搜索:

如果在.NET套接字上使用BeginSend(),则在类似于以下(伪)代码的代码中应该期望哪种行为:

主要程序代码:

Network.OutBound.SendData(messageOne);

Network.OutBound.SendData(messageTwo);

Network.OutBound类

public void SendData(byte[] buffer)
{
     connectedSocket.BeginSend(buffer,0,buffer.Length,SocketFlags.None,SendCompleteMethod);
}

private void SendComplete(arResult)
{
     int bytesSent;
     bytesSent = arResult.EndSend();

     if(bytesSent < arResult.state.buffer.Length)
     {
         //Not all data sent yet, recall BeginSend
         connectedSocket.BeginSend(buffer,bytesSent,buffer.Length - bytesSent,SocketFlags.None,SendCompleteMethod);
     }
     else
     {
         //All data sent successfully
     }
}

由于我正在使用BeginSend(),因此在调用第一个SendData(messageOne)时,它将立即返回并调用第二个SendData(messageTwo)。 然而,考虑这样一种情况:大量数据正在发送消息一和消息二,发送完成后仅有部分数据,因此BeginSend()被重新调用以发送剩余的数据。

这会导致两个消息的字节在传出套接字中混合吗? 它们都将在单独的线程池线程中运行,并且都将在同一个套接字上发送。我理解TCP套接字确保数据按顺序到达,但是运行时/套接字层如何知道我的BeginSend()是完成发送messageOne的后续尝试,还是messageTwo的开始?对它来说,数据是否不是按照写入套接字的顺序发送和接收到远程端点(即使这是可能混淆的地方)

如果是这样,那么如果我必须序列化访问它,那么Begin/End Send的意义是什么呢?

我应该有某种标志,当messageOne已完全发送时触发(即使需要多次BeginSend()尝试发送所有数据),以便主程序代码在尝试发送不同的消息之前等待它完成吗?

我应该编写某种传出队列包装器吗?还是我漏掉了什么?


1
我实际上没有使用过.NET套接字API的异步版本,所以我可能说错了,但我认为SendComplete事件直到发送完请求的所有字节(或发生套接字错误,在这种情况下所有都无法预测)才会触发。 - 500 - Internal Server Error
1
啊,我觉得我一直在混淆BeginSend()和BeginReceive()的部分接收行为(如果接收到的数据总量不是预期的数量,则会调用后续的BeginReceive)。 还有其他人可以确认吗?我在网上看到了矛盾的信息(例如:https://dev59.com/4UvSa4cB1Zd3GeqPcjnI) - Dermot
1个回答

4
使用Reflector查看,你会发现BeginSend最终从.NET包装器退出,进入Win32 API,并调用WSASend,它似乎没有提供任何形式的异步选项,因此在我看来,它只会生成阻塞调用,这些调用由.NET框架内部管理,使用线程池进行异步操作。

在我编写网络应用程序时,我从未真正考虑过这种情况,因为我有一个发送方法,如果您尝试再次调用它,则会阻塞,直到上一个发送完成,或者我始终使用.Send Only进行阻塞,因为它们通常是专用线程上接收消息的响应。

编辑:

进行了一些额外的测试。

如果你使用小缓冲区启动2个beginsends,那么它可能不会真正产生影响,但如果你的缓冲区很大,我的测试中使用了400k的缓冲区,那么你肯定会遇到麻烦,希望你只是异常,但它会崩溃。


这似乎是对的,而且很有道理。谢谢! - Dermot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接