Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A TCP/IP Server written in C#

0.00/5 (No votes)
29 Dec 2003 1  
TCP/IP Server, which is running as a Windows Service waiting for client requests

Audience for this article

C# beginners with little knowledge about Networks and Protocols. Some knowledge of threads is required. Microsoft Development environment is required (Visual Studio 2003).

Brief description

Through this article I want to present readers with a TCP/IP Server which is started by a Windows Service and can be stopped when the Windows service is stopped. This TCP/IP server listens for client connection requests. When a request comes from a client, the job of the server is to read the data sent by the client and store it in a file (Name of the file also specified by the client) in C:\\TCP directory.

Hard words used

(Experienced readers can ignore/skim through this section)

Network: A group of computers connected ("Physically connected with a wire"/ "Connected without a wire using satellite communication i.e. via Internet")

Protocol: Set of rules for a communications

Example:

Let us say there are two things (Computers/people/anything) A and B.

A first says to B:                Hello B.
B then says back to A:            Hello A.
A then Says:                      How are you?
B then says:                      Good. How about you?
A then says:                      Good.
A again Says:                     Bye B.
B says:                           Bye A

If the above communication happens always, whenever A and B meet, then it can be termed as a greeting protocol between them. If A turns his face to the other side when he sees B (May be he fought with B) and ignores him; Then above communication can't be termed as protocol because above communication is not happening always.

TCP/IP (Transmission Control Protocol / Internet Protocol): It is a communication protocol used by many networks for communication.

I/P Address: Each machine in the internet/intranet world has been given an unique number in the form xxx.xxx.xxx.xxx, where xxx can be a integer ranging from 0 to 127.

Windows Service: On Windows 2000/XP go to Start/Control Panel/Administrative Tools/ We can see Windows Services Icon. A service is a computer program run by OS automatically whenever OS is started (normally without any intervention by users). A service normally has Start, Stop, Pause states. Also a service start up can be Automatic (By the OS when computer is booted), Manual( Has to be started by the users manually) or disabled (not functioning).

Server: A program waits for some one for communications (does both Listening and Talking).

Client: Some one trying to communicate (Again both Listening and Talking) with a server.

(You might wonder then what is difference between then. The difference is waiting. Let us take a restaurant as an example. Servers take orders and deliver food for customers (Clients) and clients go away once their eating is over. But Servers are ready for some other client until their duty is over.)

Socket: A connecting point for communication between The Server and a Client. Normally by saying Socket we mean we are talking about a connection point on the Server. But a client also needs a socket to talk with their Server. Now the important point to remember is that there is only one Server serving many clients. All clients are connected to server on the same point (Socket) on the server but have different individual connecting points on them. Let us take the example of telephones. When several people ("Clients") calling the same guy "Server" through a single telephone line where that guy called "Server" uses flash/switch mechanism that he has on his phone, for switching and talking to different "Clients" independently so that no one listens what "Server" is taking to the other people.

Port (Number): It is basically a positive integer number used to identify a point of communication.

Difference between Socket and Port: Socket is an abstract concept defined by an IP address along with its port number.

How this program works :-

This program works like this.

  1. This program starts when a windows service called "TCPService" Starts.
  2. When the Windows service is started a TCP/IP server is also started and that server is always be in a waiting mode for various client connections.
  3. When ever a client connection comes in, then Server creates a "Client Socket Listening" object to handle the communication between the client socket and server socket and again goes to waiting state for another connection request.
  4. The "Client Socket Listening" object has a thread started by the server, which takes care of the communication with the client.
  5. This communication is done as per a predefined protocol (See next section).
  6. The summary of protocol is, Client first send a file name and then some data, server has to store that in the server machine in C:\\TCP directory in a filename sent by the client.
  7. Once client sends every thing it wanted to send, then it will tear the connection with the "Client Socket Listening" object i.e. with the server. So this "Client Socket Listening" created by the server is ready for deletion.
  8. Server has a another low priority thread running which takes care of removing such "Ready For Deletion" "Client Socket Listening" objects.
  9. Once the Windows service is stopped server also stopped and all client connections goes away.

This Program's Protocol

The protocol works like this.

  1. Client first sends a file name that ends with "CRLF" (Carriage Return and Line Feed="\r\n")
  2. Then Server has to return, the length of the file name to the client for validation. If the length matches with the length what client had sent earlier, then client starts sending lines of information. Otherwise socket will be closed by the client.
  3. Each line of information that client sends will end with "CRLF".
  4. This Server has to store each line of information in a text file whose file name has been sent by the client earlier.
  5. As a last line of information client sends "[EOF]" line which also ends with "CRLF" ("\r\n"). This signals this Server for an end of file and intern this Server sends the total length of the data (lines of information excludes file name that was sent earlier) back to client for validation.

Since Server has to do this for many clients as soon as he receives a connection request from a client he instantiate a separate thread for implementing above protocol for each client request.

Program Notes

I am not going to explain how to write a windows Service using C# on Microsoft Development Environment. The best resource would be

The Service code has following methods OnStart and OnStop.

I have coded them as follows:

/************** TCPService.cs *****************/
protected override void OnStart(string[] args)
{
  // Create the Server Object ans Start it.

  server = new TCPServer();
  server.StartServer();
}
 
protected override void OnStop()
{
  // Stop the Server. Release it.

  server.StopServer();
  server=null;
}


/**************** TCPServer.cs ******************/
TCPServer is the Server class. Its constructor has following code.

try
{
  m_server = new TcpListener(ipNport);
  // Create a directory for storing client sent files.

  if (!Directory.Exists(TCPSocketListener.DEFAULT_FILE_STORE_LOC))
  {
    Directory.CreateDirectory(
      TCPSocketListener.DEFAULT_FILE_STORE_LOC);
  }
}
catch(Exception e)
{
  m_server=null;
}

TCPListener is the .NET framework class for creating a Server Socket. Following code is to create "C:\\TCP" directory if it is not present. The port on which server is listening is "ipNport" (30001). If we see TCPService code, it is calling StartServer method of TCPServer. It is given as follows:

public void StartServer()
{
  if (m_server!=null)
  {
    // Create a ArrayList for storing SocketListeners before

    // starting the server.

    m_socketListenersList = new ArrayList();

    // Start the Server and start the thread to listen client 

    // requests.

    m_server.Start();
    m_serverThread = new Thread(new ThreadStart(ServerThreadStart));
    m_serverThread.Start();

    // Create a low priority thread that checks and deletes client

    // SocktConnection objcts that are marked for deletion.

    m_purgingThread = new Thread(new ThreadStart(PurgingThreadStart));
    m_purgingThread.Priority=ThreadPriority.Lowest;
    m_purgingThread.Start();
  }
}
//SocketListener is the class that represents a client communication.

Server (TCPListener object) is not started until StartServer is called. See m_server.Start() in the above code. Also we can observe that a thread is started for listening client requests. It is represented by the method "ServerThreadStart" We can notice that another low priority thread (purging thread) is started for deleting "Client Socket Listening" objects if they become obsolete i.e. communication is completed between the Server and a client. There can be a different approach for doing the above purging process. But I have chosen this approach at this moment. Another interesting thing here is in C# (in C/C++ also) a thread is associated with a method. So the method associated with Server thread is "ServerThreadStart" and the method associated with Purging thread is "PurgingThreadStart".

private void ServerThreadStart()
{
  // Client Socket variable;

  Socket clientSocket = null;
  TCPSocketListener socketListener = null;
  while(!m_stopServer)
  {
    try
    {
      // Wait for any client requests and if there is any 

      // request from any client accept it (Wait indefinitely).

      clientSocket = m_server.AcceptSocket();

      // Create a SocketListener object for the client.

      socketListener = new TCPSocketListener(clientSocket);

      // Add the socket listener to an array list in a thread 

      // safe fashon.

      //Monitor.Enter(m_socketListenersList);

      lock(m_socketListenersList)
      {
        m_socketListenersList.Add(socketListener);
      }
      //Monitor.Exit(m_socketListenersList);


      // Start a communicating with the client in a different

      // thread.

      socketListener.StartSocketListener();
    }
    catch (SocketException se)
    {
      m_stopServer = true;
    }
  }
}

I am not explaining what is going to happen in the purging thread. If see above code first interesting spot is "m_server.AcceptSocket()". This is a blocked statement. That means unless a server is closed or unless a client request comes in, code execution is blocked at the point. If a client request comes in we can see that a "Client Socket Listening" class object, (here it is 'TCPSocketListener' class) is created and added to a hash table. It is added to the hash table because purging thread should delete it ('TCPSocketListener' object) once communication is completed. Another important line of code is "socketListener.StartSocketListener();". This is to start a separate thread in "Client Socket Listening" object (i.e. 'TCPSocketListener' class object).

Last (but not the least) point to mention is the lock block on 'm_socketListenersList' hash table. This is required because, there may a time where a new connection is being added and an expired connection has to deleted. lock makes sure that both addition and deletion happen with out any problem.

public void StopServer()
{
  if (m_server!=null)
  {
    // It is important to Stop the server first before doing

    // any cleanup. If not so, clients might being added as

    // server is running, but supporting data structures

    // (such as m_socketListenersList) are cleared. This might

    // cause exceptions.


    // Stop the TCP/IP Server.

    m_stopServer=true;
    m_server.Stop();

    // Wait for one second for the the thread to stop.

    m_serverThread.Join(1000);
    
    // If still alive; Get rid of the thread.

    if (m_serverThread.IsAlive)
    {
      m_serverThread.Abort();
    }
    m_serverThread=null;
    
    m_stopPurging=true;
    m_purgingThread.Join(1000);
    if (m_purgingThread.IsAlive)
    {
      m_purgingThread.Abort();
    }
    m_purgingThread=null;

    // Free Server Object.

    m_server = null;

    // Stop All clients.

    StopAllSocketListers();
  }
}

//Above method is self explanatory. 

//'StopAllSocketListers();' is method to delete all existing 

//client socket connections.


/**************** TCPSocketListener.cs ******************/

//Let us see the constructor of this class which is 

//very simple and doesn't need any explanation.


public TCPSocketListener(Socket clientSocket)
{
  m_clientSocket = clientSocket;
}

//Let us see the 'StartSocketListener' method.

public void StartSocketListener()
{
  if (m_clientSocket!= null)
  {
    m_clientListenerThread = 
      new Thread(new ThreadStart(SocketListenerThreadStart));

    m_clientListenerThread.Start();
  }
}

//What all this method does is to start a thread by 

//associating it with the method 'SocketListenerThreadStart'


private void SocketListenerThreadStart()
{
  int size=0;
  Byte [] byteBuffer = new Byte[1024];

  m_lastReceiveDateTime = DateTime.Now;
  m_currentReceiveDateTime = DateTime.Now;

  Timer t= new Timer(new TimerCallback(CheckClientCommInterval),
    null,15000,15000);

  while (!m_stopClient)
  {
    try
    {
      size = m_clientSocket.Receive(byteBuffer);
      m_currentReceiveDateTime=DateTime.Now;
      ParseReceiveBuffer(byteBuffer, size);
    }
    catch (SocketException se)
    {
      m_stopClient=true;
      m_markedForDeletion=true;
    }
  }
  t.Change(Timeout.Infinite, Timeout.Infinite);
  t=null;
}
There are few important points to worth mention.
  1. 'CheckClientCommInterval' is a another thread method which takes care of termination 'SocketListenerThreadStart()' thread method if there is no response from the client for more than 15 seconds.
  2. 'm_clientSocket.Receive(byteBuffer)' this is blocking receive for data from the client.
  3. 'ParseReceiveBuffer' method takes care of parsing data sent by the client whose details I am not describing here because it implements the protocol described above.
//********************* Client Code ***********************


//Here I am giving the Sample client code. 

//Which I feel doesn't need any explanation.


class TCPClient
{
  /// <summary>

  /// The main entry point for the application.

  /// </summary>

  [STAThread]
  static void Main(string[] args)
  {
    TCPClient client = null;
    client = new TCPClient("SatyaTest.txt\r\n");
    client = new TCPClient("SatyaTest1.txt\r\n");
    client = new TCPClient("SatyaTest2.txt\r\n");
    client = new TCPClient("SatyaTest3.txt\r\n");
    client = new TCPClient("SatyaTest4.txt\r\n");
    client = new TCPClient("SatyaTest5.txt\r\n");
  }

  private String m_fileName=null;
  public TCPClient(String fileName)
  {
    m_fileName=fileName;
    Thread t = new Thread(new ThreadStart(ClientThreadStart));
    t.Start();
  }

  private void ClientThreadStart()
  {
    Socket clientSocket = new Socket(AddressFamily.InterNetwork, 
        SocketType.Stream, ProtocolType.Tcp );
    clientSocket.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"),31001));

    // Send the file name.

    clientSocket.Send(Encoding.ASCII.GetBytes(m_fileName));
    
    // Receive the length of the filename.

    byte [] data = new byte[128];
    clientSocket.Receive(data);
    int length=BitConverter.ToInt32(data,0);

    clientSocket.Send(Encoding.ASCII.GetBytes(m_fileName+":"+
        "this is a test\r\n"));

    clientSocket.Send(Encoding.ASCII.GetBytes(m_fileName+":"+
          "THIS IS "));
    clientSocket.Send(Encoding.ASCII.GetBytes("ANOTHRER "));
    clientSocket.Send(Encoding.ASCII.GetBytes("TEST."));
    clientSocket.Send(Encoding.ASCII.GetBytes("\r\n"));
    clientSocket.Send(Encoding.ASCII.GetBytes(m_fileName+":"+
          "TEST.\r\n"+m_fileName+":"+"TEST AGAIN.\r\n"));
    clientSocket.Send(Encoding.ASCII.GetBytes("[EOF]\r\n"));

    // Get the total length

    clientSocket.Receive(data);
    length=BitConverter.ToInt32(data,0);
    clientSocket.Close();
  }

}

I hope I explained all the important parts of the program. If any one of you feel I did some thing terribly wrong or not clear, please haul at me. I will try to respond.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here