Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can you re-use or reconnect to a socket on the same port after disconnect?

I am doing a simple project involving a single server program and a single client program. It needs to check if the client is connected (from the server side) and vice-versa for the client.

When the client loses internet, the server needs to know it is disconnected. Then, the client needs to reconnect to the server when it regains internet

When the client loses internet and then regains internet, I can't reconnect using the same port.

I tried leaving the server listening and not shutting the socket down and that didn't work either. I have tried so many properties for the sockets in terms of re-using and also I tried the lingering stuff too.

I have seen that it can be stuck on some TIME_WAIT property set by the OS in the registry (in the case of Windows). My question, to restate, is to be able to use the same socket (more importantly the same port) to reconnect after the client lost and regained internet and to be still listening awaiting the reconnect.

Like I said, I can detect when it disconnects from the server-side and also on the client but when I try to reconnect using the same port and with the same socket or a restarted socket it still won't connect and it won't show up at all. Is there any suggestion to help fix this problem? I have been searching for a long time to figure out this problem.

Scenario:

  1. Server is Listening
  2. Client Connects to Server
  3. Unplug Client from internet
  4. Plug Client internet cable back in
  5. Client automatically reconnects to server (Currently doesn't do this on same port)

TL;DR Lost and regain internet on client-server model, but can't use same socket and port to connect to server.

CODE:

    private void button2_Click(object sender, EventArgs e)
    {

        // This will stop the threads/connections and toggle the button back to its original state
        if (button2.Text == "Stop Listening")
        {

            listener.Close();
            stop = true;
            threadsActive = false;
            button2.Text = "Start Listening";
            textBox1.AppendText("Manually Closed Threads/Connections" + Environment.NewLine);

        }
        else
        {

            listenThread = new Thread(listenLoop);
            listenThread.IsBackground = true;
            status = new Thread(checkIfOnline);
            status.IsBackground = true;
            stop = false;
            threadsActive = true;
            button2.Text = "Stop Listening";
            localEndPoint = new IPEndPoint(IPAddress.Parse("129.59.79.65"), 3000);
            listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            listenThread.Start();
            status.Start();

        }

    }

====================================================

private void listenLoop()
    {

        try
        {

            listener.Bind(localEndPoint);
            listener.Listen(100);

            textBox1.AppendText("Waiting for a client..." + Environment.NewLine);

            listener = listener.Accept();
            textBox1.AppendText("Client Connected!!" + Environment.NewLine);

            status.Start();

            while (!close)
            {

                if (stop)
                    return;
                // server connection loop

            }

            if(close)
                return;

        }
        catch (Exception excp)
        {



        }

    }

====================================================

private void ResetSocket()
    {

        // stop all threads and connections
        stop = true;
        listener.Close();

        textBox1.AppendText("Attempting to kill threads..." + Environment.NewLine);
        //while (listenThread.IsAlive == true || status.IsAlive == true) { /*loop until the threads are killed*/ textBox1.AppendText("Closing Threads..."); }
        //listener.Close();
        threadsActive = false;

        textBox1.AppendText("All Threads/Connections Closed" + Environment.NewLine + "Restarting Threads/Connections..." + Environment.NewLine);

        // re-establish and start threads and connections again
        stop = false;
        listenThread = new Thread(listenLoop);
        listenThread.IsBackground = true;
        status = new Thread(checkIfOnline);
        status.IsBackground = true;
        threadsActive = true;
        localEndPoint = new IPEndPoint(IPAddress.Parse("129.59.79.65"), 3000);
        listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        listenThread.Start();
        status.Start();

        textBox1.AppendText("Threads/Connections Restarted Successfully" + Environment.NewLine);

    }
like image 971
Zach Avatar asked Sep 30 '22 06:09

Zach


2 Answers

Don't bind the client socket. Only the server port is important.

On the server side, just keep the listening socket listening. When the listening socket accepts a connection, it returns a new socket that represents that connection. When you detect a loss of connection, close the corresponding connection socket but leave the listening socket running.

On a side note, IMO the best way to detect a loss of connection is to periodically send data (in both directions).

I have a TCP/IP .NET FAQ on my blog. It is .NET, but the general concepts apply to any TCP/IP scenario.

like image 200
Stephen Cleary Avatar answered Oct 17 '22 08:10

Stephen Cleary


To elaborate on Stephen Cleary's terrific answer. After we create and bind it, the server starts to listen. Control enters the infinite loop. The client connects to server. If we now unplug the Internet, the client throws an exception, which returns us to the top of the infinite loop, where we await network connectivity before attempting to accept another connection.

private void ListenForIncomingHttpRequests()
{
    AwaitNetworkAvailability();

    // create
    var serverSocket = 
        new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    // bind
    var localEndpoint = new IPEndPoint(IPAddress.Any, port: 12000);
    serverSocket.Bind(localEndpoint);

    // listen
    serverSocket.Listen(backlog: 25);

    while (true)
    {
        AwaitNetworkAvailability();

        try
        {
            // client connects to the server
            using (var clientSocket = serverSocket.Accept())
            {
                ProcessServerSocketRequest(clientSocket);
            }
        }
        catch (Exception ex)
        {
            _logger.Write(ex.ToString());
        }
    }
}
like image 21
Shaun Luttin Avatar answered Oct 17 '22 06:10

Shaun Luttin