简体   繁体   English

C# NetworkStream.Read() 会等到指定数量的数据被读取吗?

[英]Will C# NetworkStream.Read() wait until the specified amount of data is read?

In C/C++ read() on regular non-blocking network sockets will return immediately with the amount of data currently available in the buffer, up to the amount specified (so if we ask for 16 bytes and there are only 8 available at the moment, those 8 we'll get and it's up to us to call read() again and fetch all data).在 C/C++ 中,常规非阻塞网络套接字上的 read() 将立即返回缓冲区中当前可用的数据量,最多为指定的量(因此,如果我们要求 16 个字节而目前只有 8 个可用字节) ,我们将得到那 8 个,由我们再次调用 read() 并获取所有数据)。

In C# there's NetworkStream, which has built-in timeouts - does this mean that NetworkStream.Read() waits until either the timeout is reached or the amount of data requested is read, or will it give us any amount of data currently available in the buffer larger than 0 up to the amount requested (as the standard sockets do) even if there's time left?在 C# 中,有内置超时的 NetworkStream - 这是否意味着 NetworkStream.Read() 等待直到达到超时或读取请求的数据量,或者它会为我们提供当前可用的任何数据量缓冲区大于 0 直到请求的数量(如标准套接字所做的那样),即使还有时间?

It will read available data up to the number of bytes specified in the parameters, as described on MSDN , unless the stream is closed via timeout or other exception.除非流因超时或其他异常关闭,否则它将读取参数中指定的字节数以内的可用数据,如MSDN 中所述。

The Read operation reads as much data as is available, up to the number of bytes specified by the size parameter. Read 操作读取尽可能多的可用数据,最多可达 size 参数指定的字节数。 If the remote host shuts down the connection, and all available data has been received, the Read method completes immediately and return zero bytes.如果远程主机关闭连接,并且已接收到所有可用数据,则 Read 方法立即完成并返回零字节。

I solved it like this:我是这样解决的:

        byte[] TotalData = new byte[0];
        byte[] TempData = new byte[0];
        using (TcpClient TCPClient = new TcpClient())
        {
            try
            {
                TCPClient.Connect(somehost, someport);
            }
            catch (Exception eee)
            {
                // Report the connection failed in some way if necessary
            }
            if (TCPClient.Connected)
            {
                using (NetworkStream clientStream = TCPClient.GetStream())
                {
                    // You can reduce the size of the array if you know 
                    // the data received is going to be small, 
                    // don't forget to change it a little down too

                    byte[] TCPBuffer = new byte[524288];
                    int bytesRead = 0;

                    int loop = 0;
                    // Wait for data to begin coming in for up to 20 secs
                    while (!clientStream.DataAvailable && loop< 2000)
                    {
                        loop++;
                        Thread.Sleep(10);
                    }
                    // Keep reading until nothing comes for over 1 sec
                    while (clientStream.DataAvailable)
                    {
                        bytesRead = 0;

                        try
                        {
                            bytesRead = clientStream.Read(TCPBuffer, 0, 524288);
                            Array.Resize(ref TempData, bytesRead);
                            Array.Copy(TCPBuffer, TempData, bytesRead);
                            // Add data to TotalData
                            TotalData = JoinArrays(TotalData, TempData);
                        }
                        catch
                        {
                            break;
                        }

                        if (bytesRead == 0)
                            break;

                        Thread.Sleep(1000);
                    }
                }
            }
        }

The JoinArrays() method: JoinArrays() 方法:

byte[] JoinArrays(byte[] arrayA, byte[] arrayB)
{
    byte[] outputBytes = new byte[arrayA.Length + arrayB.Length];
    Buffer.BlockCopy(arrayA, 0, outputBytes, 0, arrayA.Length);
    Buffer.BlockCopy(arrayB, 0, outputBytes, arrayA.Length, arrayB.Length);
    return outputBytes;
}

The safe method is to use MemoryStream wich will make sure to wait and read all the stream to the memory , then u can use it as you like安全的方法是使用 MemoryStream ,它会确保等待并将所有流读取到内存中,然后您可以随意使用它

     public void SaveUserTemplate(Stream stream)
     {
        MemoryStream memoryStream = new MemoryStream();
        stream.CopyTo(memoryStream);
        memoryStream.Position = 0;

      
        byte[] templatePathLength = new byte[4];
        memoryStream.Read(templatePathLength, 0, templatePathLength.Length);
        int nBytesTemplatePathLength = BitConverter.ToInt32(templatePathLength,0);

        ....

CopyTo function finally calls to this function: CopyTo 函数最终调用了这个函数:

github.com/microsoft/referencesource github.com/microsoft/referencesource

private void InternalCopyTo(Stream destination, int bufferSize)
    {
        Contract.Requires(destination != null);
        Contract.Requires(CanRead);
        Contract.Requires(destination.CanWrite);
        Contract.Requires(bufferSize > 0);
        
        byte[] buffer = new byte[bufferSize];
        int read;
        while ((read = Read(buffer, 0, buffer.Length)) != 0)
            destination.Write(buffer, 0, read);
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM