抱歉,如果之前有人问过这个问题,但我发现很难搜索:
如果我BeginSend()在.NET socket上使用,我应该在与此(伪)代码类似的代码中期望什么样的行为:
主程序代码:
Network.OutBound.SendData(messageOne);
Network.OutBound.SendData(messageTwo);
Run Code Online (Sandbox Code Playgroud)
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
}
}
Run Code Online (Sandbox Code Playgroud)
正如我使用的那样BeginSend(),当第一个SendData(messageOne)被调用时,它将立即返回,第二个SendData(messageTwo)将被调用。但是,请考虑这样一种情况:消息 1 和消息 2 都发送了大量数据,并且发送完成了部分数据量,因此BeginSend()被召回以发送剩余数据。
这会导致传出套接字中的两个消息字节混淆吗?它们都将在单独的线程池线程中运行,并且都将在同一个套接字上发送。我理解 TCP 套接字确保数据按顺序到达,但是运行时/套接字层如何知道我BeginSend()是后续尝试完成发送 messageOne 还是 messageTwo 的开始?就它而言,在远程端发送和接收的数据不会与写入套接字的顺序相同(即使这似乎是混淆的可能性)
如果是这样,如果我必须序列化对它的访问,Begin/End Send 的意义何在?
我是否应该在完全发送 messageOne 时设置某种标志(即使需要多次BeginSend()尝试发送所有数据),以便主程序代码可以在尝试发送不同的消息之前等待它完成?
我应该编写某种传出队列包装器吗?或者我错过了什么?
使用 Reflector 进行查看,您将看到 BeginSend 最终从 .NET 包装器退出到 Win32 API,并调用WSASend,而 WSASend 似乎没有提供任何形式的 ASync 选项,因此据我所知,它只会使用线程池生成在 .NET 框架内管理的异步调用。
在我用网络编写的应用程序中,我从未真正考虑过这种情况,因为我有一个发送者方法,如果您尝试在上一个发送完成之前再次调用它,它会被阻止,或者我总是使用 .Send 阻止只是因为它们通常是对专用线程上收到的消息的响应。
编辑:
做了一些额外的测试。
如果你开始 2 个开始和结束,每个缓冲区都很小,那么它可能不会真正产生影响,但如果你的缓冲区很大,我的测试我使用了 400k 缓冲区,那么你肯定会受到伤害,你希望只是例外,但它会崩溃。