locked
Socket problem: TcpClient.GetStream with ReceiveTimeout throws Exception and connected state become false RRS feed

  • Question

  • I use a Socket object to read data synchronously using GetStream().

    I initialized the ReceiveTimeout to a value of "TimeoutMs".

    TcpSockets[socketId].Client.ReceiveTimeout = TimeoutMs;

    After timeout elapses I get an exception that I catch like this:

     catch (IOException ioEx)
                {
                    if (ioEx.InnerException.GetType() == typeof(SocketException))
                    {
                        if (((SocketException)(ioEx.InnerException)).SocketErrorCode == SocketError.TimedOut)
                        {
                                                throw new SocketReadTimeoutException();
                        }

                    }

              }

     

    problem is that after catching the exception above, if I try to send/receive data to/from the socket I found that it is no more CONNECTED, so I have no other chance that ShutDown + Close the socket and open another one.

     

    To verify connection state I use following code stolen ;) from MSDN

     internal static bool IsConnected(Socket client)
            {

                // This is how you can determine whether a socket is still connected.
                bool blockingState = client.Blocking;
                try
                {
                    byte[] tmp = new byte[1];

                    client.Blocking = false;
                    client.Send(tmp, 0, 0);
                }
                catch (SocketException e)
                {
                    // 10035 == WSAEWOULDBLOCK
                    if (e.NativeErrorCode.Equals(10035))
                        Console.WriteLine("Still Connected, but the Send would block");
                    else
                    {
                        Console.WriteLine("Disconnected: error code {0}!", e.NativeErrorCode);
                    }
                }
                finally
                {
                    //restore original blocking state
                    client.Blocking = blockingState;
                }

                return client.Connected;
            }

     

    So:

    I wanted to understand if the behaviour of putting the socket object to a non-connected state is due to the timeout exception (and if so why this is not documented...) and if my choice to close the socket and open another one is correct.

     

    many thanks folks

    Tuesday, August 24, 2010 4:05 PM

Answers

  • > no more CONNECTED, so I have no other chance that ShutDown + Close the socket and open another one.

    I don't understand how one socket no longer being connected prevents you from opening another socket and using that socket.

    A timeout won't necessarily disconnect the socket.  However, if Windows thinks the socket is disconnect, that's all there is to it.

    The following demonstrates how the socket may be used for a subsequent Receive even though previous ones times out.  I think your IsConnected method is wrong.  It returns false after the timeouts, but my code shows that the Socket is indeed usable.

    private ManualResetEvent m_ServerReady = new ManualResetEvent(false);
    
    void Main()
    {
    	Thread s = new Thread(ServerThreadProc);
    	s.Start();
    	
    	m_ServerReady.WaitOne();
    	
    	Thread c = new Thread(ClientThreadProc);
    	c.Start();
    	
    	// Wait for server and client threads to finish.
    	s.Join();
    	c.Join();
    }
    
    void ServerThreadProc()
    {
    	using (Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
    	{
    		server.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345));
    		server.Listen(10);
    		Console.WriteLine("Listening...");
    		m_ServerReady.Set();
    		using (Socket accepted = server.Accept())
    		{
    			// Wait so long that client times out (twice).
    			System.Threading.Thread.Sleep(5000);
    			
    			byte[] buf = System.Text.Encoding.UTF8.GetBytes("It works!");
    			// Send entire buf.
    			int bytesSent = 0;
    			do
    			{
    				bytesSent += accepted.Send(buf, bytesSent, buf.Length - bytesSent, SocketFlags.None);
    			} while (bytesSent != buf.Length);
    			Console.WriteLine("Sent " + bytesSent + " bytes.");
    			accepted.Shutdown(SocketShutdown.Both);
    			accepted.Close();
    		}
    	}
    	Console.WriteLine("Server done.");
    }
    
    void ClientThreadProc()
    {
    	using (Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
    	{
    		client.Connect("127.0.0.1", 12345);
    		
    		byte[] buf = new byte[4096];
    		int bytesReceived;
    		
    		client.ReceiveTimeout = 500;
    
    		try
    		{
    			bytesReceived = client.Receive(buf);
    		}
    		catch (SocketException ex)
    		{
    			Console.WriteLine("First: " + ex.Message);
    		}
    		
    		try
    		{
    			bytesReceived = client.Receive(buf);
    		}
    		catch (SocketException ex)
    		{
    			Console.WriteLine("Second: " + ex.Message);
    		}
    		
    		// Use a longer timeout. It will work this time.
    		client.ReceiveTimeout = 20000;
    		
    		// Read until end-of-stream.
    		int bytesReceivedLast;
    		bytesReceived = 0;
    		while ((bytesReceivedLast = client.Receive(buf, bytesReceived, buf.Length - bytesReceived, SocketFlags.None)) != 0)
    		{
    			bytesReceived += bytesReceivedLast;
    		} // Note: Fails with exception if more than buf.Length bytes can be received.
    		Console.WriteLine("Received " + bytesReceived + " bytes.");
    		Console.WriteLine("Received: " + System.Text.Encoding.UTF8.GetString(buf, 0, bytesReceived));
    	}
    	Console.WriteLine("Client done.");
    }
    
    

    Note that testing for the Socket being connected at the current time is usually not important.  Just keep receiving data until you reach the end of the stream (Receive returns a zero).  No data was lost if you successfully get the zero.  If you follow this pattern and data was lost, you'll get a SocketException instead of ever getting the zero.

     

    • Marked as answer by CIGNUM Wednesday, August 25, 2010 3:01 PM
    Wednesday, August 25, 2010 12:37 AM

All replies

  • > no more CONNECTED, so I have no other chance that ShutDown + Close the socket and open another one.

    I don't understand how one socket no longer being connected prevents you from opening another socket and using that socket.

    A timeout won't necessarily disconnect the socket.  However, if Windows thinks the socket is disconnect, that's all there is to it.

    The following demonstrates how the socket may be used for a subsequent Receive even though previous ones times out.  I think your IsConnected method is wrong.  It returns false after the timeouts, but my code shows that the Socket is indeed usable.

    private ManualResetEvent m_ServerReady = new ManualResetEvent(false);
    
    void Main()
    {
    	Thread s = new Thread(ServerThreadProc);
    	s.Start();
    	
    	m_ServerReady.WaitOne();
    	
    	Thread c = new Thread(ClientThreadProc);
    	c.Start();
    	
    	// Wait for server and client threads to finish.
    	s.Join();
    	c.Join();
    }
    
    void ServerThreadProc()
    {
    	using (Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
    	{
    		server.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345));
    		server.Listen(10);
    		Console.WriteLine("Listening...");
    		m_ServerReady.Set();
    		using (Socket accepted = server.Accept())
    		{
    			// Wait so long that client times out (twice).
    			System.Threading.Thread.Sleep(5000);
    			
    			byte[] buf = System.Text.Encoding.UTF8.GetBytes("It works!");
    			// Send entire buf.
    			int bytesSent = 0;
    			do
    			{
    				bytesSent += accepted.Send(buf, bytesSent, buf.Length - bytesSent, SocketFlags.None);
    			} while (bytesSent != buf.Length);
    			Console.WriteLine("Sent " + bytesSent + " bytes.");
    			accepted.Shutdown(SocketShutdown.Both);
    			accepted.Close();
    		}
    	}
    	Console.WriteLine("Server done.");
    }
    
    void ClientThreadProc()
    {
    	using (Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
    	{
    		client.Connect("127.0.0.1", 12345);
    		
    		byte[] buf = new byte[4096];
    		int bytesReceived;
    		
    		client.ReceiveTimeout = 500;
    
    		try
    		{
    			bytesReceived = client.Receive(buf);
    		}
    		catch (SocketException ex)
    		{
    			Console.WriteLine("First: " + ex.Message);
    		}
    		
    		try
    		{
    			bytesReceived = client.Receive(buf);
    		}
    		catch (SocketException ex)
    		{
    			Console.WriteLine("Second: " + ex.Message);
    		}
    		
    		// Use a longer timeout. It will work this time.
    		client.ReceiveTimeout = 20000;
    		
    		// Read until end-of-stream.
    		int bytesReceivedLast;
    		bytesReceived = 0;
    		while ((bytesReceivedLast = client.Receive(buf, bytesReceived, buf.Length - bytesReceived, SocketFlags.None)) != 0)
    		{
    			bytesReceived += bytesReceivedLast;
    		} // Note: Fails with exception if more than buf.Length bytes can be received.
    		Console.WriteLine("Received " + bytesReceived + " bytes.");
    		Console.WriteLine("Received: " + System.Text.Encoding.UTF8.GetString(buf, 0, bytesReceived));
    	}
    	Console.WriteLine("Client done.");
    }
    
    

    Note that testing for the Socket being connected at the current time is usually not important.  Just keep receiving data until you reach the end of the stream (Receive returns a zero).  No data was lost if you successfully get the zero.  If you follow this pattern and data was lost, you'll get a SocketException instead of ever getting the zero.

     

    • Marked as answer by CIGNUM Wednesday, August 25, 2010 3:01 PM
    Wednesday, August 25, 2010 12:37 AM
  • Hi All,

    Not sure what the procedures are to re-open an already answered thread question, but my problem is very similar - however the BinaryCoder's answer does not cover it all.

    My problem is that whenever a receive timeout fires it sets the TCP client object's Connected property to false and it remains False even after a subsequent successful read operation. This prevents any subsequent Write operation using the NetworkStream interface.  Although, it does not prevent subsequent I/O operation on the socket itself (nor it prevents NetworkStream.Read)!

    I modified BinaryCoder's example to illustrate the problem. In this scenario the server sends a question and the client sends an asnwer back on the same tcp connection:

    static private ManualResetEvent m_ServerReady = new ManualResetEvent(false); static void Main() { Thread s = new Thread(ServerThreadProc); s.Start(); m_ServerReady.WaitOne(); Thread c = new Thread(ClientThreadProc); c.Start(); // Wait for server and client threads to finish. s.Join(); c.Join(); } static void ServerThreadProc() { using (Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { server.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345)); server.Listen(10); Console.WriteLine("SERVER> Listening..."); m_ServerReady.Set(); using (Socket accepted = server.Accept()) { // Wait so long that client times out. System.Threading.Thread.Sleep(5000); // //Send a question // byte[] buf = System.Text.Encoding.UTF8.GetBytes("Does it work?"); // Send entire buf. int bytesSent = 0; do { bytesSent += accepted.Send(buf, bytesSent, buf.Length - bytesSent, SocketFlags.None); } while (bytesSent != buf.Length); Console.WriteLine("SERVER> Sent " + bytesSent + " bytes."); accepted.ReceiveTimeout = 5000; // //Receiving answer // int bytesReceivedLast; byte[] recvbuf = new byte[4096]; int bytesReceived = 0; //Read until the end-of-stream while ((bytesReceivedLast = accepted.Receive(recvbuf, bytesReceived, recvbuf.Length - bytesReceived, SocketFlags.None)) != 0) { bytesReceived += bytesReceivedLast; } // Note: Fails with exception if more than buf.Length bytes can be received. Console.WriteLine("SERVER> Received " + bytesReceived + " bytes."); Console.WriteLine("SERVER> Received: " + System.Text.Encoding.UTF8.GetString(recvbuf, 0, bytesReceived)); accepted.Shutdown(SocketShutdown.Both); accepted.Close(); } } Console.WriteLine("SERVER> Done."); } static void ClientThreadProc() { using (TcpClient tcpclient = new TcpClient("127.0.0.1", 12345)) { NetworkStream client = tcpclient.GetStream(); byte[] buf = new byte[4096]; int bytesReceived; client.ReadTimeout = 500; try { bytesReceived = tcpclient.Client.Receive(buf); } catch (SocketException ex) { Console.WriteLine("CLIENT> Connection attempt failed: " + ex.Message); } Console.WriteLine("CLIENT> tcpclient.Connected:" + tcpclient.Connected); // Use a longer timeout. It will work this time. tcpclient.Client.ReceiveTimeout = 10000; // //Receiving question ("Does it work?") // // Read 13 bytes or end-of-stream. int bytesReceivedLast; bytesReceived = 0; while (((bytesReceived < 13) && (bytesReceivedLast = tcpclient.Client.Receive(buf, bytesReceived, buf.Length - bytesReceived, SocketFlags.None)) != 0)) { bytesReceived += bytesReceivedLast; } // Note: Fails with exception if more than buf.Length bytes can be received. Console.WriteLine("CLIENT> Received " + bytesReceived + " bytes."); Console.WriteLine("CLIENT> Received: " + System.Text.Encoding.UTF8.GetString(buf, 0, bytesReceived)); // //Sending answer // byte[] sendbuf = System.Text.Encoding.UTF8.GetBytes("It does!");

    Console.WriteLine("CLIENT> tcpclient.Connected:" + tcpclient.Connected); // //VERSION A: // using the NetworkStream interface. // try { tcpclient.GetStream().Write(sendbuf, 0, sendbuf.Length); Console.WriteLine("CLIENT> Written to stream: " + System.Text.Encoding.UTF8.GetString(sendbuf, 0, sendbuf.Length)); } catch (Exception e) { Console.WriteLine("CLIENT> EXCEPTION: " + e.ToString()); } //// ////VERSION B: //// Send entire sendbuf. //// //int bytesSent = 0; //do //{ // bytesSent += tcpclient.Client.Send(sendbuf, bytesSent, sendbuf.Length - bytesSent, SocketFlags.None); //} while (bytesSent != sendbuf.Length); //Console.WriteLine("CLIENT> Sent " + bytesSent + " bytes."); Console.WriteLine("CLIENT> tcpclient.Connected:" + tcpclient.Connected); } Console.WriteLine("CLIENT> Done."); }


    As you can see, there are two different versions for sending the answer back, one that uses NetworkStream (VERSION A) and another that uses the socket (VERSION B).

    Running version A the output is:

    SERVER> Listening...
    CLIENT> Connection attempt failed: A kapcsolódási kísérlet nem sikerült, mert a kapcsolódó partner nem válaszolt a megadott időn belül, vagy a létrehozott kapcsolatban hiba történt, mert a kapcsolódó partner nem volt képes válaszolni
    CLIENT> tcpclient.Connected:False
    SERVER> Sent 13 bytes.
    CLIENT> Received 13 bytes.
    CLIENT> Received: Does it work?
    CLIENT> tcpclient.Connected:False
    CLIENT> EXCEPTION: System.InvalidOperationException: The operation is not allowed on non-connected sockets. at System.Net.Sockets.TcpClient.GetStream()  at TCPTimeoutTest.Program.ClientThreadProc() in C:\[...]Program.cs:line 135

    CLIENT> tcpclient.Connected:False
    SERVER> Received 0 bytes.
    SERVER> Received: 
    CLIENT> Done.
    SERVER> Done.


    Output of VERSION B:

    SERVER> Listening...
    CLIENT> Connection attempt failed: A kapcsolódási kísérlet nem sikerült, mert a kapcsolódó partner nem válaszolt a megadott időn belül, vagy a létrehozott kapcsolatban hiba történt, mert a kapcsolódó partner nem volt képes válaszolni
    CLIENT> tcpclient.Connected:False
    SERVER> Sent 13 bytes.
    CLIENT> Received 13 bytes.
    CLIENT> Received: Does it work?
    CLIENT> tcpclient.Connected:False
    CLIENT> Sent 8 bytes.
    CLIENT> tcpclient.Connected:False
    SERVER> Received 8 bytes.
    SERVER> Received: It does!
    CLIENT> Done.
    SERVER> Done.

    Two conclusions

    1.  According to msdn documentation TcpClient.Connected should reflect the state of the connection as of the last I/O operation. In the above example there is at least one I/O operation that complete successfully, while TcpClientConnected remains False. In VERSION B there are two such operations the (Socket.Receive and NetworkStream.Write) that complete successfully, and still, Connected remains False. (Please note that because of convenience I didn't make two versions of the client's input operation and left it to Socket.Receive as was in BinaryCoder's example, but it has the exact same behaviour when using NetworkStream.Read - i.e it completes successfully but leaves the Connected property untouched.)
    2. It seems to me that not only the Connected property is buggy but it effects the NetworkStream interface as well. 

    Has anyone encountered the same problem? Is this a known bug - or maybe there is a sensible explanation and this is a designed behaviour?

    If anyone knows about this issue please share your knowledge on the topic!


    • Edited by zkomives Wednesday, December 12, 2012 1:36 AM
    Wednesday, December 12, 2012 1:28 AM