C#Socket.BeginReceive/EndReceive
Socket.BeginReceive/EndReceive函数的调用顺序是什么 例如,我调用BeginReceive两次,一次获取消息长度,第二次获取消息本身。现在的场景是这样的,对于我发送的每一条消息,我都开始等待它的完成(实际上是对发送的消息的确认,我也在收到确认后等待操作的完成),因此我用每个BeginReceive调用BeginReceive,但是在每个BeginReceive的回调中,我检查是否接收到长度或消息。如果我正在接收该消息,并且已完全接收到该消息,那么我将调用另一个BeginReceive来接收该操作的完成情况。这就是事情不同步的地方。因为我的一个接收回调正在接收字节,它将字节解释为消息的长度,而实际上是消息本身 现在我该如何解决它 编辑:这是一个C#NET问题:) 这是代码,基本上太大了,很抱歉C#Socket.BeginReceive/EndReceive,c#,.net,sockets,beginreceive,C#,.net,Sockets,Beginreceive,Socket.BeginReceive/EndReceive函数的调用顺序是什么 例如,我调用BeginReceive两次,一次获取消息长度,第二次获取消息本身。现在的场景是这样的,对于我发送的每一条消息,我都开始等待它的完成(实际上是对发送的消息的确认,我也在收到确认后等待操作的完成),因此我用每个BeginReceive调用BeginReceive,但是在每个BeginReceive的回调中,我检查是否接收到长度或消息。如果我正在接收该消息,并且已完全接收到该消息,那么我将调用另一个Begi
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);
}
}
通常,BeginXXX方法表示异步操作,并且您似乎希望以同步方式执行该操作
如果确实需要同步客户端/服务器,这可能会有所帮助,时间顺序应为:
BeginReceive
用于消息长度EndReceive
用于完成#1BeginReceive
用于消息正文EndReceive
用于完成#3var 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
在完成委托访问中)可以使事情变得更简单,但确实需要从命令式代码的线性思维转变为完全采用事件驱动的方法
2012年增编: .NET 4.5版本 有了C#5中的异步支持,就有了一个新选项。这使用编译器从内联代码生成手动延续(单独的回调方法)和闭包(状态)。但是,有两件事需要解决:
System.Net.Sockets.Socket
有各种…Async
方法,这些方法用于基于事件的异步模式,而不是C#5的等待使用的基于任务的模式。解决方案:使用TaskFactory.fromsync
从Begin…
结束…
对获取单个任务
TaskFactory.fromsync
仅支持向Begin…
传递最多三个附加参数(除了回调和状态)。解决方案:接受零个附加参数的lambda具有正确的签名,而C#将给我们传递参数的正确闭包
消息
是另一种类型,它处理从初始发送的以固定字节数编码的长度转换为内容缓冲区的长度,然后再将内容字节转换为内容缓冲区的长度,这一点更为充分):
private async Task ReceiveAMessage(){
var prefix=新字节[Message.PrefixLength];
var revcLen=wait Task.Factory.fromsync(
(cb,s)=>clientSocket.BeginReceive(前缀,0,前缀.Length,SocketFlags.None,cb,s),
ias=>clientSocket.EndReceive(ias),
无效);
if(revcLen!=prefix.Length){throw new ApplicationException(“接收前缀失败”);}
int contentLength=Message.GetLengthFromPrefix(前缀);
var content=新字节[contentLength];
revcLen=wait Task.Factory.fromsync(
(cb,s)=>clientSocket.BeginReceive(content,0,content.Length,SocketFlags.None,cb,s),
ias=>clientSocket.EndReceive(ias),
无效);
if(revcLen!=content.Length){抛出新的ApplicationException(“接收内容失败”);}
返回新消息(内容);
}
也许您想做的是将您的回访链接起来:
伪代码:
// 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),-)
}
请参阅:有关正确的示例,请描述所发送消息的结构
只要您只有一个BeginReceive()未完成,它就会完成,并提供下一个可用字节的数据。如果您同时有多个未完成项,则所有赌注都将被取消,因为.net不保证按任何给定顺序完成。正如其他人所说,不要在此处使用全局变量-使用套接字状态类。比如:
public class StateObject
{
public const int DEFAULT_SIZE = 1024; //size of receive buffer
public byte[] buffer = new byte[DEFAULT_SIZE]; //receive buffer
public int dataSize = 0; //data size to be received
public bool dataSizeReceived = false; //received data size?
public StringBuilder sb = new StringBuilder(); //received data String
public int dataRecieved = 0;
public Socket workSocket = null; //client socket.
public DateTime TimeStamp; //timestamp of data
} //end class StateObject
在尝试重新发送消息之前,您应该验证套接字。。。您可能有套接字异常
你可能会有回报;在您的WaitForData调用ReceiveComplete的“if”块后
蒂莫西·普拉特利(Timothy Pratley)在上文中说过,一个错误将在字节内通过第二次接收。每次您只测量从该EndReceive接收的字节数,然后将其与messageLength进行比较。你需要把收到的所有字节都记下来
您最大的错误是,在第一次调用ReceiveComplete时,您考虑了这样一个事实,即消息可能(很可能)包含的数据多于消息的大小—它也可能包含一半的消息。您需要剥离数据大小,然后将消息的其余部分存储在消息变量中。您可以添加代码/数据示例吗?主要问题似乎是(消息长度大小-bytesReceived),这在第一次运行时会很好,但之后将不是正确的消息长度大小,因为bytesReceived不再是0。使用globa有点难看
public class StateObject
{
public const int DEFAULT_SIZE = 1024; //size of receive buffer
public byte[] buffer = new byte[DEFAULT_SIZE]; //receive buffer
public int dataSize = 0; //data size to be received
public bool dataSizeReceived = false; //received data size?
public StringBuilder sb = new StringBuilder(); //received data String
public int dataRecieved = 0;
public Socket workSocket = null; //client socket.
public DateTime TimeStamp; //timestamp of data
} //end class StateObject