Hi,

I have written some software, one aspect of which is a TCP client. It sends messages to a designated address and port and receives a number of messages in response. This is all working perfectly on some PCs, but on others, it loses connection.
I have done some digging and debugging and it appears to relate to the ReadTimeout/ReceiveTimeout. On the computers that have problems, if no data is received and one of the timeout values is reached, the TCPClient shows as disconnected. On the PCs that work fine, the timeout occurs but the client remains connected.

Here's the code I am using:

                private TcpClient client; 
                private BinaryReader MessageReader;
                private BinaryWriter MessageWriter;
                private NetworkStream DataStream;
                private Thread ClientThread;

                private void ConnectTcpClient()
                {
                    //check that client and thread haven't already been instantiated/connected
                    if ((ClientThread != null && ClientThread.ThreadState != ThreadState.Stopped) 
                        && (client.Client != null && client.Connected))
                    {
                        return;
                    }

                    //create and connect client in new thread
                    ClientThread = new Thread(new ThreadStart(PerformConnection));
                    ClientThread.Start();            
                }

                private void PerformConnection()
                {
                    //try Connecting on the given ip address
                    try
                    {
                        using (client = new TcpClient()) //assign new tcp client object
                        {
                            client.ReceiveTimeout = 20000;
                            client.SendTimeout = 5000;

                            ChangeStatusContent("Connecting..."); //invoke to write status to menu on main form
                            DisconnectFlag = false;

                            IAsyncResult result = client.BeginConnect(_ethernetPort._IP, _ethernetPort._iPort, null, null);
                            bool success = result.AsyncWaitHandle.WaitOne(5000, true);
                            if (!success)
                            {
                                client.Close();
                                ChangeStatusContent("Disconnected");
                                return;
                            }

                            DataStream = client.GetStream();
                            DataStream.ReadTimeout = 10000;
                            DataStream.WriteTimeout = 5000;

                            using (MessageReader = new BinaryReader(DataStream))
                            {
                                using (MessageWriter = new BinaryWriter(DataStream))
                                {

                                    ChangeStatusContent("Connected");

                                    HandleConnection();


                                    MessageWriter.Close();
                                    MessageReader.Close();
                                    DataStream.Close();
                                    client.Close();
                                    ChangeStatusContent("Disconnected");
                                }
                            }
                        }
                    }
                    catch (Exception)
                    {
                        ChangeStatusContent("Disconnected");
                    }
                }
                private void HandleConnection()
                {
                    string message = "";

                    //loop until DisconnectFlag state changed
                    do
                    {

                        //try reading from the data stream if anything went wrong with the connection break
                        try
                        {
                            while (!message.Contains(Environment.NewLine))
                            {
                                message += MessageReader.ReadChar();//read message
                            }
                            if (message != "")
                            {
                                ChangeTextBoxContent(message);//invoke method to write message to textbox on main form
                                message = "";
                            }
                        }
                        catch (Exception ex)
                        {
                            if (client == null || !client.Connected)
                            {
                                ChangeStatusContent("connection Lost");
                                break; //get out of the while loop
                            }
                        }
                    } while (!DisconnectFlag);  
                }

I originally had no timeouts, but found that I couldn't get the thread to close as the read call would block. I added the timeouts so that I can set the DisconnectFlag to true when i want to close and join the ClientThread. I'm not sure why some PCs are handling this differently than others, although I believe that the ones with the problem are Windows XP whilst the ones that work are Win7 (the only PC that i was able to reproduce the fault on was WinXP...the others are at a remote clients site).

Any help would be greatly appreciated as this has me stumped at the moment. I don't want to take the timeouts away as then I will be back to a blocking read call which prevents the application closing cleanly once the client is connected.

According to specification, a TCP/IP connection has no requirement to idle timeout.
You're now essentially saying "I'm expecting data to be received within 20 seconds, otherwise, something went wrong."

The OS or network card itself will handle the underlying connection when this happens and in some cases could take this to mean "The connection has been lost" and close it.

You have two solutions;

  1. Remove the timeout, there's no need for one unless you're expecting data every x [milli]seconds. To remove the problem of threads not joining because of the read block, Shutdown <--- IMPORTANT and Close the socket before you try to join. This will cause the Receive method to abort and throw an exception. A little bit dirty, but within spec.

  2. Sending a simple "PING-PONG" every x seconds that goes by without a data-send, for example, would be sufficient to keep the connection open without worrying about the timeouts.

I would personally go with option 1. Option 2 can get complicated even though it may seem simple at first.

Hi Ketsuekiame, thanks for the reply. I will try what you suggested.
I think the fact that all the machines I tested before releasing it to our customer had no issues is what threw me. Frustrating that the connection is handled differently though.

Hi Ketsuekiame, sorry if this is a dumb question...but how do i "SHUTDOWN" the TCPClient?

Ah, sorry I'm used to dealing with Sockets and not the TcpClient abstraction. Calling close on the TcpClient should be enough.

You could try switching to MessageReader.ReadBytes. The advantage here being that it will read all the data available up to the size you specify, if available. This call will not block, but instead will return how many bytes it managed to read on this operation. If the stream has closed (reached the end) it will return -1. You can check for this value to tell you whether or not the stream has closed.

Also, becareful with your message parsing loop. You will get stuck in an infinite loop there if an exception is never thrown.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.