最近我解决了.Net同步接收方法的奇怪行为。我需要编写一个具有通过发送/接收数据相互通信的节点的应用程序。每个服务器都有一个同步的接收循环,在接收到序列化类后,它会反序列化并处理它。之后,它将这个序列化类异步发送到一些选定的节点(使用AsynchSendTo)。
MSDN明确表示:
“如果您使用的是面向连接的Socket,则使用Receive方法 将读取尽可能多的数据,最大可达缓冲区的大小。 如果远程主机使用Shutdown关闭Socket连接 方法,并且已接收所有可用数据,即Receive方法 将立即完成并返回零字节。“
在我的情况下,这不是真的。在接收没有阻塞并且在建立连接之后立即返回0字节(非确定性的位置)时,存在一些随机情况。我百分百肯定发件人发送的是至少1000字节。另一个有趣的事实是:在接收Sleep(500)之前,一切正常。以下是接收代码:
_listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
_listener.Bind(_serverEndpoint);
_listener.Listen(Int32.MaxValue);
while (true)
{
Console.WriteLine("Waiting for connection...");
Socket handler = _listener.Accept();
int totalBytes = 0;
int bytesRec;
var bytes = new byte[DATAGRAM_BUFFER];
do
{
//Thread.Sleep(500);
bytesRec = handler.Receive(bytes, totalBytes, handler.Available, SocketFlags.None);
totalBytes += bytesRec;
} while (bytesRec > 0);
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
}
catch (SocketException e)
{
Console.WriteLine(e);
}
发送部分:
public void AsynchSendTo(Datagram datagram, IPEndPoint recipient)
{
byte[] byteDatagram = SerializeDatagram(datagram);
try
{
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.BeginConnect(recipient, ConnectCallback, new StateObject(byteDatagram, byteDatagram.Length, socket));
}
catch (SocketException e)
{
Console.WriteLine(e);
}
}
public void ConnectCallback(IAsyncResult result)
{
try
{
var stateObject = (StateObject)result.AsyncState;
var socket = stateObject.Socket;
socket.EndConnect(result);
socket.BeginSend(stateObject.Data, 0, stateObject.Data.Length, 0, new AsyncCallback(SendCallback), socket);
}
catch (Exception ex)
{
Console.WriteLine("catched!" + ex.ToString());
}
}
public void SendCallback(IAsyncResult result)
{
try
{
var client = (Socket)result.AsyncState;
client.EndSend(result);
client.Shutdown(SocketShutdown.Both);
client.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
class StateObject
{
public Byte[] Data { get; set; }
public int Size;
public Socket Socket;
}
我的问题:我是否以错误的方式使用同步接收?尽管有数据要接收,为什么它不阻止事件?
答案 0 :(得分:6)
你在脚下射击自己。
bytesRec = handler.Receive(bytes, totalBytes, handler.Available, SocketFlags.None);
在连接的最开始,Available
将为0,强制它立即返回0.相反,您应该指定缓冲区中可用的字节数(例如bytes.Length-totalBytes
),那么它也会阻止。
答案 1 :(得分:6)
这里可能存在并发问题。在您接受连接后,您直接跳到接收。发件人流程可能没有足够的时间来接收发送呼叫,因此您的handler.Available
为0且接收返回。
这也是添加500毫秒睡眠时不会出现“错误”的原因。