Search code examples
c#tcptcpclienttcplistener

How can I make a server program keep running no matter what happens to a client?


Take a look at the following two programs:

//Server

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace MyServerProgram
{
    class Program
    {
        static void Main(string[] args)
        {            
            IPAddress ip = IPAddress.Parse("127.0.0.1");
            int port = 2000;
            TcpListener listener = new TcpListener(ip, port);
            listener.Start();

            TcpClient client = listener.AcceptTcpClient();
            Console.WriteLine("Connected " + ((IPEndPoint)client.Client.RemoteEndPoint).Address);


            NetworkStream netStream = client.GetStream();

            BinaryReader br = new BinaryReader(netStream);

            try
            {
                while (client.Client.Connected)
                {
                    string str = br.ReadString();

                    Console.WriteLine(str);
                }
            }
            catch (Exception ex)
            {
                var inner = ex.InnerException as SocketException;
                if (inner != null && inner.SocketErrorCode == SocketError.ConnectionReset)
                    Console.WriteLine("Disconnected");
                else
                    Console.WriteLine(ex.Message);

                br.Close();
                netStream.Close();
                client.Close();
                listener.Stop();
            }
        }
    }
}


//Client

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace MyClientProgram
{
    class Program
    {
        static void Main(string[] args)
        {
            int port = 2000;
            TcpClient client = new TcpClient("localhost", port);

            NetworkStream netStream = client.GetStream();

            BinaryWriter br = new BinaryWriter(netStream);

            try
            {
                int i=1;
                while (client.Client.Connected)
                {
                    br.Write(i.ToString());
                    br.Flush();
                    i++;

                    int milliseconds = 2000;
                    System.Threading.Thread.Sleep(milliseconds);
                }
            }
            catch
            {
                br.Close();
                netStream.Close();
                client.Close();
            }
        }
    }
}

The problem I am facing with the Server is, the Server program exits as soon as the client is closed.

I want the server program to keep running no matter what a client does or happens to it.

How can I do that?


Solution

  • Try putting a while loop around your AcceptTcpClient (and associated logic). To paraphrase from your server code:

    boolean keepRunning = true;
    while (keepRunning) {
      TcpClient client = listener.AcceptTcpClient();
      Console.WriteLine("Connected ...") // and other stuff deleted.
    
      // while client connected...
          string str = br.ReadString();
          // check to see if we should continue running.
          keepRunning = ! "quit".equalsIgnoreCase(str);
      // Other stuff
    

    Note this is very insecure - any client regardless of where / who they are could terminate your server be sending a "quit" message to your server. In real life, you would probably require a more strict mechanism. Obviously with this mechanism, you will need your client to be able to generate the "quit" message text when you need it to do so.

    Another method is to run the whole server in a Thread. Then in another thread, have a method that an operator could use to close the server (e.g. a menu selection in a Swing Application).

    There are plenty of options you could choose from to "manage" the shutdown.

    Also, as written, your code is single threaded. That is, it will wait for a client to connect, deal with that client and then exit (or if you apply the keepRunning while loop modification wait for the next client to connect). But, only one client can connect to this server at any one time.

    To make it multi-threaded (can service multiple clients at one time), put the body of your server (the service code) into a Thread and invoke a new instance of the Thread to serve that client. After starting the service Thread, the main loop simply waits for the next client to connect. Thus, your main loop will become something like this:

    while (keepRunning) {
        TcpClient client = listener.AcceptTcpClient();
        Console.WriteLine("Connected ...") // and other stuff deleted.
    
        ServiceThread st = new ServiceThread(client);
        st.start ();
    }
    

    and the Service Thread will be something like:

    public class ServiceThread extends Thread {
      private TcpClient client;
      public ServiceThread (TcpClient client) {
        this.client = client;
      }
      @override
      public void run() {
        NetworkStream netStream = client.GetStream();
        BinaryReader br = new BinaryReader(netStream);
        try {
                while (client.Client.Connected) {
                // Stuff deleted for clarity
                }
             }
            catch (Exception ex) {
                // Exception handling stuff deleted for clarity.
            }
      }
    }