Как определить, когда в NetworkStream больше нет данных для чтения? - PullRequest
3 голосов
/ 21 сентября 2008

У меня есть веб-приложение, которое подключается к серверу по TCP-соединению и читает двоичный документ, который затем записывается в его объект ответа. Другими словами, он передает файл с внутреннего сервера, используя собственный протокол, и возвращает этот файл своему клиенту через HTTP.

Сервер отправляет код состояния и тип MIME, который я успешно прочитал, а затем записывает содержимое файла и закрывает сокет. Кажется, это работает нормально.

Клиент (веб-приложение на C #) читает данные:

     private NetworkStream stream_;

     public void WriteDocument(HttpResponse response)
     {
        while (stream_.DataAvailable)
        {
           const int bufsize = 4 * 1024;
           byte[] buffer = new byte[bufsize];
           int nbytes = stream_.Read(buffer, 0, bufsize);
           if (nbytes > 0)
           {
              if (nbytes < bufsize)
                 Array.Resize<byte>(ref buffer, nbytes);
              response.BinaryWrite(buffer);
           }
        }
        response.End();
     }

Кажется, это всегда выходит из цикла чтения до того, как все данные поступят. Что я делаю не так?

Ответы [ 4 ]

3 голосов
/ 21 сентября 2008

Я бы использовал OutputStream напрямую с универсальной функцией. С помощью Stream вы можете управлять Flush.

    public void WriteDocument(HttpResponse response) {
        StreamCopy(response.OutputStream, stream_);
        response.End();
    }

    public static void StreamCopy(Stream dest, Stream src) {
        byte[] buffer = new byte[4 * 1024];
        int n = 1;
        while (n > 0) {
            n = src.Read(buffer, 0, buffer.Length);
            dest.Write(buffer, 0, n);
        }
        dest.Flush();
    }
2 голосов
/ 21 сентября 2008

Вот что я делаю. Обычно длина контента желательна, чтобы знать, когда заканчивать цикл хранения данных. Если ваш протокол не отправляет объем данных, ожидаемый в качестве заголовка, он должен отправить некоторый маркер, чтобы сообщить об окончании передачи.

Свойство DataAvailable просто сигнализирует, есть ли данные для чтения из сокета СЕЙЧАС, оно не (и не может) знать, есть ли еще данные для отправки или нет. Чтобы проверить, что сокет все еще открыт, вы можете проверить на stream_.Socket.Connected && stream_.Socket.Readable

    public static byte[] doFetchBinaryUrl(string url)
    {
        BinaryReader rdr;
        HttpWebResponse res;
        try
        {
            res = fetch(url);
            rdr = new BinaryReader(res.GetResponseStream());
        }
        catch (NullReferenceException nre)
        {
            return new byte[] { };
        }
        int len = int.Parse(res.GetResponseHeader("Content-Length"));
        byte[] rv = new byte[len];
        for (int i = 0; i < len - 1; i++)
        {
            rv[i] = rdr.ReadByte();
        }
        res.Close();
        return rv;
    }
1 голос
/ 30 октября 2008

Корень вашей проблемы в этой строке:

while (stream_.DataAvailable)

DataAvailable просто означает, что в буфере потока есть данные, готовые для чтения и обработки. Это не гарантирует, что «конец» потока достигнут. В частности, DataAvailable может быть ложным, если есть какая-либо пауза в передаче или если ваш отправитель медленнее, чем ваш читатель.

1 голос
/ 21 сентября 2008

Не уверен, как все работает в .Net, но в большинстве сред, в которых я работал, Read () возвращает 0 байтов при закрытии соединения Так что вы бы сделали что-то вроде:

char buffer[4096];
int num_read;

while ( num_read = src.Read(sizeof(buffer)) > 0 )
{
   dst.Write(buffer, num_read);
}
...