1

I have written code for server and multiple client using threads and sockets. Normally the clients exits by sending the 'exit' keyword to server but I want the server to also detect situation when the clients exits forcefully without sending 'exit' keyword to server, for example when user in middle of sending message to server presses cross button of client window. What I want is that server should detect this situation and displays some error code and continue receiving message from other clients connected to it.

Second problem I am facing how can I disconnect server even if multiple clients are connected to it. In my code I am using tcpListener.Stop() but when i use this method error message "server failed to start at ipaddress" is displayed and number of such windows opens is equivalent to number of clients server is listening. For example if server is listening to 1000 clients then 1000 such windows will open showing the earlier mentioned error message which doesn't look good from the point of person using this software. So How can I handle this situation? Also in this situation if clients again starts sending message to the server then is also starts receiving messages even though I have disconnected the server. The server should remain disconnected until the user restarts server.

Following is my code for server.

namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
    // Constants IP address of server and maximum number of clients server can connect.
    static class Constants
    {
        public const string IP = "127.0.0.1";
        public const int No_Of_Clients = 2;
    }

    // server port number
    int port_number;

    static IPAddress ipAddress = IPAddress.Parse(Constants.IP);

    TcpListener tcpListener;

    public Form1()
    {
        InitializeComponent();
        button1.Click += button1_Click;
        button2.Click += button2_Click;
        //this.FormClosing += Form1_FormClosing;
    }

    //Socket socketForClient;
    private void button1_Click(object sender, EventArgs e)
    {
        if (String.IsNullOrEmpty(textBox1.Text.Trim()))
        {
            System.Windows.Forms.MessageBox.Show("Port Number Empty", "Error");
        }
        else
        {
            port_number = int.Parse(textBox1.Text);
            createserver(Constants.No_Of_Clients);
            serveripaddress();
            infoBox1.Text = string.Format("The server is now listening at port {0} at ip address {1}", port_number, Constants.IP);
            infoBox1.Text = infoBox1.Text + "\r\n" + string.Format("The server can listen to maximum {0} number of clients", Constants.No_Of_Clients);
        }
    }

// this code disconnects the server
    private void button2_Click(object sender, EventArgs e)
    {
        try
        {
            tcpListener.Stop();
        }
        catch (Exception f)
        {
            MessageBox.Show(f.Message);
        }
    }

    public void serveripaddress()
    {
        serverip.Text = "Server IP : " + Constants.IP;
        //serverport.Text = "Port Number : " + port.ToString();
    }

    // Starts server
    private void createserver(int no_of_clients)
    {
        tcpListener = new TcpListener(ipAddress, port_number);
        tcpListener.Start();

        for (int i = 0; i < no_of_clients; i++)
        {
            Thread newThread = new Thread(new ThreadStart(Listeners));
            newThread.Start();
        }
    } // end of createserver();

//listen to client receiving messages     
public void Listeners()
    {
        Socket socketForClient;
        try
        {
            socketForClient = tcpListener.AcceptSocket();
        }
        catch
        {
            System.Windows.Forms.MessageBox.Show(string.Format("Server failed to start at {0}:{1}", Constants.IP, port_number), "Error");
            return;
        }

        if (socketForClient.Connected)
        {
            //System.Windows.Forms.MessageBox.Show("hello");
            string string1 = string.Format("Client : " + socketForClient.RemoteEndPoint + " is now connected to server.");
            infoBox1.Text = infoBox1.Text + "\r\n" + string1;
            NetworkStream networkStream = new NetworkStream(socketForClient);
            System.IO.StreamWriter streamWriter = new System.IO.StreamWriter(networkStream);
            System.IO.StreamReader streamReader = new System.IO.StreamReader(networkStream);
            string theString = "";
            while (true)
            {
                try
                {
                    theString = streamReader.ReadLine();
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.Message);

                }
               // if (streamReader.ReadLine() == null )
                //{
                  //  System.Windows.Forms.MessageBox.Show(string.Format("Server failed to start at {0}:{1}", Constants.IP, port_number), "Error");
               // }
                    if (theString != "exit")
                {
                    textBox2.Text = textBox2.Text + "\r\n" + "-----------------------------------------------------------------------------------";
                    string string2 = string.Format("Message recieved from client(" + socketForClient.RemoteEndPoint + ") : " + theString);
                    textBox2.Text = textBox2.Text + "\r\n" + string2;

                    // ASCII code for the message from client
                    string string3 = string.Format("ASCII Code for message is : ");
                    textBox2.Text = textBox2.Text + "\r\n" + string3;
                    string string4 = "";
                    foreach (char c in theString)
                    {
                        string4 += string.Format(System.Convert.ToInt32(c) + " ");

                    }
                    textBox2.Text = textBox2.Text + string4;

                    // Hex value of message from client
                    string hex = "";
                    foreach (char c in theString)
                    {
                        int tmp = c;
                        hex += String.Format("{0:x2}", (uint)System.Convert.ToUInt32(tmp.ToString()));
                    }
                    string string5 = string.Format("Hex Code for the message from client : " + hex);
                    textBox2.Text = textBox2.Text + "\r\n" + string5;

                    //sending acknowledgement to client
                    try
                    {
                        socketForClient.Send(new ASCIIEncoding().GetBytes("The string was recieved from Client(" + socketForClient.RemoteEndPoint + ") : " + theString));
                    }
                    catch (Exception e)
                    {
                        MessageBox.Show(e.Message);
                    }
                } // end of if loop

                // if exit from client
                else
                {
                    string string7 = string.Format("Client " + socketForClient.RemoteEndPoint + " has exited");
                    infoBox1.Text = infoBox1.Text + "\r\n" + string7;
                    break;
                }

            } // end of  while loop

            streamReader.Close();
            networkStream.Close();
            streamWriter.Close();

        } // end of if loop

        socketForClient.Close();

    }
}
} 
Roland Bär
  • 1,643
  • 3
  • 20
  • 28
prattom
  • 1,413
  • 7
  • 34
  • 59
  • http://stackoverflow.com/questions/11833808/how-to-detect-a-socket-disconnect-in-c-sharp – phillip Aug 12 '13 at 07:33
  • I'm using IOException while trying to read/write: `catch (IOException ex) {if (ex.InnerException is SocketException) { //Client disconnected! ...` – LS_ᴅᴇᴠ Aug 12 '13 at 07:53
  • Thanks for the reply but problem is that if I use this method an infinite no. of windows open showing message "an existing connection was forcibly closed by remote host" but I want the error message to exit after showing it only once. – prattom Aug 12 '13 at 08:31

1 Answers1

0

To be informed about closed client connections you have to send periodically a 'heartbeat' message to the client. If the client connection died the tcp/ip mechanism will inform you after the timeout that the connection died (can't remember the name of the exception). If the client wants to know if the connection died he has also to send a heartbeat message. This is needed as the tcp connection recognizes lost connections only if data is sent over this connection.

For the second problem you should keep a list of all the active clients (your variable socketForClient). When you want to end your server you close all the client connections (the clients in the list).

Roland Bär
  • 1,643
  • 3
  • 20
  • 28
  • Actually you do not have to send heartbeats. Usually, the server will wait for messages from the client either synchronously or asynchronously. This means that either `Read` or `BeginRead` will have been called. These methods will return (or jump to the delegate method in case of `BeginRead`) with an `IOException` if the client closes the connection. – Thorsten Dittmar Aug 12 '13 at 09:43
  • @ThorstenDittmar This is true as long as the connection is closed properly (complete close 'handshake') by the client. But the connection can also get lost by network failure, a firewall that kills the connection after some inactive time etc. For this situation you need the heartbeat, otherwise your Read waits forever. – Roland Bär Aug 12 '13 at 09:48
  • Thanks for the solution. For the second question I was thinking in similar way but how do I access variable socketForClient(which is in function Listener()) from function button2_Click. – prattom Aug 12 '13 at 09:50
  • Create a list (`List`) in your class Form1 (a member variable). Every client that connects you have to add to this list, every client that disconnects you have to remove. In `Listener()` you can iterate over this list to close all connections. – Roland Bär Aug 12 '13 at 09:56
  • Thanks for your help Roland. – prattom Aug 12 '13 at 11:18