Разница между NetworkStream.Read () и NetworkStream.BeginRead ()? - PullRequest
8 голосов
/ 08 декабря 2010

Мне нужно прочитать с NetworkStream, что будет отправлять данные случайным образом, и размер пакетов данных также будет меняться.Я реализую многопоточное приложение, где каждый поток будет иметь свой собственный поток для чтения.Если в потоке нет данных, приложение должно продолжать ждать поступления данных.Однако, если сервер завершил отправку данных и завершил сеанс, он должен завершиться.

Изначально я использовал метод Read для получения данных из потока, но он использовал для блокировкипоток и продолжал ждать, пока данные не появились в потоке.

В документации по MSDN предлагается:

Если данные для чтения недоступны, метод Read возвращает 0. Если удаленный хост завершает соединение, и все доступные данные былиПолученный метод Read завершается немедленно и возвращает ноль байтов.

Но в моем случае у меня никогда не было метода Read, который возвращал бы 0 и изящно завершал работу.Это просто ждет бесконечно.

В моем дальнейшем исследовании я столкнулся с BeginRead, который наблюдает за потоком и асинхронно вызывает метод обратного вызова, как только он получает данные.Я также пытался найти различные реализации с использованием этого подхода, но я не смог определить, когда использование BeginRead будет выгодно, а не Read.

На мой взгляд, BeginRead имеет только то преимущество, что имеет асинхронный вызов, который не блокирует текущий поток.Но в моем приложении у меня уже есть отдельный поток для чтения и обработки данных из потока, так что для меня это не будет иметь большого значения.

  • Может кто-нибудь помочь мне разобраться с механизмом ожидания и выхода для BeginRead и чем он отличается от Read?

  • Чтобудет лучшим способом реализовать желаемую функциональность?

Ответы [ 4 ]

12 голосов
/ 08 декабря 2010

Я использую BeginRead, но продолжаю блокировать поток, используя WaitHandle:

byte[] readBuffer = new byte[32];
var asyncReader = stream.BeginRead(readBuffer, 0, readBuffer.Length, 
    null, null);

WaitHandle handle = asyncReader.AsyncWaitHandle;

// Give the reader 2seconds to respond with a value
bool completed = handle.WaitOne(2000, false);
if (completed)
{
    int bytesRead = stream.EndRead(asyncReader);

    StringBuilder message = new StringBuilder();
    message.Append(Encoding.ASCII.GetString(readBuffer, 0, bytesRead));
}

В основном это позволяет тайм-аут асинхронного чтения с использованием WaitHandle и дает логическое значение (completed), если чтение было завершено в установленное время (2000 в данном случае).

Вот мой полный код чтения потока, скопированный и вставленный из одного из моих проектов Windows Mobile:

private static bool GetResponse(NetworkStream stream, out string response)
{
    byte[] readBuffer = new byte[32];
    var asyncReader = stream.BeginRead(readBuffer, 0, readBuffer.Length, null, null);
    WaitHandle handle = asyncReader.AsyncWaitHandle;

    // Give the reader 2seconds to respond with a value
    bool completed = handle.WaitOne(2000, false);
    if (completed)
    {
        int bytesRead = stream.EndRead(asyncReader);

        StringBuilder message = new StringBuilder();
        message.Append(Encoding.ASCII.GetString(readBuffer, 0, bytesRead));

        if (bytesRead == readBuffer.Length)
        {
            // There's possibly more than 32 bytes to read, so get the next 
            // section of the response
            string continuedResponse;
            if (GetResponse(stream, out continuedResponse))
            {
                message.Append(continuedResponse);
            }
        }

        response = message.ToString();
        return true;
    }
    else
    {
        int bytesRead = stream.EndRead(asyncReader);
        if (bytesRead == 0)
        {
            // 0 bytes were returned, so the read has finished
            response = string.Empty;
            return true;
        }
        else
        {
            throw new TimeoutException(
                "The device failed to read in an appropriate amount of time.");
        }
    }
}
4 голосов
/ 08 декабря 2010

Асинхронный ввод / вывод может использоваться для достижения того же количества ввода / вывода в меньшем количестве потоков.

Как вы заметили, сейчас ваше приложение имеет один поток на поток.Это нормально с небольшим количеством соединений, но что, если вам нужно поддерживать 10000 сразу?В асинхронном вводе-выводе это больше не требуется, поскольку обратный вызов завершения чтения позволяет передавать контекст, идентифицируя соответствующий поток.Ваши чтения больше не блокируются, поэтому вам не нужен один поток на поток.

Независимо от того, используете ли вы синхронизацию или асинхронный ввод / вывод, существует способ обнаружить и обработать закрытие потока в соответствующих кодах возврата API. BeginRead должен завершиться с IOException, если сокет уже был закрыт.Закрытие, пока ваше асинхронное чтение ожидает, вызовет обратный вызов, и EndRead сообщит вам о состоянии воспроизведения.

Когда ваше приложение вызывает BeginRead, система будет ждать, пока данные не будут получены иливозникает ошибка, и затем система будет использовать отдельный поток для выполнения указанного метода обратного вызова и блокирует EndRead до тех пор, пока предоставленный NetworkStream не прочитает данные или не выдаст исключение.

0 голосов
/ 29 июля 2011

Вы пробовали сервер. Получите время? Вы можете установить время, в течение которого функция Read () будет ожидать поступления данных, прежде чем вернуть ноль. В вашем случае это свойство, вероятно, установлено где-то бесконечным.

0 голосов
/ 08 декабря 2010

BeginRead - это асинхронный процесс, который означает, что ваш основной поток начнет выполнять чтение в другом процессе. Итак, теперь у нас есть 2 параллельных процесса. если вы хотите получить результат, вам нужно вызвать EndRead, который даст результат.

немного псудо

BeginRead()
//...do something in main object while result is fetching in another thread
var result = EndRead();

но если вашему основному потоку больше нечего делать и вам нужен результат, вы должны вызвать Read.

...