Question

I am writing a client application (windows service) which regularly reads data from and writes data to a server. The server is configured to always reply to the client if the frame he received is understood. I have the following method for sending and receiving :

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

I had a lot of difficulties leaving the stream open, so at the moment we close it every time after sending and receiving data. Should I leave the stream and tcpclient open? the function is regularly called.

Was it helpful?

Solution

I have worked on an application in which the NetworkStream was opened when the application started and was closed only in following scenarios:

  • Application was closed - (that application mostly runs continuously for months).
  • Network connection is lost - (very reliable gigabit ethernet + 100+Mbps MPLS) after timeout, the tcpClient.Connected property will return false and we close the NetworkStream and TcpClient. Then we start a per second timer which will check for the availability of server and as soon as the server is found, it reconnects thereby opening the TcpClient and NetworkStream
  • Server is shutting down - (very very rare) server sends the disconnect signal which causes the client application to close the NetworkStream and the TcpClient and start the polling thread to check availability of the server.

We have not observed any issue for keeping the NetworkStream and TcpClient open. Maybe there are other parts of code which may be causing issues.


Out of context, but a suggestion: When you are reading from NetworkStream, you are reading ony 256 bytes; what if the data is longer than 256 bytes?

I would suggest some separator for each set of data; e.g. if your encryption system generates Base64 Hashes, you can safely use ';' (semicolon) as the data separator. (We are using \n as separator of commands) but it totally depends on your scenario.

Also, use the following type of logic to read and store the received string, and decrypt and execute only when the separator character is available. This will ensure that you are never receiving a part string and attempting a decrypt of that.

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
   } 
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top