1

I have a simple client-server application which works like this: the server is always listening (in a separate thread) for a client connection (which sends the name of process that it wants the server to kill).

Here is the server:

private void btnStart_Click(object sender, EventArgs e)
{
  _port = int.Parse(comboBoxPorts.SelectedItem.ToString());

  _tcpListener = new TcpListener(_ipAddress, _port);

  _keepRunning = true;
  _listenerThread = new Thread(Listen);
  HandleListenerThreadStartListenEvent += HandleListenerThreadStartedEventMethod;
  ListenerThreadStartedEvent += HandleListenerThreadStartListenEvent;
  _listenerThread.Start();
}

private void btnStop_Click(object sender, EventArgs e)
{ 
  if (_tcpListener != null)
  {
    _keepRunning = false; 

    if (_tcpListener.Server.Connected)
    {
      _tcpListener.Server.Disconnect(true); 
    } 

    _tcpListener.Stop(); 
  }

  labelServerStatus.Text = "Server is stopped";   
  comboBoxPorts.Enabled = true; 
  btnStart.Enabled = true; 
  btnStop.Enabled = false; 
} 

private void Listen()
{
  try
  {
    _tcpListener.Start();
    OnListenerThreadStartListenEvent(); // just update the GUI
  }
  catch(Exception e)
  {
    MessageBox.Show("Port " + _port + " is NOT available." + Environment.NewLine +
                    "Please choose another one: " + e.Message);
    return;
  }

  _keepRunning = true;
  string ballonMessage = "Socket Server Running at " + _ipAddress + ", port: " + _port;
  notifyIcon1.ShowBalloonTip(2000, "Simplex Listener", ballonMessage, ToolTipIcon.Info);

  while (_keepRunning)
  {
    try
    {          
      #region using AcceptSocket()

      _clientSocket = _tcpListener.AcceptSocket();
      string checkString = string.Empty;
      IPAddress ipOfClient = ((IPEndPoint) _clientSocket.LocalEndPoint).Address;
      notifyIcon1.ShowBalloonTip(2000, "Simplex Listener", "New client has connected from ip " + ipOfClient, ToolTipIcon.Info);

      byte[] buffer = new byte[SIZE_OF_BUFFER];
      int bytesReceived = _clientSocket.Receive(buffer);

      // Concatenate chars as bytes to a received string.
      for (int i = 0; i < bytesReceived; i++)
        checkString += Convert.ToChar(buffer[i]);



      //..... getting the name of process and kill it (and than open it...
      RestartProcess(nameOfProcess, windowName, pathToExeFile);

      // Client is waiting to know operation is complete- so send him some char...
      ASCIIEncoding encoder = new ASCIIEncoding();
      _clientSocket.Send(encoder.GetBytes("v"));
      _clientSocket.Disconnect(true);
      _clientSocket.Close();
      #endregion
    }
    catch (Exception )
    {    
    }
  }
}

The client side:

public void RestartTheSoftwareInServerComputer(string ipOfServer, int portNumber)
{
  TcpClient client = new TcpClient();

  if (_serverEndPoint == null)
  {
    _serverEndPoint = new IPEndPoint(IPAddress.Parse(ipOfServer), portNumber);     
  }

  client.Connect(_serverEndPoint);

  // Send the command to the server:
  NetworkStream clientStream = client.GetStream();
  ASCIIEncoding encoder = new ASCIIEncoding();
  byte[] buffer = encoder.GetBytes("....detailsOfProcess....");

  clientStream.Write(buffer, 0, buffer.Length);
  clientStream.Flush();

  // Now, wait for the server's response [which means the process had been restart].
  NetworkStream stream = client.GetStream();
  byte[] bytes = new byte[5];
  stream.Read(bytes, 0, 5);
  string response = Encoding.UTF8.GetString(bytes, 0, 1);

  if (response.Equals("x"))
  {
    throw new Exception("Failed to restart X software.");
  }

  stream.Close();
  client.Close();
}

When I stop and restart the server (when no client had connected), then everything is OK.

The problem is when the server got some client connected, and is restarted, then the client has disconnected and the server needs to be restarted. When we hit the "START SERVER" again it will get the exception:

Only one usage of each socket address (protocol/network address/port) is normally permitted.

How should I close the port?

alk
  • 69,737
  • 10
  • 105
  • 255
user1165147
  • 43
  • 1
  • 1
  • 9
  • 1
    You have to terminate connection before restarting the server or else server will be stuck in time-wait state and will not be able to use the same address/port pair until timeout. This happens because you only close your application, but underlying socket still remains active in OS stack. – KBart Feb 07 '13 at 06:36
  • I understand something is still connected but I can't understand what. In the client I disconnect the TCPCLIENT and in the server I do: '_clientSocket.Disconnect(true); _clientSocket.Close();' What do I missing? – user1165147 Feb 07 '13 at 07:09
  • 1
    What you forget is that you actually have **2 active sockets** - one listens, another is for connected client. When you exit, only *client socket* is closed, and *listening socket* remains active. I'm not much into C#, but I believe [TcpListener.Stop method](http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.stop.aspx) is what you looking for. As stated in method description: *The Stop method also closes the underlying Socket*. – KBart Feb 07 '13 at 07:17
  • please edit your question and add this code there, it is unreadable now. – KBart Feb 07 '13 at 07:26
  • Here is something I forgot: When stopping the server here is the code: `private void btnStop_Click(object sender, EventArgs e) { if (_tcpListener != null) { _keepRunning = false; if (_tcpListener.Server.Connected) { _tcpListener.Server.Disconnect(true); } _tcpListener.Stop(); } labelServerStatus.Text = "Server is stopped"; comboBoxPorts.Enabled = true; btnStart.Enabled = true; btnStop.Enabled = false; } ` So I do close the server socket.... – user1165147 Feb 07 '13 at 07:30
  • Edit the **question**, not the comment. There is "edit" button in the lower left corner of your question (just below the tags). – KBart Feb 07 '13 at 07:31
  • Sorry that is how it pastes the code... I'm trying to newLine with shift+enter but it doesn't show it in this format. – user1165147 Feb 07 '13 at 07:37
  • You might like to set the `TcpListener`'s property `ExclusiveAddressUse` to false prior to starting the listener. – alk Feb 07 '13 at 07:47
  • Still not working... if a client connected and than disconnected, and after stopping the server you can't start the server on the same port again. – user1165147 Feb 07 '13 at 08:09
  • Does starting the server, stopping it and starting it again work if no client had connected in the mean time? – alk Feb 07 '13 at 08:18
  • Yes, If starting the server, and no clients connect and then stop the server it working fine. I can start+stop how many times I want to. There is something with the client's connection, but I can't find how to close it. I do everything I could find in GOOGLE, still port is stuck. – user1165147 Feb 07 '13 at 08:26
  • @user1165147 - If you are not bothered about understanding the underlying socket usage have you possibly considered using a network library such as networkcomms.net? There is a tutorial which demonstrates an almost identical client server setup here http://www.networkcomms.net/how-to-create-a-client-server-application-in-minutes/ – MarcF Feb 07 '13 at 10:15
  • @MarcF thanks for your advice but I work in a global company and I'm not sure it is legal to use external dlls... (actually I'm pretty sure it doesn't). :) – user1165147 Feb 07 '13 at 11:13
  • @KBart You are mistaken. When a process exits, all its resources are released, including both listening and accepted sockets. The cause of the TIME_WAIT state is the accepted socket, not the listening socket, as the OP's observations show. – user207421 Feb 07 '13 at 11:28
  • @EJP Do you have any suggestion? What I haven't do with the accepted socket? BTW- even if I close both applications (stop the debug) and restart it the port is still stucked. – user1165147 Feb 07 '13 at 11:49
  • @user1165147 - You could always purchase a commercial licence for networkcomms.net. Then its completely legal. – MarcF Feb 07 '13 at 14:42

1 Answers1

0

When you exit your server, you should call _tcpListener.Stop() to close the main socket that the server is listening on.

Edit: You could also try to call _listenerThread.Join() in your stop button click, to wait for the listener thread to finish, before starting the next one.

private void btnStop_Click(object sender, EventArgs e)
{ 
  if (_tcpListener != null)
  {
    _keepRunning = false;

    if (_tcpListener.Server.Connected)
    {
      _tcpListener.Server.Disconnect(true); 
      _tcpListener.Stop();
      if (_clientSocket != null)
      {
        _clientSocket.Close();
        _clientSocket = null;
      }
      _listenerThread.Join();
    }
  }

  labelServerStatus.Text = "Server is stopped";   
  comboBoxPorts.Enabled = true; 
  btnStart.Enabled = true; 
  btnStop.Enabled = false; 
} 

EDIT 2:

here is a windows form that does similar to your server. I didn't need a client, just use "telnet localhost 49152" from a command prompt to 'pretend' to be the client connecting.

public partial class Form1 : Form
{
    private TcpListener _tcpListener;
    private bool _keepRunning;
    private Thread _listenerThread;
    private Socket _clientSocket;


    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        var address = IPAddress.Parse("127.0.0.1");
        _tcpListener = new TcpListener(address, 49152);

        _keepRunning = true;
        _listenerThread = new Thread(Listen);
        _listenerThread.Start();

        button1.Enabled = false;
        button2.Enabled = true;
    }

    private void Listen()
    {
        try
        {
            _tcpListener.Start();
        }
        catch (Exception e)
        {
            MessageBox.Show(e.Message);
            return;
        }

        _keepRunning = true;

        while (_keepRunning)
        {
            try
            {
                _clientSocket = _tcpListener.AcceptSocket();

                var buffer = new byte[8192];
                var bytesReceived = _clientSocket.Receive(buffer);
                var checkString = String.Empty;

                if (_keepRunning)
                {
                    // bytesReceived can be 0 if the remote socket disconnected
                    if (bytesReceived > 0)
                    {
                        checkString = Encoding.ASCII.GetString(buffer, 0, bytesReceived);

                        // Client is waiting to know operation is complete- so send him some char...
                        var encoder = new ASCIIEncoding();
                        _clientSocket.Send(encoder.GetBytes("v"));
                    }
                    if (_clientSocket.Connected) _clientSocket.Disconnect(true);
                }
                _clientSocket.Close();
            }
            catch (Exception ex)
            {
                // really should do something with these exceptions
            }
        }
    }

    private void button2_Click(object sender, EventArgs e)
    {
        if (_tcpListener != null)
        {
            _keepRunning = false;

            if (_tcpListener.Server.Connected)
            {
                _tcpListener.Server.Disconnect(true);
            }
            _tcpListener.Stop();

            if (_clientSocket != null)
            {
                _clientSocket.Close();
                _clientSocket = null;
            }
            _listenerThread.Join();
        }

        button1.Enabled = true;
        button2.Enabled = false;
    }
}

There are a lot of problems with this code, e.g. sharing variables across threads etc. but on my machine the Join doesn't seem to block for any length of time. The problem with _keepRunning is that on certain systems it's possible for one thread to not see the change from true to false, because it gets optimised or cached. You should really use some form of thread synchronisation, make it volatile, or wrap it in a lock etc. I'd suggest you have a read here about this. I'd also suggest you have a read up on Sockets too, or if as other commenters have mentioned, if you aren't interested in learning about all the idiosyncrasies of sockets and threading, perhaps you should look for a higher level library that hides it all?

Matt
  • 2,984
  • 1
  • 24
  • 31
  • I've edited my answer to suggest that you wait for the first listener thread to exit before starting another one, using Thread.Join. Placing it after the _tcpListener.Stop will then cause the thread to block until the current connection has completed. If you need to force the client to disconnect you'll need to stick a call to force the client to disconnect before the call to join. – Matt Feb 07 '13 at 08:33
  • First, I've noticed that the if (_tcpListener.Server.Connected) always returns false so I wrote it like that: `if (_tcpListener.Server.Connected) { _tcpListener.Server.Disconnect(true); } _tcpListener.Stop(); _listenerThread.Join(); } //... }` This cause the GUI to freeze. it almost 5 minutes that it is stucked... it's like the thread can't be stoped for some reason. – user1165147 Feb 07 '13 at 09:30
  • The reason is most likely the calls to _tcpListener.AcceptSocket() or _clientSocket.Receive(). These calls will block until something happens. Although, the _tcpListener.Stop() should cancel the AcceptSocket call. As I said in my previous comment, just before the Thread.Join you should probably check if _clientSocket is not null, and if it's connected call Disconnect(). I've updated my answer to include the change. – Matt Feb 07 '13 at 22:16
  • I'd also suggest you check the _keepRunning flag immediately after calling _clientSocket.Receive(), and break from the while loop if it's false. Also check if receive returned 0 bytes as that is an indication that the remote socked disconnected. – Matt Feb 08 '13 at 03:25
  • thanks for your answers but unfortunately it doesn't help... the "_tcpListener.Server.Connected" returns false. Even if I put the other condition `if (_clientSocket != null)` outside the first condition it gets stuck because of the Join(). – user1165147 Feb 10 '13 at 07:10
  • Did you also call _clientSocket.Disconnect() before calling Join? If the client socket is connected, and your thread is in the call to _clientSocket.Receive() the thread will block waiting for the client to send the command. This will cause Join to wait until the client either disconnects, or is disconnected. Maybe you should look at implementing timeouts so that things don't wait for ever. – Matt Feb 10 '13 at 23:00
  • Hi, The code which get stucked: `if (_tcpListener != null){ _keepRunning = false; _tcpListener.Stop(); if (_tcpListener.Server.Connected) { _tcpListener.Server.Disconnect(true); } if (_clientSocket != null) { _clientSocket.Close(); _clientSocket = null; } _listenerThread.Join(); }` It get stucked in the Join() part. Whose timeout I should set? the TcpListener? – user1165147 Feb 12 '13 at 06:48
  • The join waits for the thread to complete. So your Listen() thread is still running, or stuck waiting for the client socket to do something. You need to work out what that is. I'd suggest adding if(!_keepRunning){_clientSocket.Close(); return;} after the call to _clientSocket.Receive(buffer); I'd also suggest you try to debug both client and server to see why your listen thread is doing what it's doing. Using shared variables for thread synchronisation is also not a good idea, depending on which version of the runtime and os you are using it could change behaviour. – Matt Feb 12 '13 at 22:04