Thursday 29 September 2011

TCP EchoServer

/*
  @filename : client.c
  @brief: Client Program
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>

#define MAX_LINE  256


void *SendReceive(void* sockid);

int main(int argc,char *argv[])
{
  struct sockaddr_in  serv_addr ;
  int           sockid,  tcp_port ;
  pthread_t     th;

  if(argc < 3 )
  {
    printf("usage: %s <server-address> <port> \n",argv[0]);
    return -1;
  }
  tcp_port = atoi(argv[2]) ;

  sockid = socket(AF_INET,SOCK_STREAM,0) ;
  if(sockid == -1 )
  {
    printf("Failed to get socket\n");
    return -2;
  }

  serv_addr.sin_family = AF_INET ;
  serv_addr.sin_addr.s_addr = inet_addr(argv[1]) ;
  serv_addr.sin_port = htons(tcp_port) ;

  if(connect(sockid,(struct sockaddr*)&serv_addr,sizeof(serv_addr)) != 0)
  {
    printf("Failed to Connect Server %s\n",argv[2]);
    return -3 ;
  }

  if (pthread_create (&th, NULL, SendReceive, (void*)sockid) != 0)
  {
    close(sockid) ;
    return -4 ;
  }

  pthread_join(th,NULL);
  close(sockid);

  return 0 ;
}

void *SendReceive(void* sockfd)
{

  char  buf[MAX_LINE],sendbuf[MAX_LINE];
  int   length,ret,sockid ;

  sockid =(int)sockfd;
  while(1)
  {
          memset(buf,0,sizeof(buf));
          if(fgets(buf,MAX_LINE,stdin) == NULL )
          {
                  printf ("received EOF from stdin\n");
                  break;
          }

          length = strlen(buf);

          if( (ret = send(sockid, &buf, length, 0)) <= 0 )
          {
                 printf("Client coultn't send message to server\n");
                  break;
          }

          ret = recv(sockid,&buf,MAX_LINE,0);
          if ( ret == 0 )
          {
                  break;
          }
          else if ( ret < 0 )
          {
                  printf("Error Occurs Err[#%d,%s]\n", errno, strerror(errno) );
                  break;
          }
          printf("%s",buf);
  }

  return NULL;
}



/*
@filename : server.c
@brief:  Concurrent  Server Program
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>


#define MAX_LINE 256

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
int         conn_count=0;
void *HandlingClient(void *);

int main(int argc,char *argv[])
{
  struct sockaddr_in  serv_addr,  client_addr;
  int           sockid, newsockid, tcp_port, len;
  pthread_t     tid;

  if(argc < 2 )
  {
    printf("usage: %s <port>\n", argv[0]);
    return -1;
  }
  tcp_port = atoi(argv[1]);

  sockid = socket(AF_INET,SOCK_STREAM,0);
  if(sockid < 0 )
  {
    printf("Failed to get socket\n");
    return -1;
  }
 len=1;
  setsockopt (sockid, SOL_SOCKET, SO_REUSEADDR, &len, sizeof(int));

  serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  serv_addr.sin_port = htons(tcp_port);
  if((bind(sockid,(struct sockaddr*)&serv_addr,sizeof(serv_addr))) != 0)
  {
    printf("Failed to bind\n");
    return -2;
  }

  pthread_mutex_init(&mutex, NULL);

  listen(sockid,128);

  while (1)
  {
    printf("active connections #%d\n", conn_count);

    len = sizeof(client_addr);
    if((newsockid = accept(sockid,(struct sockaddr*)&client_addr,&len)) == -1)
    {
       printf("Failed to accept the client request\n");
       continue;
    }

    if(pthread_create(&tid, NULL, HandlingClient, (void*)newsockid) != 0 )
    {
      printf("Failed to create thread\n");
      close (newsockid);
      sleep (1);
      continue;
    }

    pthread_mutex_lock(&mutex);
    conn_count ++;
    pthread_mutex_unlock(&mutex);
  }

  pthread_mutex_destroy(&mutex);
  return 0;
}
void *HandlingClient(void *id)
{
  char buf[MAX_LINE];
  int ret, sockid;

  sockid = (int)id;


  while(1)
  {

    ret = recv(sockid,&buf,MAX_LINE,0);
    if ( ret == 0 )
    {
      break;
    }
    else if ( ret < 0 )
    {
      printf("Error Occurs Err[#%d,%s]\n", errno, strerror(errno) );
      break;
    }

    if(!strncasecmp(buf,"bye",3))
    {
            pthread_mutex_lock(&mutex);
            conn_count--;
            pthread_mutex_unlock(&mutex);
            break;
    }

    /* send back to client*/
    if( (ret = send(sockid, &buf, ret, 0)) <= 0 )
    {
      printf("Client coultn't send message to server\n");
      break;
    }
  }

  close(sockid);

  return NULL;
}










Makefile:

MESSAGE="Echo Program Built Successfully"
MESSAGE1 = " ------------------------- "

CC      = gcc

CFLAGS  = -g -I./include

all:  server client
    @echo $(MESSAGE1)
    @echo $(MESSAGE)
    @echo $(MESSAGE1)

server: server.o
      ${CC} -o $@ server.o -lpthread

client: client.o
      ${CC} -o $@ client.o -lpthread

clean:
    -@rm *.o

distclean:  clean
    -@rm server client


4 comments:

  1. output example:
    #./server 1444
    #./client 10.20.50.2 1444
    hello
    hello
    bye
    #
    say "bye" to end up the session

    ReplyDelete
  2. instead of recv and send system call, we can use read and write , the difference is the fourth argument of recv/send where we can set the flags for socket recv and send operation , in case of read and write the same may not be possible !!

    ReplyDelete
  3. Why do we use bind ? Why bind used for server program , why not in client program ?

    - Basically bind means that binding port on the given ip address i.e interface
    - we can either bind on all interface available on the server , INADDR_ANY macro preferred for this
    - Also can restrict bind to single interface, so other interfaces can be used for other application in tern will react as multiple machines
    - Client no need to bind on port, since its a sender also might be some other server could have used the same port on client host


    ReplyDelete
    Replies
    1. With UDP, client have to bind() the socket in the client because UDP is connectionless, so there is no other way for the stack to know which program to deliver datagrams to for a particular port.

      If you could recvfrom() without bind(), you'd essentially be asking the stack to give your program all UDP datagrams sent to that computer. Since the stack delivers datagrams to only one program, this would break DNS, Windows' Network Neighborhood, network time sync

      Delete