Showing posts with label socket. Show all posts
Showing posts with label socket. Show all posts

Saturday, May 23, 2009

Passing Complex Types Between TCP Client and Server

Matrix Transpose with Readn&Writen:

In order to pass an array between a TCP client and server, it is suitable to place your array in a structure.

For this sample which is aimed to pass a 5X10 integer array from client to server and return back the transpose of that 5X10 array back to the client; define one structure for the 5X10 integer array you pass to the server, and another structure for the result 10X5 integer array that you return back to the client.

Place both structure definitions and Readn&Writen functions in a matrix.h file.Writen is used to send the structure to the server by client.Readn is used to read the reply, and then print the result at client side by using printf function as defined by Stevens code.

Server reads the arguments by calling Readn,takes the transpose of the 5X10 array passed to the server-side and by using Writen, 10X5 matrix is returned back to the client.

In addition to client and server source code files an extra header file is used to hold 2 structures and Readn&Writen method implementations.

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>
#include "matrix.h"

int main(int argc,char **argv)
{
    int clientfd;
    short int port_no;
    char msg[1000];
    struct sockaddr_in servaddr;
    char *server_ip_address;
    struct matrices matrix;
    struct matriceresult matriceresult;
    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");

    //fill initial matrice to be sent from the client to the server. 
    int i,j;
    for(i=0;i<5 br="" i="">      for(j=0;j<10 br="" j="">        matrix.matrice[i][j]=rand()%100;
    //print matrice
    printf("Printing initial 5X10 matrice at client side\n");
    int k,l;
    for(k=0;k<5 br="" k="">      for(l=0;l<10 br="" l="">        printf("%d\t",matrix.matrice[k][l]);
      printf("\n"); 
    }
    writen(clientfd,&matrix,sizeof(matrix));
    readn(clientfd,&matriceresult,sizeof(matriceresult));
    printf("Client received 10X5 matrix from server! Printing Transposed New Matrix at Client\n");
    int m,n;
    for(m=0;m<10 br="" m="">       for(n=0;n<5 br="" n="">         printf("%d\t",matriceresult.resultmatrice[m][n]);
       printf("\n"); 
    }
    close(clientfd);
    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>
#include "matrix.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;
    struct matrices matrix;
    struct matriceresult matriceresult;
    
    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);
        Readn(connectfd,&matrix,sizeof(matrix));
    //transpose the matrix that sent from client to server 
    int i,j;        
    for(i=0;i<5 br="" i="">      for(j=0;j<10 br="" j="">        matriceresult.resultmatrice[j][i]=matrix.matrice[i][j];
      }
    }
    //send resultMatrix to client side
    Writen(connectfd,&matriceresult,sizeof(matriceresult));
    }
    return 0;
}

matrix.h file is as follows:
#ifndef __matrix_h
#define __matrix_h
#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>

struct matrices{
int matrice[5][10];
};

struct matriceresult{
int resultmatrice[10][5];
};

ssize_t writen(int fd, const void *vptr, size_t n)
{
 size_t  nleft;
 ssize_t  nwritten;
 const char *ptr;

 ptr = vptr;
 nleft = n;
 while (nleft > 0) {
  if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
   if (errno == EINTR)
    nwritten = 0;  /* and call write() again */
   else
    return(-1);   /* error */
  }
  nleft -= nwritten;
  ptr   += nwritten;
 }
 return(n);
}
/* end writen */

void Writen(int fd, void *ptr, size_t nbytes)
{
 if (writen(fd, ptr, nbytes) != nbytes)
  printf("writen error");
}

ssize_t readn(int fd, void *vptr, size_t n)
{
 size_t nleft;
 ssize_t nread;
 char *ptr;

 ptr = vptr;
 nleft = n;
 while (nleft > 0) {
  if ( (nread = read(fd, ptr, nleft)) < 0) {
   if (errno == EINTR)
    nread = 0;  /* and call read() again */
   else
    return(-1);
  } else if (nread == 0)
   break;    /* EOF */

  nleft -= nread;
  ptr   += nread;
 }
 return(n - nleft);  /* return >= 0 */
}
/* end readn */

ssize_t Readn(int fd, void *ptr, size_t nbytes)
{
 ssize_t  n;

 if ( (n = readn(fd, ptr, nbytes)) < 0)
  printf("readn error");
 return(n);
}
#endif /* __matrix_h */


Sample run can be created as follows:

1-)Create server and client executables :

$ cc -o server server.c matrix.h
$ cc -o client client.c matrix.h

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.c, server.c and matrix.h files from the links.Application tested on ubuntu9.04.

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.