C#: Understanding CLOSE_WAIT and FIN_WAIT_2

Here’s a short C# program which can be used to better understand what the TCP states CLOSE_WAIT and FIN_WAIT_2 are and why you sometimes see connections stuck in these states:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;

namespace TcpTester
{
    internal static class Program
    {
        private const int Port = 15000;
        private const string Hostname = "127.0.0.1";

        private static void Main(string[] args)
        {
            if (args.Length > 0 && args[0] == "client")
            {
                // Started in client mode
                var tcpClient = new TcpClient();
                tcpClient.Connect(Hostname, Port);
                Console.WriteLine("Connected to {0}:{1}", Hostname, Port);
                PrintState();
                Console.WriteLine("Press any key to close the connection from this side.");
                Console.ReadKey();
                tcpClient.Close();
                PrintState();
            }
            else
            {
                // Started in server mode
                var tcpListener = new TcpListener(IPAddress.Parse(Hostname), Port);
                tcpListener.Start();
                Console.WriteLine("Listening on {0}:{1}", Hostname, Port);
                TcpClient tcpClient = tcpListener.AcceptTcpClient();
                tcpListener.Stop();
                Console.WriteLine("Client connected on {0}:{1}", Hostname, Port);
                PrintState();
                Console.WriteLine("Press any key to close the connection from this side.");
                Console.ReadKey();
                tcpClient.Close();
                PrintState();
            }
        }

        private static void PrintState()
        {
            IEnumerable<TcpConnectionInformation> activeTcpConnections =
                IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections()
                    .Where(c => c.LocalEndPoint.Port == Port || c.RemoteEndPoint.Port == Port);
            foreach (TcpConnectionInformation connection in activeTcpConnections)
            {
                Console.WriteLine("{0} {1} {2}", connection.LocalEndPoint, connection.RemoteEndPoint, connection.State);
            }
        }
    }
}

You can start the program without parameters to start a server and with the parameter “client” to start a client (guess it was kind of obvious…).

The server listens to 127.0.01:15000 and the clients connects to it. First start the server. The following will be written to the console:

Listening on 127.0.0.1:15000

Then start the client in another window. The following will appear in the client window:

Connected to 127.0.0.1:15000
127.0.0.1:15000 127.0.0.1:57663 Established
127.0.0.1:57663 127.0.0.1:15000 Established

This tells you that the client is connected from port 57663 (this will change every time you run this test) to port 15000 (where the server is listening).

In the server window, you will see that it got a client connection and the same information regarding port and connection states.

Then press any key on the server console and the following will be displayed:

127.0.0.1:15000 127.0.0.1:57663 FinWait2
127.0.0.1:57663 127.0.0.1:15000 CloseWait

So once the server closed the connection, the connection on the server side went to FIN_WAIT_2 and the one on the client side went to CLOSE_WAIT.

Then press any key in the client console to get the following displayed:

127.0.0.1:15000 127.0.0.1:57663 TimeWait

The connection will stay in TIME_WAIT state for some time. If you really wait a long time before pressing a key in the client console, this last line will not be displayed at all.

So, this should make it easier to understand what the TCP states CLOSE_WAIT and FIN_WAIT_2 are: When the connection has been closed locally but not yet remotely, the local connection is the state FIN_WAIT and the remote one in CLOSE_WAIT.

For more details about the different TCP states, please refer to TCP: About FIN_WAIT_2, TIME_WAIT and CLOSE_WAIT.

One thought on “C#: Understanding CLOSE_WAIT and FIN_WAIT_2

  1. If server closed connection firstly, server side tcp state transits to FIN-WAIT-2; client side received FIN then CLOSE-WAIT, send FIN to server, transits to LAST-ACK; server side received client’s FIN, send ACK, transits to TIME-WAIT, wait 2MSL, then CLOSED; client received server’s ACK, then CLOSED.

Leave a Reply

Your email address will not be published. Required fields are marked *