C# Socket.BeginReceive/EndReceive
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);
}
}
In what order is the Socket.BeginReceive/EndReceive functions called?
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.
Now how do I resolve it?
EDIT: 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);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
时间顺序应为:
BeginReceive
表示消息长度EndReceive
表示 #1 的完成BeginReceive
表示消息正文EndReceive
code> 用于完成 #3例如不使用你可以拥有的回调:
但是,你最好只使用
Receive
!我认为您可能会发现对两个不同的接收使用单独的处理程序会更容易:
最终将所有关联的状态对象放入状态对象中并从 BeginReceive 传递它(在通过 IAsyncResult.AsyncState 的完成委托访问中)可以让事情变得更容易,但确实需要从命令式代码的线性思维转变为完全采用事件驱动的方法。
2012 附录:
.NET 4.5 版本
随着 C#5 中的异步支持,出现了一个新选项。这使用编译器从内联代码生成手动延续(单独的回调方法)和闭包(状态)。然而,有两件事需要解决:
虽然
System.Net.Sockets.Socket
有各种...Async
方法,但这些方法适用于基于事件的异步模式,而不是基于事件的异步模式。 C#5 的await
使用的基于Task
的模式。解决方案:使用TaskFactory.FromAsync
从Begin...
End...
对中获取单个Task
。TaskFactory.FromAsync
仅支持将最多三个附加参数(除了回调和状态之外)传递给Begin...
。解决方案:采用零个附加参数的 lambda 具有正确的签名,并且 C# 将为我们提供正确的闭包来传递参数。因此(通过
Message
更充分地实现,它是另一种处理从初始发送以固定数量的字节编码的长度,然后将内容字节转换为内容缓冲区的长度):The order in time should be:
BeginReceive
for message lengthEndReceive
for the completion of #1BeginReceive
for the message bodyEndReceive
for the completion of #3E.g. not using callbacks you could have:
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:
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 Addendum:
.NET 4.5 Version
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:
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
only supports passing up to three additional arguments (in addition to the callback and state) toBegin…
. Solution: a lambda taking zero additional arguments has the right signature, and C# will give us the right closure to pass the arguments in.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):也许您想要做的是链接您的回调:
伪代码:
请参阅: http://msdn.microsoft.com/en-us/library/system.asynccallback.aspx 获取正确示例
Perhaps what you want to do is chain your call-backs :
pseudo code:
see: http://msdn.microsoft.com/en-us/library/system.asynccallback.aspx for correct examples
通常,BeginXXX 方法表示异步操作,而您似乎希望以同步方式执行此操作。
如果您确实想要同步客户端/服务器,也许这会有所帮助 http://sharpoverride.blogspot.com/2009/04/another-tcpip-server-client-well-it.html
Usually BeginXXX methods indicate an asynchronous operation, and you seem to want to do it in a synchronous manner.
If indeed you want a synchronous client/server maybe this will help http://sharpoverride.blogspot.com/2009/04/another-tcpip-server-client-well-it.html
如果您描述所发送消息的结构将会有所帮助。
只要您只有一个 BeginReceive() 未完成,它就会完成并为您提供线路上的下一个可用数据字节。如果您同时有多个未完成的任务,那么所有的赌注都会被取消,因为 .net 不保证按照任何给定的顺序完成。
It will help if you describe the structure of the message you are sending.
As long as you have only one BeginReceive() outstanding, it will complete and give you the next available bytes of data on the wire. If you have more than one outstanding at the same time, then all bets are off, because .net does not guarantee that the completion will be in any given order.
正如其他人所说,不要在这里使用全局变量 - 使用套接字状态类。类似于:
在尝试重新发送消息之前,您应该验证套接字...您可能会遇到套接字异常。
您可能应该得到回报;在 ReceiveComplete 的“if”块中调用 WaitForData 之后。
Timothy Pratley 上面说过,第二次通过时会在 bytesRecieved 中出现一个错误。每次您仅测量从该 EndReceived 接收的字节数,然后将其与 messageLength 进行比较。您需要保留所有已接收字节的总和。
您最大的错误是,在第一次调用 ReceiveComplete 时,您考虑到消息可能(很可能)包含比消息大小更多的数据 - 它可能也包含消息的一半。您需要去掉数据大小,然后将消息的其余部分存储在消息变量中。
As the others have said, don't use global variables here - use a class for socket state. Something like:
Before trying to resend the message, you should verify the socket... you might have a socket exception.
You should probably have a return; after your WaitForData call in the "if" block of ReceiveComplete.
Timothy Pratley said it above, one error will be in bytesRecieved the second time through. Each time you are only measuring the bytesReceived from that EndReceive and then comparing it to messageLength. You need to keep a sum of all bytesRecieved.
And your biggest error is that in your first call to ReceiveComplete, you take account for the fact that the message might (most likely) contain more data than just the size of the message - it will probably contain half the message as well. You need to peel off the data size, and then also store the rest of the message in your message variable.