Teth

posix multithreaded primitive chat server

May 28th, 2018
129
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 10.80 KB | None | 0 0
  1. #include <sys/types.h>
  2. #include <sys/socket.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <netinet/in.h>
  6. #include <unistd.h>
  7. #include <netdb.h>
  8. #include <errno.h>
  9. #include <fcntl.h>
  10. #include <string.h>
  11. #include <pthread.h>
  12. //#include "mychat.h"
  13.  
  14. struct chatter {
  15.   char server[61];
  16.   char name[21];
  17.   char password[21];
  18.   int sock;
  19.   int whispers;
  20.   int room;
  21.   pthread_t thread;
  22.   struct chatter * next;
  23.   };
  24.  
  25.  struct chatter * find(char *);
  26.  
  27.  pthread_mutex_t lock_writ=PTHREAD_MUTEX_INITIALIZER;
  28.  pthread_cond_t  cond_writ=PTHREAD_COND_INITIALIZER;
  29.  int writers=0;
  30.  pthread_mutex_t lock_read=PTHREAD_MUTEX_INITIALIZER;
  31.  pthread_cond_t  cond_read=PTHREAD_COND_INITIALIZER;
  32.  int readers=0;
  33.  pthread_mutex_t lock_head=PTHREAD_MUTEX_INITIALIZER;
  34.  struct chatter * head=NULL;
  35.  pthread_mutex_t lock_done=PTHREAD_MUTEX_INITIALIZER;
  36.  struct chatter * done=NULL;
  37.  
  38. int writestr(int fd, char * msg) {
  39.      return send(fd, msg, strlen(msg), 0);
  40.  }
  41.  
  42. int readstr(int fd, char * msg) {
  43.      return recv(fd, msg, 256, 0);
  44.  }
  45.  
  46.  
  47.  void writer_lock(void) {
  48.    // Tell the world another writer is waiting
  49.    pthread_mutex_lock(&lock_writ);
  50.    writers++;
  51.    pthread_mutex_unlock(&lock_writ);
  52.    // Wait for readers to get out of the way
  53.    pthread_mutex_lock(&lock_read);
  54.    while (readers>0)
  55.     pthread_cond_wait(&cond_read,&lock_read);
  56.    pthread_mutex_unlock(&lock_read);
  57.    // Now that the readers are out of the way, we can
  58.    // try to lock the client list then do our work
  59.    pthread_mutex_lock(&lock_head);
  60.    }
  61.    
  62. void writer_unlock(void) {
  63.   //release head
  64.    pthread_mutex_unlock(&lock_head);
  65.   // Now tell the world one less writer is active
  66.   pthread_mutex_lock(&lock_writ);
  67.   writers--;
  68.   if (writers==0) pthread_cond_signal(&cond_writ);
  69.   pthread_mutex_unlock(&lock_writ);
  70.   }
  71.  
  72.  // READER PROCEDURES
  73.  
  74.  void reader_access(void) {
  75.   // Make sure no writers are interested in making changes
  76.   pthread_mutex_lock(&lock_writ);
  77.   while (writers>0)
  78.     pthread_cond_wait(&cond_writ,&lock_writ);
  79.   // Tell the world another reader is busy accessing the list
  80.   pthread_mutex_lock(&lock_read);
  81.   readers++;
  82.   pthread_mutex_unlock(&lock_read);
  83.   pthread_mutex_unlock(&lock_writ);
  84.   }
  85.  
  86. void reader_finish(void) {
  87.   // Tell the world I am done accessing the list
  88.   pthread_mutex_lock(&lock_read);
  89.   readers--;
  90.   if (readers==0) pthread_cond_signal(&cond_read);
  91.   pthread_mutex_unlock(&lock_read);
  92.   }
  93.  
  94. // SOCKET FUNCTIONS
  95.  
  96. int setup_post(short port_number) {
  97.   struct sockaddr_in puffaddr;
  98.   int sock;
  99.   if ((sock=socket(AF_INET,SOCK_STREAM,0))==-1)
  100.     { perror("Could not create socket!"); exit(1);}
  101.   puffaddr.sin_family=AF_INET;
  102.   puffaddr.sin_addr.s_addr=INADDR_ANY;
  103.   puffaddr.sin_port=htons(port_number);
  104.   if (bind(sock,(struct sockaddr *)(&puffaddr),sizeof(puffaddr))<0)
  105.     { perror("Could not bind socket!"); exit(1);}
  106.   if (listen(sock,20)<0)
  107.     { perror("Could not set max backlog size."); exit(1);}
  108.   return sock;
  109.   }
  110.  
  111. struct chatter * listening_post(int sock) {
  112.   unsigned char * addy;
  113.   struct sockaddr_in client;
  114.   int length;
  115.   struct hostent * hostptr;
  116.   struct chatter * person;
  117.   int fd;
  118.   int fd_flags;
  119.   length=sizeof(struct sockaddr);
  120.   fd=accept(fd,(struct sockaddr *)(&client),&length);
  121.   if (fd==-1) return NULL;
  122.   if ((fd_flags=fcntl(fd,F_GETFL,0))==-1) {
  123.     close(fd); return NULL;
  124.     } else {
  125.     fd_flags|=O_NONBLOCK;
  126.     if (fcntl(fd,F_SETFL,fd_flags)==-1) {
  127.       close(fd); return NULL; }
  128.     }
  129.   person=(struct chatter *)malloc(sizeof(struct chatter));
  130.   //figure out domain name... if not possible, use ip addy
  131.   hostptr=gethostbyaddr((char *)(&client.sin_addr.s_addr),4,AF_INET);
  132.   if (hostptr==NULL) {
  133.     addy=(unsigned char *)(&client.sin_addr.s_addr);
  134.     sprintf(person->server,"%d.%d.%d.%d",addy[0],addy[1],addy[2],addy[3]);
  135.     } else
  136.     strcpy(person->server,hostptr->h_name);
  137.   person->sock=fd;
  138.   person->next=NULL;
  139.   sprintf(person->name,"Connecting...");
  140.   person->whispers=1;
  141.   person->room=1;
  142.   return person;
  143.   }
  144.  
  145.  // USER COMMAND FUNCTIONS
  146.  
  147. void broadcast(char * message) {
  148.   struct chatter * current;
  149.   reader_access();
  150.   current=head;
  151.   while (current!=NULL) {
  152.     if (current->room==1) writestr(current->sock,message);
  153.     current=current->next;
  154.     }
  155.   reader_finish();
  156.   }
  157.    
  158. void whisper(struct chatter * person, char * buf) {
  159.   int buflen;
  160.   char * who_name;
  161.   char * say_what;
  162.   char wbuf[512];
  163.   struct chatter * who_to;
  164.   reader_access();
  165.   buflen=strlen(buf);
  166.   strtok(buf," \t");
  167.   who_name=strtok(NULL," \t");
  168.   if (who_name!=NULL) say_what=who_name+strlen(who_name)+1;
  169.   if (who_name!=NULL && say_what-buf<buflen) {
  170.     if ((who_to=find(who_name))!=NULL) {
  171.       if (who_to->whispers==1) {
  172.         sprintf(wbuf,"<%s> %s\n",person->name,say_what);
  173.         writestr(who_to->sock,wbuf);
  174.         } else {
  175.         sprintf(wbuf,"Sorry, %s isn't listening to whispers right now.\n");
  176.         writestr(person->sock,wbuf);
  177.         }
  178.       } else if (who_to==NULL) {
  179.         sprintf(wbuf,"Sorry, %s doesn't seem to be on.\n",who_name);
  180.         writestr(person->sock,wbuf);
  181.         }
  182.       } else {
  183.       if (who_name==NULL)
  184.         sprintf(wbuf,"It helps to enter the command correctly.\n");
  185.         else
  186.         sprintf(wbuf,"It helps to actually enter something to whisper.\n");
  187.       writestr(person->sock,wbuf);
  188.       }
  189.   reader_finish();
  190.   }
  191.    
  192. void wholist(struct chatter * person) {
  193.     struct chatter * current;
  194.     char buf[65536];
  195.     char * whb;
  196.     int i;
  197.     int n=0;
  198.     reader_access();
  199.     current=head;
  200.     whb=buf;
  201.     sprintf(whb,"---------- Who's online ----------\n");
  202.     i=strlen(whb);
  203.     whb+=i;
  204.     while (current!=NULL) {
  205.         n++;
  206.         sprintf(whb,"[%s:%s]\n",current->server,current->name);
  207.         i=strlen(whb);
  208.         whb+=i;
  209.         current=current->next;
  210.         }
  211.     sprintf(whb,"----------------------------------\n");
  212.     i=strlen(whb);
  213.     whb+=i;
  214.     sprintf(whb,"Total users online: %d\n",n);
  215.     i=strlen(whb);
  216.     whb+=i;
  217.     sprintf(whb,"----------------------------------\n");
  218.     i=strlen(whb);
  219.     whb+=i;
  220.     writestr(person->sock,buf);
  221.     reader_finish();
  222. }
  223.  
  224. // UTILITY FUNCTIONS FOR MAIN CLIENT ROUTINE
  225.  
  226. struct chatter * find(char * nomen) {
  227.     struct chatter * current;
  228.     current=head;
  229.     while (current!=NULL && !(strcmp(current->name,nomen)==0))
  230.         current=current->next;
  231.     return current;
  232. }
  233.  
  234. void removal(struct chatter * person) {
  235.     struct chatter * current;
  236.     struct chatter * previous;
  237.     current=head;
  238.     previous=NULL;
  239.     while (current!=NULL && person!=current) {
  240.         previous=current;
  241.         current=current->next;
  242.     }
  243.     if (previous==NULL)
  244.         head=person->next;
  245.         else
  246.         previous->next=person->next;
  247.     person->next=NULL;
  248. }
  249.  
  250. void disconnect(struct chatter * ego) {
  251.     // REMOVE CLIENT FROM CLIENTS LIST
  252.     writer_lock();
  253.     removal(ego);
  254.     writer_unlock();
  255.     //CLOSE SOCKET
  256.     close(ego->sock);
  257.     printf("Connection from %s closing.\n",ego->server);
  258.     // PLACE SELF ON THE DONE LIST SO I CAN BE WAITED ON
  259.     pthread_mutex_lock(&lock_done);
  260.     ego->next=done;
  261.     done=ego;
  262.     pthread_mutex_unlock(&lock_done);
  263.     printf("The thread is ready to be disposed of.\n");
  264.     pthread_exit(NULL);
  265. }
  266.  
  267. void await_input(int fd) {
  268.     fd_set readset;
  269.     FD_ZERO(&readset);
  270.     FD_SET(fd,&readset);
  271.     select(fd+1,&readset,NULL,NULL,NULL);
  272. }
  273.  
  274. void login(struct chatter * ego) {
  275.     char buf[256];
  276.     char nomen[21];
  277.     int i;
  278.     int flag=3;
  279.     while (!(flag==0)) {
  280.         sprintf(buf,"Enter your username, please:\n");
  281.         writestr(ego->sock,buf);
  282.         do {
  283.             await_input(ego->sock);
  284.             if (readstr(ego->sock,buf)==0) disconnect(ego);
  285.         } while (!(strlen(buf)>0 && strlen(buf)<=20));
  286.         strcpy(nomen,buf);
  287.         printf("Attempted login by: %s\n",nomen);
  288.         reader_access();
  289.         if (find(nomen)!=NULL) {
  290.             reader_finish();
  291.             sprintf(buf,"That person is already connected. Who are you, really?\n");
  292.             writestr(ego->sock,buf);
  293.             flag--;
  294.             if (flag==0) {
  295.                 sprintf(buf,"See you on the dark side of the moon.\n");
  296.                 writestr(ego->sock,buf);
  297.                 disconnect(ego);
  298.                 }
  299.             } else {
  300.             reader_finish();
  301.             flag=0;
  302.             strcpy(ego->name,nomen);
  303.             sprintf(buf,"Welcome to the machine, %s.\n",ego->name);
  304.             writestr(ego->sock,buf);
  305.             }
  306.     }
  307. }
  308.  
  309. // MAIN CLIENT ROUTINE
  310.  
  311. void * handle_client(void * argh) {
  312.     int i;
  313.     int flag=0;
  314.     char buf[256];
  315.     char wbuf[2048];
  316.     char check;
  317.     struct chatter * ego;
  318.     ego=(struct chatter *)argh;
  319.     // get name and password information from client
  320.     login(ego);
  321.     // ENTER CLIENT ONTO CLIENTS LIST
  322.     writer_lock();
  323.     ego->next=head;
  324.     head=ego;
  325.     writer_unlock();
  326.     // MAIN LOOP
  327.     while (!flag) {
  328.         await_input(ego->sock);
  329.         if (readstr(ego->sock,buf)==0)
  330.             flag=1;
  331.             else {
  332.             if (buf[0]!='.') {
  333.                 sprintf(wbuf,"[%s:%s] %s\n",ego->server,ego->name,buf);
  334.                 broadcast(wbuf);
  335.             } else { //COMMANDS
  336.                 check=buf[3];
  337.                 buf[3]=0;
  338.                 if (strcmp(buf,".lo")==0) flag=1;
  339.                 if (strcmp(buf,".wo")==0) wholist(ego);
  340.                 if (strcmp(buf,".wh")==0) {
  341.                     buf[3]=check;
  342.                     whisper(ego,buf);
  343.                     buf[3]=0;
  344.                 }
  345.                 if (strcmp(buf,".tw")==0) {
  346.                     ego->whispers=1-ego->whispers;
  347.                     if (ego->whispers==1)
  348.                         sprintf(wbuf,"You now hear whispers.\n");
  349.                         else
  350.                         sprintf(wbuf,"You no longer hear whispers.\n");
  351.                     writestr(ego->sock,wbuf);
  352.                 }
  353.                 if (strcmp(buf,".tr")==0) {
  354.                     ego->room=1-ego->room;
  355.                     if (ego->room==1)
  356.                         sprintf(wbuf,"You now hear the room.\n");
  357.                         else
  358.                         sprintf(wbuf,"You no longer hear the room.\n");
  359.                     writestr(ego->sock,wbuf);
  360.                 }
  361.             }
  362.         }
  363.     }
  364.     //CLEANING UP  
  365.     disconnect(ego);
  366. }
  367.  
  368. int main() {
  369.     int server;
  370.     short port_num;
  371.     int ok;
  372.     struct chatter * current;
  373.     fd_set readset;
  374.     struct timeval timeout;
  375.     port_num=5703;
  376.     server=setup_post(port_num);
  377.     printf("Server is launched, using port number %d. %d\n",port_num,server);
  378.     while (server!=-1) {
  379.         printf("server loop, using fd %d\n",server);
  380.         sched_yield();
  381.         timeout.tv_sec=1;
  382.         timeout.tv_usec=0;
  383.         FD_ZERO(&readset);
  384.         FD_SET(server,&readset);
  385.         if ((select(server+1,&readset,NULL,NULL,&timeout)==-1) && (errno!=EINTR))
  386.             { perror("Select failed!\n"); exit(1); }
  387.         sched_yield();
  388.         if (FD_ISSET(server,&readset)) {
  389.             current=listening_post(server);
  390.             if (current!=NULL) {
  391.                 printf("New connection from %s\n", current->server);
  392.                 //launch new thread which will handle client entirely
  393.                 ok=pthread_create(&(current->thread),NULL,handle_client,(void *) current);
  394.                 sched_yield(); // yield to give new child opportunity to copy ptr
  395.                 if (ok!=0) { perror("Could not create a thread!"); exit(1); }
  396.                 sched_yield(); // a second chance for child to copy ptr
  397.             }
  398.         }
  399.         sched_yield();
  400.         // check for threads that need to be waited on
  401.         if (pthread_mutex_trylock(&lock_done)==0) {
  402.             while (done!=NULL) {
  403.                 if (pthread_join(done->thread,NULL)!=0)
  404.                     printf("No thread to join.\n");
  405.                     else
  406.                     printf("Thread has been joined.\n");
  407.                 current=done;
  408.                 done=done->next;
  409.                 free(current);
  410.                 }
  411.             pthread_mutex_unlock(&lock_done);
  412.             }
  413.     } // end while loop
  414.     return 0;
  415. }
Advertisement
Add Comment
Please, Sign In to add comment