Domanda

I am writing a basic TCP/IP server to receive messages from an existing Client. Currently, I am able to receive single- and multi-buffer messages. The problem comes when the client sends a set of multiple messages. Rather than sending each message as a single communication, it expects the stream to open and all messages in the set to be sent one after the other before the stream closes again. I have coded for this in the edited snippet below:

private void AcceptMessage(IAsyncResult ar)
{
    String receivedMessage = "";

    //  Only run if the server is listening. Otherwise, an exception will be thrown.
    if (isListening)
    {
       ASCIIEncoding encoder = new ASCIIEncoding();
       try
       {
           TcpListener listener = (TcpListener)ar.AsyncState;
           using (TcpClient client = listener.EndAcceptTcpClient(ar))
           using (NetworkStream stream = client.GetStream())
           {
               int bytesRead;
               int totalBytes = 0;
               stream.ReadTimeout = ReadTimeout;
               do
               {
                   byte[] receivedBytes = new byte[BufferSize];
                   StringBuilder message = new StringBuilder();
                   bytesRead = 0;
                   do
                   {
                       try
                       {
                           if (stream.CanRead)
                           bytesRead = stream.Read(receivedBytes, 0, BufferSize);
                       }
                       catch (...Exception Handling...)

                       if (bytesRead == 0)
                           break;

                       message.Append(encoder.GetString(receivedBytes, 0, bytesRead));
                       totalBytes += bytesRead;
                   } while (bytesRead == BufferSize);     //  Allow for multiple buffers in a single message
                   byte[] ack = encoder.GetBytes(GetAck(message.ToString(), ackNak, status, statusMessage));
                   stream.Write(ack, 0, ack.Length);
                   ...Message Processing...
               } while (stream.DataAvailable);         //  Allow for multiple messages                        
           }
       }
       catch (...Error Handling...)
       finally { ...Cleanup...}
   }
}

The problem comes at the second while (stream.DataAvailable); When the check occurs, DataAvailable shows false when sending a set of messages, but if I put a break on the next line, it will show true. In other words, it is checking to see if data is available before it is available. I could fix this by adding a one second delay before checking to see if there are more messages, but in 99 out of 100 cases, this would be an unnecessary delay. Since the client can only send one message (or set of messages) at a time, it could add a significant amount of time to receiving messages during busy times.

Is there a better way to determine if there are more messages pending (without client modifications)? Thanks!

EDIT

I changed the code to include suggestions. I've added a timeout, and changed the first check from stream.DataAvailable to bytesRead == BufferSize. I do not have access to the Client software, so I cannot add an EOM transmission. I also have no guarantee whether multiple messages will be sent in a single stream or multiple separate streams. I still haven't figured out how to tell if there are more messages pending. I cannot leave the stream open because that blocks the next transmission.

I marked Elgonzo as the answer because he helped with most aspects of my question. It still didn't answer how to tell if there are messages pending, which may not be possible, but my manager decided that once a connection was open, we should leave it open as a dedicated connection. This works since our listeners only communicate with a single device over an internal secure network with consistent traffic. We are adding a time-out to disconnect if there is inactivity for a certain amount of time.

È stato utile?

Soluzione

Whether data is available or not and the speed (and periodicity) of the data coming in at the server side do not just depend on the timing/behaviour of the client, but also on speed and latency factors of the network/internet connections inbetween (which itself depends on many and unknown factors).

In consequence, you cannot use stream.DataAvailable in your outer while-loop to check whether further data will come in or not. You will likely have to set a ReadTimeout on the NetworkStream to know when the client is done/disconnected.

Also, it is preferable to implement a specific signal/message to be send by a client when the client is about to end its connection, and not only rely on a timeout. This will help you troubleshoot problems where it is not clear whether the cause is a bug in the server or client, or whether problems are caused by issues with the network connections.

Implementation details in the answer to this question (although different application scenario the approach is the same): Reading from closed NetworkStream doesn't cause any exception

Additionally, even your inner while-loop might fail on crappy/slow network connections, and might leave you with a partially received message from the client, just because stream.DataAvailable might be false for a moment before the next data packet (continuing the current message) will be received...

Altri suggerimenti

I would suggest starting your own thread which runs in the background and just reads data from the stream if there is data available. If data is received just collect the data until you received a complete message (which should be indicated by a defined byte or byte-sequence) and afterwards fire an event. In my opinion this should help avoiding the problem.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top