简体   繁体   中英

C#: How many bytes to read from a NetworkStream of a chat using a Unicode encoding?

I've followed a tutorial of a chat using the ASCII encoding to send/receive messages, and it used to read 4096 bytes in a row to write on the chat client. But now I've changed the encoding to unicode and I know that Unicode use more bytes per character.

My code is the following:

taskOpenEndpoint = Task.Factory.StartNew(() =>
        {
            while (true)
            {
                serverStream = tcpClient.GetStream();
                byte[] message = new byte[4096];
                int bytesRead = 0;
                try
                {
                    bytesRead = serverStream.Read(message, 0, 4096);
                }
                catch (Exception ex)
                {
                    if(flag_chiusura == false)
                        MessageBox.Show(ex.Message);
                    return;
                }
                UnicodeEncoding encoder = new UnicodeEncoding();
                AddMessage(encoder.GetString(message, 0, bytesRead));
                Thread.Sleep(500);
            }
        });

Should I change the number of bytes read, now that I'm using the Unicode encoding?

Switching to Unicode certainly will have an impact on this code. It is buggy code, it assumes that you'll read a "packet" of data from the socket. This is only the case if you use a UDP socket, TCP sockets implement a stream . Where the number of bytes you read from a socket is entirely unpredictable and will not match the number of bytes transmitted at the other end of the network connection.

Your Thread.Sleep() call is a workaround of sorts. It is however an extremely evil kind of fix. For one, it is placed at the wrong spot, sleeping after you read instead of before you read. Much worse, it provides no guarantee that the bug is actually fixed, it the app on the other end of the wire also suffers a delay, when the machine is heavily loaded for example, then you still won't read a "packet". This doesn't happen nearly often enough to ever have a shot at debugging the problem. And that this is a problem on a machine that's a thousand miles away of course makes it entirely impossible.

So using Unicode increases the odds that your app will crash. The Encoder will throw when it is asked to decode a partial byte sequence.

You need to fix the bug. Delete the Thread.Sleep() call. Turn a TCP stream in a stream of packets by first transmitting the number of bytes in the packet. The receiver can now read the length and knows how often to loop, repeatedly calling Read() until it has received all the bytes. After this, decoding can of course never cause any trouble.

Something like the following would work without changing your protocol:

Note: It's good to have some notion of record/start/end other than just block size.

taskOpenEndpoint = Task.Factory.StartNew(() =>
    {
        UnicodeEncoding enc = new UnicodeEncoding(false, false, false);
        while (true)
        {
           serverStream = tcpClient.GetStream();
           byte []message = new byte[16384];
           int bytesRead = 0;
           try {

     // --------- first version may avoid blocking:
               while( encoder.GetCharCount(message, 0, bytesRead) < 4096 
                  && bytesRead < message.Length )
               {
                  if( serverStream.DataAvailable /* && !timed_out */ )
                      message[bytesRead++] = serverStream.ReadByte();
                  else
                      Thread.Sleep(10);  // Better to wait than sleep...
               }

     // --------- second version will fully block:  
               while( encoder.GetCharCount(message, 0, bytesRead) < 4096
                      && bytesRead < message.Length)
                   bytesRead += server.Read(
                        message, 
                        bytesRead, 
                        168384 - message.Length);

           catch (Exception ex)
           {
               if(flag_chiusura == false)
                   MessageBox.Show(ex.Message);
               return;
           }
           AddMessage(encoder.GetString(message, 0, bytesRead));
           Thread.Sleep(500);
        }
    });

It could be more efficient by figuring out GetByteCount(decoded) and only re-decoding the portion that hasn't already been fully decoded.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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