Using TcpClient to push data across the network. Client only sends data. At some point, the server may close the socket. After the server closes the socket, the first client send completes without an exception. A client second send throws an IOException. I would have expected the first client send to return an IOException, because the socket on the far side is closed.
Is there an explanation for why things behave this way?
I would have expected the first send to the closed socket to throw an exception, because it didn't reach the socket on the far side.
Sample code to reproduce this behavior:
const string _address = "127.0.0.1";
const int _port = 5500;
const int _payloadSize = 100;
static async Task RunTestAsync()
{
IPAddress.TryParse(_address, out IPAddress ip);
Task serverTask = Task.Run(async () =>
{
Console.WriteLine("Server listening");
IPEndPoint ipLocal = new IPEndPoint(ip, _port);
TcpListener listener = new TcpListener(ipLocal);
listener.Start();
using (TcpClient serverSocket = await listener.AcceptTcpClientAsync())
{
Console.WriteLine("Server accepted connection");
byte[] serverbytes = new byte[_payloadSize];
using (NetworkStream serverStream = serverSocket.GetStream())
{
int bytesRead = await serverStream.ReadAsync(serverbytes, 0, serverbytes.Length);
Console.WriteLine("Server received {0} bytes", bytesRead);
}
}
Console.WriteLine("Socket closed from server (CLOSE_WAIT until client closes)");
listener.Stop();
Console.WriteLine("Listener stopped");
});
using (TcpClient clientSocket = new TcpClient())
{
await clientSocket.ConnectAsync(ip, _port);
Console.WriteLine("Client connected");
byte[] clientbytes = new byte[_payloadSize];
using (NetworkStream clientStream = clientSocket.GetStream())
{
await clientStream.WriteAsync(clientbytes, 0, clientbytes.Length);
Console.WriteLine("Client transmitted bytes");
await Task.Delay(2000);
Console.WriteLine("Client delay for server to close socket");
await clientStream.WriteAsync(clientbytes, 0, clientbytes.Length);
Console.WriteLine("Client transmitted bytes on closed socket (FIN_WAIT_2) with no error");
await clientStream.WriteAsync(clientbytes, 0, clientbytes.Length);
Console.WriteLine("Client never transmitted these bytes. Got exception instead");
}
}
await serverTask;
}
I also wireharked the app running.
I would have expected the RST,ACK returned from the second packet to generate an exception in the application, because the socket on the far side is closed, so the packet clearly did not get to its recipient.
1 10:14:25.980424 127.0.0.1 127.0.0.1 TCP 66 50131 → 5500 [SYN] Seq=0 Win=65535 Len=0 MSS=65495 WS=256 SACK_PERM=1
2 10:14:25.980464 127.0.0.1 127.0.0.1 TCP 66 5500 → 50131 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=65495 WS=256 SACK_PERM=1
3 10:14:25.980537 127.0.0.1 127.0.0.1 TCP 54 50131 → 5500 [ACK] Seq=1 Ack=1 Win=2619648 Len=0
4 10:14:25.982960 127.0.0.1 127.0.0.1 VNC 154
5 10:14:25.982976 127.0.0.1 127.0.0.1 TCP 54 5500 → 50131 [ACK] Seq=1 Ack=101 Win=2619648 Len=0
6 10:14:25.984495 127.0.0.1 127.0.0.1 TCP 54 5500 → 50131 [FIN, ACK] Seq=1 Ack=101 Win=2619648 Len=0
7 10:14:25.984511 127.0.0.1 127.0.0.1 TCP 54 50131 → 5500 [ACK] Seq=101 Ack=2 Win=2619648 Len=0
8 10:14:27.986372 127.0.0.1 127.0.0.1 VNC 154
9 10:14:27.986583 127.0.0.1 127.0.0.1 TCP 54 5500 → 50131 [RST, ACK] Seq=2 Ack=201 Win=0 Len=0```