🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Reading from network stream (C#)

Started by
8 comments, last by SyncViews 5 years, 3 months ago

Hi, 

I am having a problem reading data from a network. Here is the code of the thread that listening from a thread and saving the content as string. 


private void ReadData()
    {
        string lMessageInString;

        print("Reading data");


        byte[] lInStream = new byte[1024];
        char[] lCharStream = new char[1024];

        serverStream = clientSocket.GetStream();

        while (true)
        {
            print("While True");

            serverStream.Read(lInStream, 0, lInStream.Length);

            for (int i = 0; i < lInStream.Length; i++)
            {
                lCharStream[i] = (char)(((int)lInStream[i]));
            }

            lMessageInString = new string(lCharStream);

            print("The message length: " + lMessageInString.Length);

            ProcessMessage(lMessageInString);

            print(lMessageInString);
        }

    }

The code seems to work but I have a problem trying to optimize the code. THe size of the string (which is derived from the array of char) is 1024 instead of the size of the character sent by the network. Using method serverStream.Length seems to fail the program. How can I limit the string length to match the size of the received characters from the stream? ie. if there are only 10 characters (bytes), the string will have 10 characters instead of the maximum 1024. It seems redundant to process the whole array when the stream only sent 10 characters. 

 

Thank you in advance

Advertisement

Is clientSocket a TCP connection? TCP doesn't packetize data, writing to a TCP stream is the same as writing to a file.If you want to know how many bytes to read, you need to first write the count of bytes (encoded as a known number of bytes) and then write that many bytes.

Because the number of bytes received on the other end will not be 1:1 with writing, you also need to buffer everything received until you have "enough." For example, if you encode length of packets as 2 bytes, and thus write 2 bytes of length, and then write 480 bytes of payload data, the receiving end may see any of the following:

- one clump of 482 bytes
- one clump of 1 byte, one clump of 7 bytes, one clump of 474 bytes
- one clump of 2 bytes, one clump of 480 bytes

If you keep writing those packets, it of course gets worse, because you may get some fraction of two different packets (end of one, count field, start of next) in a single receive call.

Thus, you need to keep one buffer of bytes that's as big as the max possible packet, plus size field, and a count of how much of that buffer is used. Then when you call receive, receive into the "rest" of the buffer, counting how much you actually got, and update the "available" counter. Then, if "available" is greater than the size of your packet length field (2 bytes in this example) you decode the length (without removing those bytes) and check whether you have additional data to cover that length. If THAT is true, you remove the received length+packet from the buffer and handle it, keeping whatever is left and checking for another received packet, until there's no full packet left in the buffer. Then you go back to receiving.

Separately, the "Length" on a fixed array in C# is always the size of the array. If you want to know how many of those elements are "used" you need to keep your own counter, and update it appropriately.

enum Bool { True, False, FileNotFound };

All Stream.Read calls (even non-TCP ones) should be written so that they are capable of handling the case where the return value is less than the desired number of bytes.  You should assume that this type of thing can happen on any Stream type.

See the example on Stream.Read for one way to write a loop like this:

https://docs.microsoft.com/en-us/dotnet/api/system.io.stream.read?view=netframework-4.7.2

14 hours ago, jt.tarigan said:

Using method serverStream.Length seems to fail the program.

You should be seeing an exception for this, "fail the program" is not generally useful for debugging. Are you catching the exceptions? Are you running this code in Visual Studio or some other IDE? If the exception is not caught it should give you a popup, otherwise I generally find it useful to go to the "Exceptions" window and have it break on all exceptions to help catch issues sooner with as much stack context as possible.

In this case, from the documentation for NetworkStream you should see it is not seekable and you should be seeing a "NotSupportedException". https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.networkstream?view=netframework-4.7.2

Quote

The NetworkStream does not support random access to the network data stream. The value of the CanSeek property, which indicates whether the stream supports seeking, is always false; reading the Position property, reading the Length property, or calling the Seek method will throw a NotSupportedException.

 

Thanks for the replies. I think I have a rough idea of how to solve this problem. In short, there is no way to know the length of the byte stream received. What I can do is to modify my protocol so the first thing I put in the data is the length of the data. When the message is received, I should check the length first before going through the data. Is that correct?

I'm using Unity + Visual Studio. I am debugging with Unity's built-in console, it does not help very much in showing errors. Sometimes, it just stopped without showing anything. 

it does not help very much in showing errors

You can wrap some of your outer functions in try/catch that catch exceptions, log them with information you need, and then re-throw them.

enum Bool { True, False, FileNotFound };
8 hours ago, jt.tarigan said:

I'm using Unity + Visual Studio. I am debugging with Unity's built-in console, it does not help very much in showing errors. Sometimes, it just stopped without showing anything. 

Debugging in Visual Studio proper should be possible. Breakpoints, variable inspection, single step, etc.

image.png.4edbca29101fc7c9cf3706fa9f9f0601.png

 

Apparently Unity made a change so you don't get unhandled exceptions, but catching them on throw or any other sort of breakpoint should work.

Quote

https://developercommunity.visualstudio.com/content/problem/230762/unity-debugger-didnt-break-on-exception-with-net-4.html

With the new runtime (.NET 4.6 equivalent), Unity introduced a new way for managing user exceptions and as a result, all exceptions are seen as "user-handled" even if they are outside a try/catch block. That's why you now need to explicitly check them in the Exception Settings Window if you want the debugger to break.

 

10 hours ago, jt.tarigan said:

 I think I have a rough idea of how to solve this problem. In short, there is no way to know the length of the byte stream received.

No, there is of course, it is the return value of Read() fuction, that you're not using up.

2 hours ago, JohnnyCode said:

No, there is of course, it is the return value of Read() fuction, that you're not using up.

The return value is an important part, but you can't use it to directly determine TCP "message size". It could be part of the message or maybe you read part of two messages. Even in a directly single command-response scheme with no pipe-lining, with that alone you don't know if a message is "complete" or not without some long timeout or dropping the connection (as original HTTP did for responses of unknown length), and then actual connection drops can mess with it (incomplete download, no way to tell).

This topic is closed to new replies.

Advertisement