C# Socket.BeginReceive/EndReceive [英] C# Socket.BeginReceive/EndReceive
问题描述
Socket.BeginReceive/EndReceive 函数的调用顺序是什么?
In what order is the Socket.BeginReceive/EndReceive functions called?
例如,我调用 BeginReceive 两次,一次是获取消息长度,第二次是获取消息本身.现在的场景是这样的,对于我发送的每条消息,我开始等待它的完成(实际上是对发送的消息的确认,也是在收到确认后等待动作完成),所以我调用 BeginReceive 对于每个 BeginSend,但在每个 BeginReceive 的回调中,我检查我是否收到了长度或消息.如果我正在接收消息并且已经完全接收到它,那么我会调用另一个 BeginReceive 来接收操作的完成.现在这就是事情不同步的地方.因为我的接收回调之一是接收字节,它会将其解释为消息的长度,而实际上它是消息本身.
For instance, I call BeginReceive twice, once to get the message length and the second time to get the message itself. Now the scenario is like that, for every message I send, I start waiting for its completion (actually acknowledgment of the message sent, also I wait for the action's completion after receiving the acknowledgment), so I call BeginReceive with each BeginSend, but in each BeginReceive's callback, I check if I'm receiving the length or the message. If I'm receiving the message and have received it completely, then I call another BeginReceive to receive the completion of the action. Now this is where things get out of sync. Because one of my receive callback is receiving bytes which it interprets as the length of them message when in fact it is the message itself.
现在我该如何解决?
这是一个 C#.NET 问题:)
This is a C#.NET question :)
这是代码,基本上是太大了,抱歉
Here is the code, basically it is too big, sorry for that
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
用于完成#1BeginReceive
用于消息正文EndReceive
用于完成#3
BeginReceive
for message lengthEndReceive
for the completion of #1BeginReceive
for the message bodyEndReceive
for the completion of #3
例如不使用您可能拥有的回调:
E.g. not using callbacks you could have:
var sync = socket.BeginReceive(....);
sync.AsyncWaitHandle.WaitOne();
var res = socket.EndReceive(sync);
sync = socket.BeginReceive(....);
sync.AsyncWaitHandle.WaitOne();
var res2 = socket.EndReceive(sync);
但是,您最好只使用 Receive
!
But then, you would be better just using Receive
!
我认为您可能会发现为两个不同的接收使用单独的处理程序更容易:
I think you might find it easier to use separate handlers for the two different receives:
... 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
访问)可以使事情变得更容易,但确实改变了线性思维命令式代码和完全拥抱事件驱动的方法.
Ultimately putting all the associated in a state object and passing that around (in the completion delegate access via IAsyncResult.AsyncState
) from BeginReceive can make things easier, but does take a shift from the linear thinking of imperative code and fulling embracing a event driven approach.
2012 年附录:
通过 C#5 中的异步支持,有一个新选项.这使用编译器从内联代码生成手动延续(单独的回调方法)和闭包(状态).但是,有两件事需要解决:
With the async support in C#5 there is a new option. This uses the compiler to generate the manual continuations (the separate callback methods) and closures (state) from inline code. However there are two things to work around:
虽然
System.Net.Sockets.Socket
有各种...Async
方法,但这些方法是针对基于事件的异步模式的,而不是Task
C#5 的await
使用的基于 code> 的模式.解决方案:使用TaskFactory.FromAsync
从Begin...
End...
对中获取单个Task
.
While
System.Net.Sockets.Socket
has various…Async
methods these are for the event based asynchronous pattern, not theTask
based pattern that C#5'sawait
uses. Solution: useTaskFactory.FromAsync
to get a singleTask<T>
from aBegin…
End…
pair.
TaskFactory.FromAsync
仅支持将最多三个附加参数(除了回调和状态之外)传递给 Begin...
.解决方案:采用零个附加参数的 lambda 具有正确的签名,C# 将为我们提供正确的闭包来传递参数.
TaskFactory.FromAsync
only supports passing up to three additional arguments (in addition to the callback and state) to Begin…
. Solution: a lambda taking zero additional arguments has the right signature, and C# will give us the right closure to pass the arguments in.
因此(更完全地实现,Message
是另一种类型,它处理从初始发送以固定数量的字节编码的长度然后内容字节到内容缓冲区长度的转换):
Hence (and more fully realised with Message
being another type that handles the conversion from an initial send of the length encoded in some fixed number of bytes then the content bytes into a length for the content's buffer):
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# Socket.BeginReceive/EndReceive的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!