TCP多客户端服务器程序

时间:2015-04-28 09:30:18

标签: c linux sockets tcp

我正在制作一个tcp多客户端服务器聊天室。我的TCP多客户端服务器和客户端的代码随附。我无法为我的程序添加另一个功能,即客户端删除。显示空闲客户端可以从服务器列表中删除。例如,如果客户端空闲30秒,则应由服务器删除。

请帮助我。这是服务器程序

 /* to compile me in Linux, type:  gcc -o server t1.c -lpthread */  
 /* t1.c - code for server program that uses TCP/IP */  
 /****SERVER CODE****/  
 #include <stdio.h>            
 #include <stdlib.h>            
 #include <sys/socket.h>          //for sockets  
 #include <sys/types.h>            
 #include <string.h>          //for string operations  
 #include <netinet/in.h>          //Internet Protocol family sockaddr_in defined here   
 #include <pthread.h>          // for the cosy POSIX threads  
 #include <arpa/inet.h>          // for inet_ntoa() function  
 #include <unistd.h>          //NULL constant defined here  
 #include <signal.h>          //for ctrl+c signal  
 #define BACKLOG 100          // connections in the queue  
 #define MAXDATALEN 256           //max size of messages to be sent  
 #define PORT 2012          //default port number  
 /*Note:  The port argument is optional. If no port is specified,  
  *     the server uses the default given by PORT.*/  
   struct Node                         /*structure to handle all clients*/  
     {  
       int port;  
        char username[10];  
       struct Node *next;  
     };  
   typedef struct Node *ptrtonode;  
   typedef ptrtonode head;  
   typedef ptrtonode addr;  
      void sendtoall(char *,int new_fd);     /*send chat msgs to all connected clients*/  
      void Quitall( );               /*send msg to all if server shuts down*/  
      head MakeEmpty( head h );          /*clearing list*/  
      void Delete( int port, head h );     /*delete client values on client exit*/  
      void Insert(int port,char*,head h,addr a);/*inserting new client */  
      void DeleteList( head h );          /*clearing list*/  
      void Display( const head h );          /*list all clients connected*/  
      void *Quitproc( );                /*signal handler*/  
      void *server(void * arg);          /*server instance for every connected client*/  
      void zzz();  
      char      username[10];          /*size of username*/  
      int sf2;  
      head h;                    /*variable of type struct head*/  
      char     buffer[MAXDATALEN];  
      /******main starts ***********/  
 int main(int argc, char *argv[]) {  
      int       sockfd,new_fd;                /*variables for socket*/  
      int      portnum;               /*variable for port numb if provided*/  
      struct sockaddr_in      server_addr;     /*structure to hold server's address */  
      struct sockaddr_in      client_addr;     /*structure to hold client's address */  
      int      cli_size,z;               /*length of address */  
      pthread_t      thr;               /*variable to hold thread ID */  
      int      yes=1;  
       addr a;                         /*variable of type struct addr*/  
      printf("\n\t*-*-*-*SERVER STARTED*-*-*-*\n");  
      /*=optional or default port argument=*/  
  if( argc == 2 )       
       portnum = atoi(argv[1]);  
  else   
      portnum = PORT; //if port number not given as argument then using default port  
      printf("PORT NO.:\t%d\n",portnum);  
      h = MakeEmpty( NULL );          //frees the list  
      /*=set info of server =*/  
      server_addr.sin_family=AF_INET;          /* set family to Internet   */  
      server_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* set IP address */  
      server_addr.sin_port=htons(portnum);  
       printf("IP ADDRESS:\t%s\n",inet_ntoa(server_addr.sin_addr));  
      /*=creating socket=*/  
      sockfd = socket(AF_INET, SOCK_STREAM, 0);  
   if(sockfd == -1){  
      printf("server- socket() error");     // debugging  
      exit(1);  
   }else  
      printf("socket\t\tcreated.\n");  
   if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,sizeof(int)) == -1) {  
      printf("setsockopt error");     // debugging  
      exit(1);  
   }else printf("reusing\t\tport\n");  
      /*=binding socket=*/  
   if(bind(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr))==-1){  
           printf("binding failed\n");     // debugging  
      exit(1);}  
   else   
      printf("binding\t\tsuccess.\n\n");  
 printf("\t\tPRESS CTRL+z TO VIEW ONLINE CLIENTS\n\n");  
      /*=socket on listening mode=*/  
      listen(sockfd, BACKLOG);  
      printf("waiting for clients......\n");  
   if (signal(SIGINT,(void *)Quitproc)==0)     //signal handler  
   if(signal(SIGTSTP, zzz)==0)               //signal handler  
   while(1){  
             cli_size=sizeof(struct sockaddr_in); //cli_size necessary as an argument for pthread_create  
             new_fd = accept(sockfd, (struct sockaddr *)&client_addr,&cli_size); //accepting connection from client   
             a =h ;  
           /*=sign in with name=*/  
           bzero(username,10);            
           if(recv(new_fd,username,sizeof(username),0)>0);  
             username[strlen(username)-1]=':';  
             printf("\t%d->%s JOINED chatroom\n",new_fd,username);  
             sprintf(buffer,"%s IS ONLINE\n",username);  
             Insert( new_fd,username, h, a );               //inserting newly accepted client socked fd in list  
             a = a->next;  
           /*=notify all clients about newly joining clients=*/   
             a = h ;  
           do{  
             a = a->next;  
             sf2 = a->port;  
           if(sf2!=new_fd)  
           send(sf2,buffer ,sizeof(buffer),0);  
           } while( a->next != NULL );  
              printf("server got connection from %s & %d\n\n",inet_ntoa(client_addr.sin_addr),new_fd); // debugging  
            struct Node args;                          //struct to pass multiple arguments to server function  
             args.port=new_fd;  
             strcpy(args.username,username);  
            pthread_create(&thr,NULL,server,(void*)&args);      //creating thread for every client connected   
            pthread_detach(thr);  
       } /*while end*/  
  DeleteList(h);                          //deleting all clients when server closes  
  close(sockfd);  
 }/********main ends***********/  
      /*******ALL FUNCTIONS DEFINED BELOW*********/  
 /* ==========Server function for every connected Client =========*/  
 void *server(void * arguments){  
  struct Node *args=arguments;  
  char     buffer[MAXDATALEN],ubuf[50],uname[10];     /* buffer for string the server sends */   
  char *strp;            
  char      *msg = (char *) malloc(MAXDATALEN);  
  int      ts_fd,x,y;  
  int      sfd,msglen;  
  ts_fd = args->port;      /*socket variable passed as arg*/  
  strcpy(uname,args->username);   
  addr      a;  
       /*=sending list of clients online=*/  
       a =h ;  
           do{  
           a = a->next;  
           sprintf( ubuf," %s is online\n",a->username );  
           send(ts_fd,ubuf,strlen(ubuf),0);  
           } while( a->next != NULL );  
       /*=start chatting=*/  
   while(1){  
        bzero(buffer,256);  
        y=recv(ts_fd,buffer,MAXDATALEN,0);  
       if (y==0)   
       goto jmp;  
        /*=if a client quits=*/  
       if ( strncmp( buffer, "quit", 4) == 0 ){  
      jmp:     printf("%d ->%s left chat deleting from list\n",ts_fd,uname);  
           sprintf(buffer,"%s has left the chat\n",uname);  
           addr a = h ;  
           do{  
                a = a->next;  
                sfd = a->port;  
                if(sfd == ts_fd)   
                 Delete( sfd, h );  
                if(sfd != ts_fd)   
                 send(sfd,buffer,MAXDATALEN,0);  
            }while ( a->next != NULL );  
                Display( h );  
                close(ts_fd);  
                free(msg);  
             break;  
            }  
      /*=sending message to all clients =*/  
      printf("%s %s\n",uname,buffer);  
      strcpy(msg,uname);  
      x=strlen(msg);  
      strp = msg;  
      strp+= x;  
      strcat(strp,buffer);  
      msglen=strlen(msg);  
        addr a = h ;  
      do{  
       a = a->next;  
       sfd = a->port;  
      if(sfd != ts_fd)   
        send(sfd,msg,msglen,0);  
      } while( a->next != NULL );  
       Display( h );  
       bzero(msg,MAXDATALEN);  
   }//end while  
    return 0;  
 }// end server  
 /*=====empties and deletes the list======*/  
 head MakeEmpty( head h )  
      {  
      if( h != NULL )  
           DeleteList( h );  
           h = malloc( sizeof( struct Node ) );  
      if( h == NULL )  
           printf( "Out of memory!" );  
           h->next = NULL;  
      return h;  
      }  
 /*======delete list=======*/  
 void DeleteList( head h )  
      {  
      addr a, Tmp;  
      a = h->next;   
      h->next = NULL;  
           while( a != NULL )  
           {  
           Tmp = a->next;  
           free( a );  
           a = Tmp;  
           }  
      }  
 /*===============inserting new clients to list==========*/  
 void Insert( int port,char *username, head h, addr a )  
      {  
      addr TmpCell;  
      TmpCell = malloc( sizeof( struct Node ) );  
           if( TmpCell == NULL )  
           printf( "Out of space!!!" );  
      TmpCell->port = port;  
      strcpy(TmpCell->username,username);  
      TmpCell->next = a->next;  
      a->next = TmpCell;  
      }  
 /*========displaying all clients in list==================*/  
 void Display( const head h )  
      {  
        addr a =h ;  
        if( h->next == NULL )  
      printf( "NO ONLINE CLIENTS\n" );  
        else  
        {  
           do  
           {  
             a = a->next;  
             printf( "%d->%s \t", a->port,a->username );  
           } while( a->next != NULL );  
           printf( "\n" );  
        }  
      }  
 /*===========client deleted from list if client quits================*/  
 void Delete( int port, head h ){  
        addr a, TmpCell;  
        a = h;  
    while( a->next != NULL && a->next->port != port )  
       a = a->next;  
   if( a->next != NULL ){             
      TmpCell = a->next;  
      a->next = TmpCell->next;   
      free( TmpCell );  
   }  
  }  
 /*======handling signals==========*/  
 void *Quitproc(){        
      printf("\n\nSERVER SHUTDOWN\n");  
      Quitall( );  
     exit(0);  
  }  
 /*===============notifying server shutdown===========*/  
 void Quitall(){  
   int sfd;  
   addr a = h ;  
   int i=0;  
    if( h->next == NULL ) {  
     printf( "......BYE.....\nno clients \n\n" );  
      exit(0);  
     } else {  
        do{  
          i++;  
         a = a->next;  
         sfd = a->port;  
         send(sfd,"server down",13,0);  
         } while( a->next != NULL );  
       printf("%d clients closed\n\n",i);         
       }  
  }  
 void zzz(){  
   printf("\rDISPLAYING ONLINE CLIENTS\n\n");  
   Display(h);  
  }  
 /*=====================================================================================================*/  

Here is the client program

 /* To compile me in Linux type: gcc -o client t2.c -lpthread*/  
 /* t2.c - code for client that uses TCP     */  
 /***CLIENT CODE*/  
 #include <stdio.h>  
 #include <stdlib.h>        
 #include <string.h>          //for string operations  
 #include <unistd.h>          //NULL constant defined here  
 #include <sys/types.h>            
 #include <sys/socket.h>          //for sockets  
 #include <netinet/in.h>          //Internet Protocol family sockaddr_in defined here   
 #include <pthread.h>          // for the cosy POSIX threads  
 #include <signal.h>          //for ctrl+c signal  
 #define MYPORT 2012     /* default port number */  
 #define MAXDATALEN 256  
 int      sockfd;  
 int      n,x;                         /*variables for socket*/  
 struct sockaddr_in serv_addr;          /* structure to hold server's address */  
 char      buffer[MAXDATALEN];  
 char      buf[10];            
 void *quitproc();  
 void* chat_write(int);  
 void* chat_read(int);  
 void *zzz();  
 /***************main starts************/  
 int main(int argc, char *argv[]){  
  pthread_t thr1,thr2;          /* variable to hold thread ID */  
     if( argc != 2 ){       
       printf("help:u need to put server ip\n");  
       exit(0);  
       }  
      /*=============socket creating==============*/  
      sockfd = socket(AF_INET, SOCK_STREAM, 0);  
      if (sockfd == -1)  
        printf ("client socket error\n");  
      else       
        printf("socket\t\tcreated\n");  
      /*===============set info===================*/  
      bzero((char *) &serv_addr, sizeof(serv_addr));  
      serv_addr.sin_family = AF_INET;  
      serv_addr.sin_port = htons(MYPORT);  
      serv_addr.sin_addr.s_addr = inet_addr(argv[1]);  
      /*=========for username=====================*/  
      bzero(buf,10);  
      printf("\nENTER YOUR NAME::");  
      fgets(buf,10,stdin);  
      __fpurge(stdin);       
      buf[strlen(buf)-1]=':';  
      /*=============client connect to server============*/  
      if(connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr))==-1)  
      {  
        printf("client connect error\n");  
        exit(0);  
      }  
      else  
        printf("%s connected to server\n",buf);  
      printf("\rYOU JOINED AS- %s",buffer-1);  
      send(sockfd,buf,strlen(buf),0);  
      pthread_create(&thr2,NULL,(void *)chat_write,(void *)sockfd);     //thread creation for writing  
      pthread_create(&thr1,NULL,(void *)chat_read,(void *)sockfd);     //thread creation for reading  
      pthread_join(thr2,NULL);  
      pthread_join(thr1,NULL);  
      return 0;  
 } /*main ends*/  
 /*======================================================================*/  
 /*======reading continously from socket=============*/  
 void* chat_read(int sockfd)  
 {  
      if (signal(SIGINT,(void *)quitproc)==0)  
      if(signal(SIGTSTP, (void *)zzz)==0)  
      while(1)  
      {  
        n=recv(sockfd,buffer,MAXDATALEN-1,0);  
         if(n==0){   
            printf("\nDUE TO SOME UNEXPECTED REASONS SERVER HAS BEEN SHUTDOWN\n\n");  
             exit(0);  
             }  
         if(n>0){  
              printf("\n%s ",buffer);  
            bzero(buffer,MAXDATALEN);  
             }  
      }//while ends  
 }  
 /*======writing continously to socket=============*/  
 void* chat_write(int sockfd)  
 {  
      while(1)  
      {  
        printf("%s",buf);  
          fgets(buffer,MAXDATALEN-1,stdin);  
         if(strlen(buffer)-1>sizeof(buffer)){  
             printf("buffer size full\t enter within %d characters\n",sizeof(buffer));  
             bzero(buffer,MAXDATALEN);  
             __fpurge(stdin);  
             }  
        n=send(sockfd,buffer,strlen(buffer),0);  
         if(strncmp(buffer,"quit",4)==0)  
             exit(0);  
         bzero(buffer,MAXDATALEN);  
      }//while ends  
 }  
 /*======handling signals==========*/  
 void *quitproc(){            //handling ctrl+d  
    printf("\rPLEASE TYPE 'quit' TO EXIT\n");  
 }  
 void *zzz(){               //handling ctrl+z  
    printf("\rPLEASE TYPE 'quit' TO EXIT\n");  
 }  
 /*=================================================================*/  

2 个答案:

答案 0 :(得分:0)

您可以尝试在服务器端调用setsockopt()时使用套接字选项SO_RCVTIMEO和SO_SNDTIMEO。因此,如果TCP堆栈在指定的超时时间内处于非活动状态,则TCP堆栈将终止此套接字上的任何连接。

可能/应该是这样的情况,即对server()函数中的recv()的阻塞调用(即服务器的客户端线程)将因错误而解除阻塞,但我记得确切的行为可能是OS-依赖。 否则,您需要循环检查任何打开的fd是否可以从另一个监视线程“到达”,例如,通过发送一个小状态消息。 (那个线程的循环时间当然必须高于配置的超时,否则你可以通过循环发送消息来保持连接处于活动状态。)

一旦检测到连接已关闭,您可以在服务器的客户端线程终止之前执行任何必要的错误/清除处理。

如果您还想从客户端启动连接关闭,请使用shutdown()函数,该函数还应告知服务器上相应的(阻塞)recv()调用以解除阻塞并显示错误。

答案 1 :(得分:0)

我看到你正在使用阻塞套接字。

在线程函数server中,您在while(1)在客户端套接字上执行recv。如果recv返回零或客户端发送“退出”消息,则关闭套接字ts_fd并通知其他客户端。

您可以使用SO_RCVTIMEO功能(使用setsockopt设置套接字选项)来暂停recv次呼叫。如果recv没有数据可以在指定时间内提供给应用程序,recv将返回EGAGAIN或EWOULDBLOCK。您可以将此视为客户端处于空闲状态并关闭连接。

struct timeval tv;

tv.tv_sec = 30;  /* 30 Secs Timeout */
tv.tv_usec = 0;  

setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval));