/* * Simple multi-thread chat server. * * wildcat - 2004 */ #include #include #include #include #include #include #include #include #include #include #include #define MAX_CLIENT 5 #define MAX_TRIES 3 #define O_R 0x1 #define O_W 0x2 #define NONE 0x0 typedef struct { int s; int state; /* O_R || O_W */ } sock; #define SHMSZ (sizeof(sock)*MAX_CLIENT + sizeof(int)) /* last int is used to store current number of connection */ #define CURRENT(x) *(x + SHMSZ - sizeof(int)) void syntax(char **); void enter_loop(int *); void init_set(sock *, int); void delfromset(sock *, int, int); void dump_set(sock *, int); void addtoset(sock *, int, int); int sendtoall(sock *, int, char *, int); int sendtoone(sock *, int, char *, int, int); void *process_client(void *); /* shm things: */ char *create_shm (key_t); cur int main (int argc, char **argv) { int ssock; /* serveur socket */ int csock[MAX_CLIENT]; /* clients sockets */ int i; int port; struct sockaddr_in saddr; struct hostent *n_saddr; if (argc != 3) syntax(argv); if ((port = atoi(argv[2])) == 0) { printf("Error, port incorrect..."); return -1; } if ( (ssock = socket(PF_INET, SOCK_STREAM, 0)) == -1) { printf("Error, cannot create socket..\n"); return -1; } printf("Resolv %s..\n", argv[1]); if ( (n_saddr = gethostbyname(argv[1])) == NULL ) { perror("Unable to get ip"); return -1; } saddr.sin_family = n_saddr->h_addrtype; memcpy(&saddr.sin_addr.s_addr, n_saddr->h_addr, n_saddr->h_length); saddr.sin_port = htons(port); /* binding */ printf("Trying to bind %s:%d\n", argv[1], port); i=MAX_TRIES; do { printf("Try %d of %d...\n", MAX_TRIES - i + 1, MAX_TRIES); if (bind(ssock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) { perror("Cannot bind port"); continue; } else /* we are binding port... */ { listen(ssock, 5); printf("Good, we listen %s:%d !\n", argv[1], port); enter_loop(&ssock); printf("Server shutdown.\n"); return 1; } sleep(5); /* wait 5second before retrying */ } while (--i) ; return 1; } void syntax (char **a) { printf("Syntax: %s \n", a[0]); exit(1); } void init_set(sock *set, int nb) { int i; for (i=0;is = 0; (set+i)->state = NONE; } return; } void dump_set(sock *set, int max) { int i; printf("Dump set:\n"); for (i=0;i %d\n", i, (set + i)->s); } void addtoset(sock *set, int max, int v) { int i; for (i=0;is == 0) { (set+i)->s = v; (set+i)->state = NONE; break; } return; } void delfromset(sock *set, int max, int v) { int i; printf("Closing socket %d\n", v); for (i=0; is == v) { close (v); (set+i)->s = 0; (set+i)->state = NONE; CURRENT(cs)--; break; } return; } int sendtoone(sock *set, int max, char *buf, int size, int s) { int i,j; printf("Sending to one: %s (%d)->%d\n", buf, size, s); for (i=0;is == s) { // MAX_TRIES attempt to write j=MAX_TRIES; do { if ((set+i)->state == NONE) { (set+i)->state = O_W; if (write((set+i)->s, buf, size) == -1) { if (errno == EPIPE) { delfromset(set, max, (set + i)->s); *(set)--; } } (set+i)->state = NONE; return size; } sleep(1); } while (--i) ; } return -1; } int sendtoall(sock *set, int max, char *buf, int size) { int i,j; printf("Sending to all: %s (%d)\n", buf, size); for (i=0;is != 0) { // MAX_TRIES attempt to write j=MAX_TRIES; do { if ((set+i)->state == NONE) { (set+i)->state = O_W; if (write((set+i)->s, buf, size) == -1) { if (errno == EPIPE) { delfromset(set, max, (set + i)->s); } } (set+i)->state = NONE; break; } sleep(1); } while (--i) ; } return; } typedef struct { int sock; key_t key; }client; void enter_loop(int *ss) { pthread_t thread[MAX_CLIENT]; pthread_attr_t attr; int biggest = 0; fd_set c_set; fd_set s_set; struct timeval t_out, *to; struct sockaddr c_addr; int s, len; int i, j, k, size; char buffer[100]; sock *cs; client *c; key_t key = (rand() % 10000) + 1; printf(" creation shm avec cle: %d\n", key); if ((cs = (sock *)create_shm(key)) == NULL) { /* erreur SHM */ return; } pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); init_set(cs, MAX_CLIENT); while(1) { FD_ZERO(&s_set); FD_SET(*ss, &s_set); if (!CURRENT(cs)) to = NULL; else { t_out.tv_sec = 0; t_out.tv_usec = 500; to = &t_out; } if (select((*ss) + 1, &s_set, NULL, NULL, to) > 0) { /* client a accepter */ len = sizeof(c_addr); if ( (s = accept((*ss), (struct sockaddr *) &c_addr, &len)) == -1) { perror("Error accept"); } else { printf("New client accepted\n"); if (CURRENT(cs) == MAX_CLIENT) { printf("Server full closing connection\n"); write (s, "Server Full", 13); close (s); } else { c = (client *) malloc (sizeof(client)); c->sock = s; c->key = key; pthread_create(&thread[CURRENT(cs)], &attr, process_client,(void*)c); addtoset(cs,MAX_CLIENT ,s); CURRENT(cs)++; } dump_set(cs, MAX_CLIENT); } } } } void *process_client(void *a) { client *c = (client *) a; int shmid; sock *shm; fd_set c_set; char buffer[100]; int size; struct timeval t_out; /* * Locate the segment. */ if ((shmid = shmget(c->key, SHMSZ, 0600)) < 0) { perror("shmget thread"); pthread_exit((void *) c->sock); } /* * Now we attach the segment to our data space. */ if ((shm = (sock *) shmat(shmid, NULL, 0)) == (void *) -1) { perror("shmat thread"); pthread_exit((void *) c->sock); } while (1) { FD_ZERO(&c_set); FD_SET(c->sock , &c_set); t_out.tv_sec = 5; t_out.tv_usec = 0; if ( (select (c->sock + 1, &c_set, NULL, NULL, &t_out)) > 0) { /* requete bloquante */ /* read from this socket */ bzero(buffer, sizeof(buffer)); size = read(c->sock, &buffer, 100); if (!size) { delfromset(shm, MAX_CLIENT, c->sock); break; } /* send the buffer read to all */ sendtoall(shm, MAX_CLIENT, &buffer[0], size); } } printf("Le process %d s'occupant du socket %d va crever...", getpid(), c->sock); delfromset(shm, MAX_CLIENT, c->sock); /* on le delete au cas ou ca serais pas déjà fait... */ free(c); pthread_exit((void *) 1); } /* * return the shared memory space */ char *create_shm (key_t key) { char c; int shmid; char *shm; if ((shmid = shmget(key, SHMSZ, IPC_CREAT | 0600)) < 0) { perror("shmget pere"); return NULL; } if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) { perror("shmat pere"); return NULL; } return shm; }