TcpClient.GetStream().DataAvailable returns false, but stream has more data

后端 未结 5 2067
天涯浪人
天涯浪人 2020-12-05 15:50

So, it would seem that a blocking Read() can return before it is done receiving all of the data being sent to it. In turn we wrap the Read() with a loop that is controlled b

5条回答
  •  心在旅途
    2020-12-05 16:40

    When I have this code:

        var readBuffer = new byte[1024];
        using (var memoryStream = new MemoryStream())
        {
            do
            {
                int numberOfBytesRead = networkStream.Read(readBuffer, 0, readBuffer.Length);
                memoryStream.Write(readBuffer, 0, numberOfBytesRead);
            }
            while (networkStream.DataAvailable);
        }
    

    From what I can observe:

    • When sender sends 1000 bytes and reader wants to read them. Then I suspect that NetworkStream somehow "knows" that it should receive 1000 bytes.
    • When I call .Read before any data arrives from NetworkStream then .Read should be blocking until it gets more than 0 bytes (or more if .NoDelay is false on networkStream)
    • Then when I read first batch of data I suspect that .Read is somehow updating from its result the counter of those 1000 bytes at NetworkStream and before this happens I suspect, that in this time the .DataAvailable is set to false and after the counter is updated then the .DataAvailable is then set to correct value if the counter data is less than 1000 bytes. It makes sense when you think about it. Because otherwise it would go to the next cycle before checking that 1000 bytes arrived and the .Read method would be blocking indefinitely, because reader could have already read 1000 bytes and no more data would arrive.
    • This I think is the point of failure here as already James said:

    Yes, this is just the way these libraries work. They need to be given time to run to fully validate the data incoming. – James Apr 20 '16 at 5:24

    • I suspect that the update of internal counter between end of .Read and before accessing .DataAvailable is not as atomic operation (transaction) so the TcpClient needs more time to properly set the DataAvailable.

    When I have this code:

        var readBuffer = new byte[1024];
        using (var memoryStream = new MemoryStream())
        {
            do
            {
                int numberOfBytesRead = networkStream.Read(readBuffer, 0, readBuffer.Length);
                memoryStream.Write(readBuffer, 0, numberOfBytesRead);
    
                if (!networkStream.DataAvailable)
                    System.Threading.Thread.Sleep(1); //Or 50 for non-believers ;)
            }
            while (networkStream.DataAvailable);
        }
    

    Then the NetworkStream have enough time to properly set .DataAvailable and this method should function correctly.

    Fun fact... This seems to be somehow OS Version dependent. Because the first function without sleep worked for me on Win XP and Win 10, but was failing to receive whole 1000 bytes on Win 7. Don't ask me why, but I tested it quite thoroughly and it was easily reproducible.

提交回复
热议问题