Note that there are some explanatory texts on larger screens.

plurals
  1. POWhy is Socket.Receive timing out on a half-closed connection when timeout is set to infinite?
    text
    copied!<p>Below is a C# program demonstrating the problem.</p> <p>The server starts listening on a socket. The client connects to the server, sends a message, uses Shutdown(SocketShutdown.Send) to close its send half of the connection to let the server know where the end of the message is, and waits for a response from the server. The server reads the message, does some lengthy computation (simulated here with a sleep call), sends a message to the client, and closes the connection.</p> <p>On Windows, the client's Receive call always fails after exactly 2 minutes with "A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond" even though the timeout is set to infinite.</p> <p>If I run the program in Linux with Mono, the timeout does not occur even if I set the "lengthy operation" to 10 minutes, but it happens in Windows whether I run it with Mono or .NET. If I set the timeout to 1 second, it times out after 1 second. In other words, it times out in the timeout I set or 2 minutes, whichever is less.</p> <p>A similar sample program in which the server sends a message to the client, with no message from client to server and no half-close, works as expected with no timeout.</p> <p>I can get around this by modifying my protocol to use some other method of indicating to the server when a message is complete (perhaps prefixing the message with the length of the message). But I want to know what's going on here. Why does Socket.Receive time out on a half-closed connection when the timeout is set to infinite?</p> <p>From what I understand, a connection with only its send half closed should be able to continue receiving data indefinitely. It seems unlikely that there would be a bug in such a fundamental part of Windows. Am I doing something wrong?</p> <pre><code>using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Net.Sockets; using System.Net; using System.Threading.Tasks; using System.Diagnostics; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { // Start server thread Thread serverThread = new Thread(ServerStart); serverThread.IsBackground = true; serverThread.Start(); // Give the server some time to start listening Thread.Sleep(2000); ClientStart(); } static int PortNumber = 8181; static void ServerStart() { TcpListener listener = new TcpListener(new IPEndPoint(IPAddress.Any, PortNumber)); listener.Start(); while (true) { TcpClient client = listener.AcceptTcpClient(); Task connectionHandlerTask = new Task(ConnectionEntryPoint, client); connectionHandlerTask.Start(); } listener.Stop(); } static void ConnectionEntryPoint(object clientObj) { using (TcpClient client = (TcpClient)clientObj) using (NetworkStream stream = client.GetStream()) { // Read from client until client closes its send half. byte[] requestBytes = new byte[65536]; int bufferPos = 0; int lastReadSize = -1; while (lastReadSize != 0) { lastReadSize = stream.Read(requestBytes, bufferPos, 65536 - bufferPos); bufferPos += lastReadSize; } client.Client.Shutdown(SocketShutdown.Receive); string message = Encoding.UTF8.GetString(requestBytes, 0, bufferPos); // Sleep for 2 minutes, 30 seconds to simulate a long-running calculation, then echo the client's message back byte[] responseBytes = Encoding.UTF8.GetBytes(message); Console.WriteLine("Waiting 2 minutes 30 seconds."); Thread.Sleep(150000); try { stream.Write(responseBytes, 0, responseBytes.Length); } catch (SocketException ex) { Console.WriteLine("Socket exception in server: {0}", ex.Message); } } } static void ClientStart() { using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { // Set receive timeout to infinite. socket.ReceiveTimeout = -1; // Connect to server socket.Connect(IPAddress.Loopback, PortNumber); // Send a message to the server, then close the send half of the client's connection // to let the server know it has the entire message. string requestMessage = "Hello"; byte[] requestBytes = Encoding.UTF8.GetBytes(requestMessage); socket.Send(requestBytes); socket.Shutdown(SocketShutdown.Send); // Read the server's response. The response is done when the server closes the connection. byte[] responseBytes = new byte[65536]; int bufferPos = 0; int lastReadSize = -1; Stopwatch timer = Stopwatch.StartNew(); try { while (lastReadSize != 0) { lastReadSize = socket.Receive(responseBytes, bufferPos, 65536 - bufferPos, SocketFlags.None); bufferPos += lastReadSize; } string responseMessage = Encoding.UTF8.GetString(responseBytes, 0, bufferPos); Console.WriteLine(responseMessage); } catch (SocketException ex) { // Timeout always occurs after 2 minutes. Why? timer.Stop(); Console.WriteLine("Socket exception in client after {0}: {1}", timer.Elapsed, ex.Message); } } } } } </code></pre> <p>The following program prefixes messages with a 4-byte message length rather than using socket.Shutdown(SocketShutdown.Send) to signal end of message. The timeout does <strong>not</strong> occur in this program.</p> <pre><code>using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; using System.Net; using System.Threading.Tasks; using System.Diagnostics; using System.Threading; namespace WithoutShutdown { class Program { static void Main(string[] args) { // Start server thread Thread serverThread = new Thread(ServerStart); serverThread.IsBackground = true; serverThread.Start(); // Give the server some time to start listening Thread.Sleep(2000); ClientStart(); } static int PortNumber = 8181; static void ServerStart() { TcpListener listener = new TcpListener(new IPEndPoint(IPAddress.Any, PortNumber)); listener.Start(); while (true) { TcpClient client = listener.AcceptTcpClient(); Task connectionHandlerTask = new Task(ConnectionEntryPoint, client); connectionHandlerTask.Start(); } listener.Stop(); } static void SendMessage(Socket socket, byte[] message) { // Send a 4-byte message length followed by the message itself int messageLength = message.Length; byte[] messageLengthBytes = BitConverter.GetBytes(messageLength); socket.Send(messageLengthBytes); socket.Send(message); } static byte[] ReceiveMessage(Socket socket) { // Read 4-byte message length from the client byte[] messageLengthBytes = new byte[4]; int bufferPos = 0; int lastReadSize = -1; while (bufferPos &lt; 4) { lastReadSize = socket.Receive(messageLengthBytes, bufferPos, 4 - bufferPos, SocketFlags.None); bufferPos += lastReadSize; } int messageLength = BitConverter.ToInt32(messageLengthBytes, 0); // Read the message byte[] messageBytes = new byte[messageLength]; bufferPos = 0; lastReadSize = -1; while (bufferPos &lt; messageLength) { lastReadSize = socket.Receive(messageBytes, bufferPos, messageLength - bufferPos, SocketFlags.None); bufferPos += lastReadSize; } return messageBytes; } static void ConnectionEntryPoint(object clientObj) { using (TcpClient client = (TcpClient)clientObj) { byte[] requestBytes = ReceiveMessage(client.Client); string message = Encoding.UTF8.GetString(requestBytes); // Sleep for 2 minutes, 30 seconds to simulate a long-running calculation, then echo the client's message back byte[] responseBytes = Encoding.UTF8.GetBytes(message); Console.WriteLine("Waiting 2 minutes 30 seconds."); Thread.Sleep(150000); try { SendMessage(client.Client, responseBytes); } catch (SocketException ex) { Console.WriteLine("Socket exception in server: {0}", ex.Message); } } } static void ClientStart() { using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { // Set receive timeout to infinite. socket.ReceiveTimeout = -1; // Connect to server socket.Connect(IPAddress.Loopback, PortNumber); // Send a message to the server string requestMessage = "Hello"; byte[] requestBytes = Encoding.UTF8.GetBytes(requestMessage); SendMessage(socket, requestBytes); // Read the server's response. Stopwatch timer = Stopwatch.StartNew(); try { byte[] responseBytes = ReceiveMessage(socket); string responseMessage = Encoding.UTF8.GetString(responseBytes); Console.WriteLine(responseMessage); } catch (SocketException ex) { // Timeout does not occur in this program because it does not call socket.Shutdown(SocketShutdown.Send) timer.Stop(); Console.WriteLine("Socket exception in client after {0}: {1}", timer.Elapsed, ex.Message); } } } } } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload