c#:使用Unicode编码从聊天的NetworkStream中读取多少字节



我遵循了一个使用ASCII编码发送/接收消息的聊天教程,它习惯于一行读取4096字节,然后写入聊天客户端。但是现在我已经将编码改为unicode,并且我知道unicode每个字符使用更多字节。

我的代码如下:
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);
            }
        });

我应该改变字节读取的数量,现在我使用Unicode编码?

切换到Unicode肯定会对这段代码产生影响。这是错误的代码,它假设您将从套接字读取数据的"数据包"。只有在使用UDP套接字时才会出现这种情况,TCP套接字实现。其中从套接字读取的字节数是完全不可预测的,并且与网络连接另一端传输的字节数不匹配。

你的Thread.Sleep()调用是一种变通方法。然而,这是一种极其邪恶的修复方法。首先,它被放置在错误的位置,在你阅读之后睡觉,而不是在你阅读之前。更糟糕的是,它不能保证漏洞已经被修复,如果网络另一端的应用程序也出现延迟,比如当机器负载过重时,那么你仍然无法读取"数据包"。这种情况并不经常发生,因此我们没有机会调试这个问题。而这个问题发生在千里之外的一台机器上,这当然使它完全不可能。

所以使用Unicode会增加你的应用程序崩溃的几率。当要求Encoder解码部分字节序列时将抛出。

你需要修复这个bug。删除Thread.Sleep()调用。通过首先传输数据包中的字节数,将TCP流转换为数据包流。接收方现在可以读取长度并知道循环的频率,反复调用read()直到接收到所有字节。在此之后,解码当然不会造成任何麻烦。

不需要更改协议就可以使用下面的代码:

注意:有一些记录/开始/结束的概念是很好的,而不仅仅是块大小。

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);
        }
    });

可以通过计算GetByteCount(decoded)并且只重新解码尚未完全解码的部分来更有效。

最新更新