Friday, May 22, 2009

TCP Echo Client Server

When writing a TCP client server program in unix environment, elementary socket functions are used.As described at Stevens book these elementary functions are listed as:

-socket : To perform network I/O, the first thing a process must do is call the socket function.
-connect : used by a TCP client to establish a connection with a TCP server.
-bind : assigns a local protocol address to a socket.
-listen : called by only a TCP server and it converts an unconnected socket into a passive socket.
-accept :called by a TCP server to return the next completed connection from the front of the completed connection queue.
-fork :In Unix environment it is used to create a new process. By using fork() a process makes a copy of itself so that one copy can handle one operation while the other does another task.
-close : close a socket and terminate a TCP connection.

By using above elementary functions we can build our simple echo client and server programs. In this program, when a client connects to the server, the server will use fork() to create a new child process.

Newly created child process will serve the client. The child process terminates after the client sends its message and breaks the connection. The algorithm is as follows:

If this is the child process:
- Close listenfd since it is the responsibility of the parent to handle incoming connections.
- Display the message "Child process serving the client."
- Read the message from the client and display it.
- Close connectfd.
- Exit. The child process was created only to serve the client.

Since the client has transmitted its message and the connection is terminated, the child must terminate.

If this is the parent process:
- Close connectfd since it is the responsibility of the child process to serve the client.
- Go back to accept. Note that the server (parent) runs indefinitely.

client-source.c file is as follows:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main(int argc,char **argv)
{
    int clientfd;
    short int port_no;
    char msg[1000];
    struct sockaddr_in servaddr;
    char *server_ip_address;
    if(argc!=5){
       printf("Usage Format: ./client -a <IPAddress> -p <PortNumber>\n");
       printf("Sample Run: ./client -a 127.0.0.1 -p 2000\n");
       exit(1);
    }
    server_ip_address=(char *)argv[2];
    port_no = atoi(argv[4]);
    printf("Client will connect to the server at IP %s, port #%d\n", server_ip_address, port_no);
   
    // Create client socket.
    if((clientfd = socket(AF_INET, SOCK_STREAM, 0))<0 br="">       printf("Socket could not be created\n");
       printf("errno %i: %s\n",errno,strerror(errno));
       exit(1);
    }
    printf("Client socket created\n");
    errno=0;
    // Connect to the server client
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(port_no);
    if((inet_pton(AF_INET, server_ip_address, &servaddr.sin_addr))<=0){
      printf("inet_pton error for %s\n",server_ip_address);
      printf("errno %d: %s\n",errno,strerror(errno));
      exit(1);
    }
    errno=0;
    if((connect(clientfd, (struct sockaddr *) &servaddr, sizeof(servaddr)))<0 br="">      printf("Connect error\n");
      printf("errno %d: %s\n",errno,strerror(errno));
      exit(1);
    }
    printf("Client socket connected\n");
    // Read one line of message from the input and send it to the server.
    printf("Enter the message to be sent to the server: ");
    scanf("%s", msg);
    send(clientfd, msg, strlen(msg)+1, 0);
    close(clientfd);
    printf("Client sent the message and disconnected. \n");
    return 0;
}


server-source.c file is as follows:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
extern int errno;
int main(int argc,char **argv)
{
    int clientaddrlen, listenfd, connectfd, bytes_rcvd, listen_queue_size=1;
    short int port_no;
    char buffer[1000];
    struct sockaddr_in servaddr, clientaddr;
    pid_t  childpid;
    int status;
    if(argc!=3){
       printf("Usage Format: ./server -p <PortNumber>\n");
       printf("Sample Run: ./server -p 2000\n");
       exit(1);
    }
    port_no = atoi(argv[argc-1]);
    printf("Server running at port #%d\n", port_no);

    // Create server socket.
    if ( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
        fprintf(stderr, "Cannot create server socket! errno %i: %s\n",errno,strerror(errno));
        exit(1);
    }
    printf("Server socket created\n");
    // Bind (attach) this process to the server socket.
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(port_no);
    errno = bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
    if(errno < 0){
        printf("Server bind failure errno %i: %s\n",errno,strerror(errno));
        exit(1);
    }
    printf("Server socket is bound to port #%d\n", port_no);
    // Turn 'listenfd' to a listening socket. Listen queue size is 1.
    errno=listen(listenfd,listen_queue_size);
    if(errno < 0){
        printf("Server listen failure errno %i: %s\n",errno,strerror(errno));
        exit(1);
    }
    printf("Server listening with a queue of size %d. \n", listen_queue_size);
    // Wait for connection(s) from client(s).
    while (1){
        clientaddrlen = sizeof(clientaddr);
        connectfd = accept(listenfd, (struct sockaddr *) &clientaddr, &clientaddrlen);
        if(connectfd<0 br="">            printf("Server accept failure errno %d: %s\n",errno,strerror(errno));
            exit(1);
        }
        printf("A connection received from a client. Creating a child to serve the client.\n");
        if((childpid = fork()) == 0) { /* child process */
           close(listenfd); /* close listening socket */
           printf("Child process serving the client.\n");
           if (recv(connectfd, buffer, sizeof(buffer), 0 ) > 0){
               printf("Received message: %s\n", buffer);
           }
        close(connectfd);   /* parent closes connected socket */
        exit(1);
        }
        else if (childpid <0 br="" failed="" fork="" to="">             printf("Failed to fork\n");    
             printf("Fork error errno %d: %s\n",errno,strerror(errno));
        }
        else if(childpid != 0){  /* parent process */
             close(connectfd);   /* parent closes connected socket */  
             childpid = wait(&status);  
        }
    }
    return 0;
}

In order to avoid zombie processes created in the system, wait() function is used in the server main function.

Sample run can be created as follows:

1-)Create server and client executables :

$ cc -o server server_source.c
$ cc -o client client_source.c

2-)Run server at the same tab

$ ./server -p 2000

3-)Open a new tab, run client

$ ./client -a 127.0.0.1 -p 2000

You can get client_source.c and server_source.c files from the links.Application tested on ubuntu9.04.

No comments:

Post a Comment