我正在编写一个客户端应用程序(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 保持打开状态吗?该函数被定期调用。

最佳答案

我曾研究过一个应用程序,其中 NetworkStream 在应用程序启动时打开,仅在以下情况下关闭:

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

  • 我们没有观察到任何保持 NetworkStream 和 TcpClient 打开的问题。 也许还有其他部分的代码可能会导致问题。

    脱离上下文,但建议: 当您从 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
       }
    }
    

    关于c# - 何时关闭 tcpclient 和 networkstream,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/23381627/

    10-09 02:15