Синхронный прием .NET C # не блокируется - PullRequest
6 голосов
/ 17 ноября 2011

Недавно я рассмотрел странное поведение метода синхронного приема .Net.Мне нужно было написать приложение, в котором есть узлы, которые общаются друг с другом, отправляя / получая данные.Каждый сервер имеет цикл приема, который является синхронным, после получения сериализованного класса он десериализуется и обрабатывает его.После этого он отправляет этот сериализованный класс асинхронно некоторым выбранным узлам (используя AsynchSendTo).

MSDN четко говорит, что:

"Если вы используете Socket-ориентированный сокет,Метод Receive будет считывать столько данных, сколько доступно, вплоть до размера буфера.Если удаленный хост завершает соединение Socket с помощью метода Shutdown и все доступные данные были получены, метод Receive завершится немедленно и вернет ноль.байт. "

В моем случае это не так.Есть несколько случайных случаев, когда Receive не блокируется и возвращает 0 байтов (недетерминированная ситуация) сразу после установления соединения.Я на 100% уверен, что отправитель отправлял как минимум 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;
}

Мой вопрос: неправильно ли я использую синхронный прием?Почему он не блокирует событие, хотя есть данные для получения?

Ответы [ 2 ]

6 голосов
/ 17 ноября 2011

У вас может быть проблема с параллелизмом.После того, как вы принимаете соединение, вы сразу переходите к получению.Процесс отправителя может не иметь достаточно времени, чтобы дозвониться до отправляемого вызова, поэтому ваш handler.Available равен 0, а получение возвращается.

По этой же причине «ошибка» не возникает при добавлении спящего режима500 мс.

6 голосов
/ 17 ноября 2011

Ты стреляешь себе в ногу.

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

В самом начале соединения, Available будет 0, что заставит его немедленно вернуться с 0. Вместо этого, вы должны указать количество байтов, которые свободны в вашем буфере (например, bytes.Length-totalBytes), затем также заблокирует.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...