При каких условиях NetworkStream не считывает все данные одновременно?

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

Условие if (bytesRead < totalBytes) никогда не срабатывает, даже если одновременно отправляется много данных (тысячи символов) и даже если размер буфера установлен на очень маленькое значение (16 или около того).

Я не проверял это «старомодным способом», поскольку я использую Task.Factory.FromAsync вместо вызова NetworkStream.BeginRead и предоставления обратного вызова, когда я вызываю EndRead. Возможно, Задачи автоматически включают эту функцию, не возвращаясь, пока все данные не будут прочитаны? Я не уверен.

В любом случае, мне все еще любопытно, когда все данные не будут считаны сразу. Нужно ли вообще проверять, не все ли данные были прочитаны, и если да, то читать заново? Кажется, я не могу заставить условное выражение когда-либо запускаться.

Спасибо.


person Ryan Peschel    schedule 11.10.2011    source источник
comment
Это, безусловно, ненормальное поведение для NetworkStream, получение меньшего количества байтов, чем запрошено, очень распространено. Это все скорее зависит от пропускной способности соединения и от того, как быстро вы отвечаете на обратный вызов. Вы получаете все, что буферизовано в стеке драйверов tcp/ip.   -  person Hans Passant    schedule 11.10.2011


Ответы (1)


Попробуйте отправить мегабайты данных по медленному каналу. Почему поток должен ждать, пока он все не будет там, прежде чем передать вызывающему объекту что-либо из этого? Что, если бы другая сторона не закрыла соединение — в этот момент не существует понятия «все данные».

Предположим, вы открываете соединение с другим сервером и вызываете BeginRead (или Read) с большим буфером, но он отправляет только 100 байт, а затем ждет вашего ответа — что вы ожидаете от NetworkStream? Никогда не отдавать вам данные, потому что вы выделили для них слишком большой буфер? Это было бы крайне контрпродуктивно.

Вы не должны абсолютно предполагать, что какой-либо поток (за спорным исключением MemoryStream) заполнит предоставленный вами буфер. Возможно, FileStream всегда будет для локальных файлов, но я ожидаю, что это не так для общих файлов.

РЕДАКТИРОВАТЬ: Пример кода, который показывает, что буфер не заполняется - делает запрос HTTP 1.1 (довольно плохо :)

// Please note: this isn't nice code, and it's not meant to be. It's just quick
// and dirty to demonstrate the point.
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;

class Test
{
    static byte[] buffer;

    static void Main(string[] arg)
    {
        TcpClient client = new TcpClient("www.yoda.arachsys.com", 80);
        NetworkStream stream = client.GetStream();

        string text = "GET / HTTP/1.1\r\nHost: yoda.arachsys.com:80\r\n" +
            "Content-Length: 0\r\n\r\n";
        byte[] bytes = Encoding.ASCII.GetBytes(text);
        stream.Write(bytes, 0, bytes.Length);
        stream.Flush();

        buffer = new byte[1024 * 1024];
        stream.BeginRead(buffer, 0, buffer.Length, ReadCallback, stream);
        Console.ReadLine();
    }

    static void ReadCallback(IAsyncResult ar)
    {
        Stream stream = (Stream) ar.AsyncState;
        int bytesRead = stream.EndRead(ar);
        Console.WriteLine(bytesRead);
        Console.WriteLine("Asynchronous read:");
        Console.WriteLine(Encoding.ASCII.GetString(buffer, 0, bytesRead));
        string text = "Bad request\r\n";
        byte[] bytes = Encoding.ASCII.GetBytes(text);
        stream.Write(bytes, 0, bytes.Length);
        stream.Flush();
        Console.WriteLine();
        Console.WriteLine("Synchronous:");

        StreamReader reader = new StreamReader(stream);
        Console.WriteLine(reader.ReadToEnd());
    }
}
person Jon Skeet    schedule 11.10.2011