简体   繁体   English

.NET C#同步接收不会阻止

[英].NET C# Synchronous Receive doesn't block

Recently I have tackled a strange behaviour of .Net synchronous receive method. 最近我解决了.Net同步接收方法的奇怪行为。 I needed to write an application that has nodes which communicate with each other by sending/receiving data. 我需要编写一个具有通过发送/接收数据相互通信的节点的应用程序。 Each server has a receipt loop which is synchronous, after receiving a serialized class it deserializes and processes it. 每个服务器都有一个同步的接收循环,在接收到序列化类后,它会反序列化并处理它。 After that it sends asynchronously this serialized class to some chosen nodes (using AsynchSendTo). 之后,它将这个序列化类异步发送到一些选定的节点(使用AsynchSendTo)。

The MSDN clearly says that: MSDN清楚地说:

"If you are using a connection-oriented Socket, the Receive method will read as much data as is available, up to the size of the buffer. If the remote host shuts down the Socket connection with the Shutdown method, and all available data has been received, the Receive method will complete immediately and return zero bytes." “如果您使用的是面向连接的套接字,则Receive方法将读取尽可能多的数据,最大可达缓冲区大小。如果远程主机使用Shutdown方法关闭Socket连接,并且所有可用数据都有收到后,Receive方法将立即完成并返回零字节。“

In my case it's not true. 在我的情况下,这不是真的。 There are some random cases when the Receive doesn't block and returns 0 bytes (non-deterministic situtation) right away after establishing connection. 在接收没有阻塞并且在建立连接之后立即返回0字节(非确定性的位置)时,存在一些随机情况。 I'm 100% sure that the sender was sending at lest 1000 bytes. 我百分百肯定发件人发送的是至少1000字节。 One more funny fact: when putting Sleep(500) before receive everything works just fine. 另一个有趣的事实是:在接收Sleep(500)之前,一切正常。 Hereunder is the receiving code: 以下是接收代码:

_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);
}

Also the sending part: 发送部分:

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;
}

My question: am I using the synchronous receive in a wrong way? 我的问题:我是否以错误的方式使用同步接收? Why it doesn't block event though there is data to receive? 尽管有数据要接收,为什么它不会阻止事件?

You're shooting yourself in the foot. 你在脚下射击自己。

bytesRec = handler.Receive(bytes, totalBytes, handler.Available, SocketFlags.None);

At the very beginning of the connection, Available will be 0, forcing it to return immediately with 0. Instead, you should specify the number of bytes which are free in your buffer (eg bytes.Length-totalBytes ), then it will also block. 在连接的最开始, Available将为0,强制它立即返回0.相反,您应该指定缓冲区中可用的字节数(例如bytes.Length-totalBytes ),然后它也将阻塞。

You may have a concurrency problem here. 您可能在这里遇到并发问题。 After you accept a connection, you jump straight into receive. 在您接受连接后,您直接跳到接收。 The sender process may not have enough time to reach the call to send and so your handler.Available is 0 and the receive returns. 发送方进程可能没有足够的时间来发送调用以及您的handler.Available为0并且接收返回。

This is also why the "bug" does not occur when you add the sleep of 500 ms. 这也是添加500毫秒睡眠时不会出现“错误”的原因。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM