如何确定何时在NetworkStream中没有其他数据要读取?

时间:2020-03-06 14:30:27  来源:igfitidea点击:

我有一个Web应用程序,该应用程序使用TCP连接连接到服务器,并读取一个二进制文档,然后将其写入其响应对象。换句话说,它使用自定义协议从后端服务器传输文件,然后通过HTTP将文件返回给客户端。

服务器发送一个状态代码和一个mime类型,我成功读取了该状态代码,然后写入了文件的内容并关闭了套接字。这似乎工作正常。

客户端(一个Cweb应用程序)读取数据:

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

这似乎总是在所有数据到达之前退出读取循环。我究竟做错了什么?

解决方案

这是我的工作。通常,希望知道内容长度以何时结束数据存储循环。如果协议没有发送期望作为报头的数据量,则它应该发送一些标记来表示传输结束。

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

我会直接将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();
    }

不知道在.Net中如何工作,但是在大多数环境中,当连接关闭时,我在Read()中工作的返回0字节。因此,我们将执行以下操作:

char buffer[4096];
int num_read;

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

我们问题的根源是这一行:

while (stream_.DataAvailable)

DataAvailable只是意味着流缓冲区中有准备读取和处理的数据。它不能保证已达到流的"终点"。特别是,如果传输中有任何暂停,或者发件人比阅读器慢,则DataAvailable可以为false。