Socket.BeginReceive/EndReceive 函数的调用顺序是什么?
例如,我两次调用 BeginReceive,一次是为了获取消息长度,第二次是为了获取消息本身。现在的场景是这样的,对于我发送的每条消息,我都开始等待它的完成(实际上是对发送消息的确认,我也在收到确认后等待操作完成),所以我调用 BeginReceive 对于每个 BeginSend,但在每个 BeginReceive 的回调中,我检查我是否收到了长度或消息。如果我正在接收消息并且已经完全接收到它,那么我会调用另一个 BeginReceive 来接收操作的完成。现在这就是事情不同步的地方。因为我的接收回调之一正在接收字节,它解释为消息的长度,而实际上它是消息本身。
现在我该如何解决?
编辑:这是一个 C#.NET 问题 :)
这是代码,基本上它太大了,抱歉
public void Send(string message)
{
try
{
bytesSent = 0;
writeDataBuffer = System.Text.Encoding.ASCII.GetBytes(message);
writeDataBuffer = WrapMessage(writeDataBuffer);
messageSendSize = writeDataBuffer.Length;
clientSocket.BeginSend(writeDataBuffer, bytesSent, messageSendSize, SocketFlags.None,
new AsyncCallback(SendComplete), clientSocket);
}
catch (SocketException socketException)
{
MessageBox.Show(socketException.Message);
}
}
public void WaitForData()
{
try
{
if (!messageLengthReceived)
{
clientSocket.BeginReceive(receiveDataBuffer, bytesReceived, MESSAGE_LENGTH_SIZE - bytesReceived,
SocketFlags.None, new AsyncCallback(RecieveComplete), clientSocket);
}
}
public void Send(string message)
{
try
{
bytesSent = 0;
writeDataBuffer = System.Text.Encoding.ASCII.GetBytes(message);
writeDataBuffer = WrapMessage(writeDataBuffer);
messageSendSize = writeDataBuffer.Length;
clientSocket.BeginSend(writeDataBuffer, bytesSent, messageSendSize, SocketFlags.None,
new AsyncCallback(SendComplete), clientSocket);
}
catch (SocketException socketException)
{
MessageBox.Show(socketException.Message);
}
}
public void WaitForData()
{
try
{
if (! messageLengthReceived)
{
clientSocket.BeginReceive(receiveDataBuffer, bytesReceived, MESSAGE_LENGTH_SIZE - bytesReceived,
SocketFlags.None, new AsyncCallback(RecieveComplete), clientSocket);
}
else
{
clientSocket.BeginReceive(receiveDataBuffer, bytesReceived, messageLength - bytesReceived,
SocketFlags.None, new AsyncCallback(RecieveComplete), clientSocket);
}
}
catch (SocketException socketException)
{
MessageBox.Show(socketException.Message);
}
}
public void RecieveComplete(IAsyncResult result)
{
try
{
Socket socket = result.AsyncState as Socket;
bytesReceived = socket.EndReceive(result);
if (! messageLengthReceived)
{
if (bytesReceived != MESSAGE_LENGTH_SIZE)
{
WaitForData();
return;
}
// unwrap message length
int length = BitConverter.ToInt32(receiveDataBuffer, 0);
length = IPAddress.NetworkToHostOrder(length);
messageLength = length;
messageLengthReceived = true;
bytesReceived = 0;
// now wait for getting the message itself
WaitForData();
}
else
{
if (bytesReceived != messageLength)
{
WaitForData();
}
else
{
string message = Encoding.ASCII.GetString(receiveDataBuffer);
MessageBox.Show(message);
bytesReceived = 0;
messageLengthReceived = false;
// clear buffer
receiveDataBuffer = new byte[AsyncClient.BUFFER_SIZE];
WaitForData();
}
}
}
catch (SocketException socketException)
{
MessageBox.Show(socketException.Message);
}
}
public void SendComplete(IAsyncResult result)
{
try
{
Socket socket = result.AsyncState as Socket;
bytesSent = socket.EndSend(result);
if (bytesSent != messageSendSize)
{
messageSendSize -= bytesSent;
socket.BeginSend(writeDataBuffer, bytesSent, messageSendSize, SocketFlags.None,
new AsyncCallback(SendComplete), clientSocket);
return;
}
// wait for data
messageLengthReceived = false;
bytesReceived = 0;
WaitForData();
}
catch (SocketException socketException)
{
MessageBox.Show(socketException.Message);
}
}
最佳答案
时间顺序应该是:
-
BeginReceive
消息长度 -
EndReceive
完成#1 -
BeginReceive
对于邮件正文 -
EndReceive
完成#3
例如不使用你可能拥有的回调:
var sync = socket.BeginReceive(....);
sync.AsyncWaitHandle.WaitOne();
var res = socket.EndReceive(sync);
sync = socket.BeginReceive(....);
sync.AsyncWaitHandle.WaitOne();
var res2 = socket.EndReceive(sync);
但是,你最好只使用 Receive
!
我认为您可能会发现为两个不同的接收使用单独的处理程序更容易:
... Start(....) {
sync = socket.BeginReceive(.... MessageLengthReceived, null);
}
private void MessageLengthReceived(IAsyncResult sync) {
var len = socket.EndReceive(sync);
// ... set up buffer etc. for message receive
sync = socket.BeginReceive(... MessageReceived, null);
}
private void MessageReceived(IAsyncResult sync) {
var len = socket.EndReceive(sync);
// ... process message
}
最终将所有关联的对象放入一个状态对象中并从 BeginReceive 传递它(在通过 IAsyncResult.AsyncState
的完成委托(delegate)访问中)可以使事情变得更容易,但确实改变了命令式代码的线性思维并完全拥抱事件驱动方法。
2012 年附录:
.NET 4.5版本
有了 C#5 中的异步支持,就有了一个新选项。这使用编译器从内联代码生成手动延续(单独的回调方法)和闭包(状态)。但是,有两件事需要解决:
同时
System.Net.Sockets.Socket
有各种…Async
这些方法用于基于事件的异步模式,而不是Task
基于 C#5 的await
的模式使用。解决方案:使用TaskFactory.FromAsync
得到一个Task<T>
来自Begin…
End…
对。TaskFactory.FromAsync
仅支持将最多三个附加参数(除了回调和状态)传递给Begin…
.解决方案:采用零个附加参数的 lambda 具有正确的签名,而 C# 将为我们提供正确的闭包来传递参数。
因此(并且更充分地实现了 Message
是另一种类型,它处理从初始发送的长度的转换,该长度以固定数量的字节编码,然后将内容字节转换为内容缓冲区的长度):
private async Task<Message> ReceiveAMessage() {
var prefix = new byte[Message.PrefixLength];
var revcLen = await Task.Factory.FromAsync(
(cb, s) => clientSocket.BeginReceive(prefix, 0, prefix.Length, SocketFlags.None, cb, s),
ias => clientSocket.EndReceive(ias),
null);
if (revcLen != prefix.Length) { throw new ApplicationException("Failed to receive prefix"); }
int contentLength = Message.GetLengthFromPrefix(prefix);
var content = new byte[contentLength];
revcLen = await Task.Factory.FromAsync(
(cb, s) => clientSocket.BeginReceive(content, 0, content.Length, SocketFlags.None, cb, s),
ias => clientSocket.EndReceive(ias),
null);
if (revcLen != content.Length) { throw new ApplicationException("Failed to receive content"); }
return new Message(content);
}
关于C# 套接字.BeginReceive/EndReceive,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1388561/