Jump to content
  • Advertisement
Sign in to follow this  
jt.tarigan

Reading from network stream (C#)

Recommended Posts

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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Posted (edited)

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

Edited by Nypyren

Share this post


Link to post
Share on other sites
Posted (edited)
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.

 

Edited by SyncViews

Share this post


Link to post
Share on other sites

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. 

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites
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.

 

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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).

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!