调用Socket.BeginReceive/EndReceive函数的顺序是什么?
例如,我调用BeginReceive两次,一次获取消息长度,第二次调用消息本身.现在的情况是这样的,对于我发送的每条消息,我开始等待它的完成(实际上确认发送的消息,我等待接收到确认后的动作完成),所以我用每个BeginSend调用BeginReceive,但是每个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); } }
Richard.. 20
时间顺序应该是:
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 }
最终将所有关联放在一个状态对象中并IAsyncResult.AsyncState
从BeginReceive 传递它(在完成委托访问中)可以使事情变得更容易,但确实从命令式代码的线性思维转变并完全采用事件驱动的方法.
2012年附录:
通过C#5中的异步支持,有一个新选项.这使用编译器从内联代码生成手动延续(单独的回调方法)和闭包(状态).但是有两件事要解决:
虽然System.Net.Sockets.Socket
有各种…Async
方法,但这些方法适用于基于事件的异步模式,而不是Task
C#5 await
使用的基础模式.解决方案:用于从一对中TaskFactory.FromAsync
获取单个.Task
Begin…
End…
TaskFactory.FromAsync
只支持传递最多三个额外的参数(除了回调和状态)Begin…
.解决方案:一个带有零个额外参数的lambda具有正确的签名,而C#将为我们提供正确的闭包来传递参数.
因此(并且更充分地实现了Message
另一种类型,它处理从以某个固定数量的字节编码的长度的初始发送转换然后将内容字节转换为内容缓冲区的长度):
private async TaskReceiveAMessage() { 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); }
Timothy Prat.. 6
也许你想要做的就是连接你的回调:
伪代码:
// read the first 2 bytes as message length BeginReceive(msg,0,2,-,-,new AsyncCallback(LengthReceived),-) LengthReceived(ar) { StateObject so = (StateObject) ar.AsyncState; Socket s = so.workSocket; int read = s.EndReceive(ar); msg_length = GetLengthFromBytes(so.buffer); BeginReceive(so.buffer,0,msg_length,-,-,new AsyncCallback(DataReceived),-) } DataReceived(ar) { StateObject so = (StateObject) ar.AsyncState; Socket s = so.workSocket; int read = s.EndReceive(ar); ProcessMessage(so.buffer); BeginReceive(so.buffer,0,2,-,-,new AsyncCallback(LengthReceived),-) }
请参阅:http://msdn.microsoft.com/en-us/library/system.asynccallback.aspx以获取正确的示例
时间顺序应该是:
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 }
最终将所有关联放在一个状态对象中并IAsyncResult.AsyncState
从BeginReceive 传递它(在完成委托访问中)可以使事情变得更容易,但确实从命令式代码的线性思维转变并完全采用事件驱动的方法.
2012年附录:
通过C#5中的异步支持,有一个新选项.这使用编译器从内联代码生成手动延续(单独的回调方法)和闭包(状态).但是有两件事要解决:
虽然System.Net.Sockets.Socket
有各种…Async
方法,但这些方法适用于基于事件的异步模式,而不是Task
C#5 await
使用的基础模式.解决方案:用于从一对中TaskFactory.FromAsync
获取单个.Task
Begin…
End…
TaskFactory.FromAsync
只支持传递最多三个额外的参数(除了回调和状态)Begin…
.解决方案:一个带有零个额外参数的lambda具有正确的签名,而C#将为我们提供正确的闭包来传递参数.
因此(并且更充分地实现了Message
另一种类型,它处理从以某个固定数量的字节编码的长度的初始发送转换然后将内容字节转换为内容缓冲区的长度):
private async TaskReceiveAMessage() { 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); }
也许你想要做的就是连接你的回调:
伪代码:
// read the first 2 bytes as message length BeginReceive(msg,0,2,-,-,new AsyncCallback(LengthReceived),-) LengthReceived(ar) { StateObject so = (StateObject) ar.AsyncState; Socket s = so.workSocket; int read = s.EndReceive(ar); msg_length = GetLengthFromBytes(so.buffer); BeginReceive(so.buffer,0,msg_length,-,-,new AsyncCallback(DataReceived),-) } DataReceived(ar) { StateObject so = (StateObject) ar.AsyncState; Socket s = so.workSocket; int read = s.EndReceive(ar); ProcessMessage(so.buffer); BeginReceive(so.buffer,0,2,-,-,new AsyncCallback(LengthReceived),-) }
请参阅:http://msdn.microsoft.com/en-us/library/system.asynccallback.aspx以获取正确的示例