什么时候关闭TcpClient和NetworkStream?

4

我正在编写一个客户端应用程序(Windows服务),定期从服务器读取数据并向服务器写入数据。如果服务器收到的帧被理解,服务器将配置为始终回复客户端。我有以下方法用于发送和接收:

 public byte[] Sendmessage(byte[] arrbMessage)
    {
        byte[] arrbDataInput;                                               // byteArray for received data

        try
        {
            _oStream = _oClient.GetStream();                                // try to get a networkstream
        }
        catch (InvalidOperationException e)
        {
            Connect();                                                      // if this fails, tcpclient is probably disconnected, reconnect client and networstream
        }

        if (_oClient.Connected)
            try
            {                                                               // Send the arrbMessage to the connected TcpServer.
                string sKey = "123456789ABC";
                byte[] arrbMessageEncrypted = EncryptedFrame(arrbMessage, sKey);                

                if (_oStream.CanWrite)                                      // if stream is available for writing
                {
                    _oStream.Write(arrbMessageEncrypted, 0, arrbMessageEncrypted.Length);     //send message
                    _oStream.Flush();                                       //Clear stream
                }
                // Receive the TcpServer.response. 
                if (_oStream.CanRead)                                       // if stream is available for reading
                {
                    arrbDataInput = new byte[256];                          // set inputbuffer to 256
                    //_oClient.NoDelay = true;                                // don't wait if nothing is received
                    // Read the first batch of the TcpServer response bytes.
                    _oStream.ReadTimeout = 2000;
                    Int32 bytes = _oStream.Read(arrbDataInput, 0, arrbDataInput.Length); //read out data, put datalength in "bytes"
                    Array.Resize(ref arrbDataInput, bytes);                 // resize array to length of received data

                    _oStream.Close();                                       // close the network stream

                    if (arrbDataInput.Length > 0)
                    {
                        byte[] arrbMessageDecrypted = DecryptedFrame(arrbDataInput, sKey);

                        if (CheckBusy(arrbMessageDecrypted))
                        throw new ArgumentNullException();

                        return arrbMessageDecrypted;
                    }
                    return null;                                   // return the received data
                }
            }
            catch (ArgumentNullException e)
            {
                return Sendmessage(arrbMessage);
            }
            catch (SocketException e)
            {
            }
            catch (System.IO.IOException e)
            {
                while (!_oClient.Connected)
                {
                    Connect();
                }
            }
        else
        {
            while (!_oClient.Connected)
            {
                Connect();
            }
        }
        return null;
    }

我在保持数据流开放时遇到了很多困难,因此我们目前在发送和接收数据后每次都关闭它。我应该保持数据流和tcpclient开放吗?这个函数是经常被调用的。

1个回答

4

我曾经参与开发一个应用程序,其中NetworkStream在应用程序启动时打开,并仅在以下情况下关闭:

  • 应用程序被关闭 - (该应用程序通常连续运行数月)。
  • 网络连接丢失 - (非常可靠的千兆以太网+100+Mbps MPLS),超时后tcpClient.Connected属性将返回false,我们关闭NetworkStreamTcpClient。然后我们启动每秒钟检查服务器可用性的计时器,一旦找到服务器,就重新连接,从而打开TcpClientNetworkStream
  • 服务器正在关闭 - (非常非常罕见)服务器发送断开信号,导致客户端应用程序关闭NetworkStreamTcpClient并开始轮询线程以检查服务器的可用性。

我们没有观察到保持NetworkStreamTcpClient打开会出现任何问题。也许代码的其他部分可能会引起问题。


不相关,但有建议:当你从NetworkStream中读取数据时,你只读取了256个字节;如果数据超过256个字节怎么办?

我建议为每组数据使用一些分隔符;例如,如果你的加密系统生成Base64哈希值,你可以安全地使用';'(分号)作为数据分隔符。(我们正在使用\n作为命令分隔符),但这完全取决于你的情况。

此外,使用以下类型的逻辑来读取和存储接收到的字符串,在分隔符字符可用时解密和执行。这将确保您永远不会接收到部分字符串并尝试对其进行解密。

string allPendingCommands = "";
string commandSeparator = ";";  // your command separator character here

while(tcpClient.Connected)
{
   if (!networkStream.DataAvailable)
        System.Threading.Thread.Sleep(100);
        // you can change it depending on the frequency of availability of data

   // read 256 bytes into you array 
   // convert the byte[] to string
   // add the newly read text to the text remaining from previous command execution.
   allPendingCommands += theReadBytes;

   while(allPendingCommands.Contains(commandSeparator))
   {
       // it may happen that the string at the moment contains incomplete
       // string, which can also not be decoded/decrypted. This loop will
       // stop and the next 256 bytes (as much available) will be read and
       // appended into allPendingCommands. When the command gets completed,
       // allPendingCommands will contain the separator character and the
       // command will be properly decoded and executed.

       int idx = allPendingCommands.IndexOf(commandSeparator);
       string command = allPendingCommands.SubString(0, idx);
       allPendingCommand = allPendingCommand.SubString(idx + 1);

       // convert those bytes back to string (after decrypting/decoding)
        command = Decrypt(command);

       // Process the Command (command); // do the needful in the function
   } 
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接