/* vim: set sw=4 ts=4 si et: */ #include #include #include #include #include #include #include #include #include #include #include #include #define LINK_CELL_NAME_SIZE 100 #define KEEP_SOCK_MAX(x, y) if (x < ((y) +1)){ x = (y) + 1; } #define FOREACH_SOCKET_IN_SOCKBASKET(i, sockbasket, link) for (i = 0; \ ((i < sockbasket->size) \ && (link_basket_get(sockbasket, i, &link)) \ ); \ i++) \ if (link != NULL) #define SELECT_TIMEOUT 100000; #define REQUEST_INTERVAL 5 #define REQUEST_TIMEOUT 30 #define MIN(x, y) (((x) < (y))?(x):(y)) #define bool short #define true 1 #define false 0 typedef struct { int sock_desc; bool is_dead; bool is_alive; bool is_registered; time_t request_stamp; time_t answer_stamp; char * name; } link_cell_t; typedef struct { link_cell_t ** data; int size; int next; } link_basket_t; void show_progress(){ static int prog = 0; prog = prog % 100; printf("["); if (prog < 25){ printf("|"); } else if (prog < 50) { printf("/"); } else if (prog < 75) { printf("-"); } else { printf("\\"); } printf("] "); prog += 25; } bool link_basket_get (link_basket_t *sb, int index, link_cell_t ** res){ bool result; if (index < sb->size){ *res = sb->data[ index ]; /* if (*res != NULL){ printf ("Get at %d == %d\n", index, (*res)->sock_desc); } */ result = 1; } else { *res = NULL; result = 0; } return result; } void link_basket_delete (link_basket_t * sb){ free (sb->data); free (sb); } void link_basket_remove (link_basket_t * sb, int index){ link_cell_t * result = sb->data[ index ]; if (sb->data[ index ]){ free(result->name); free (result); } sb->data[ index ] = NULL; if (index < sb->next){ sb->next = index; } return; } void link_basket_add (link_basket_t * sb, int sockfd){ int i; assert (sb->next != -1); // basket overflow ! link_cell_t * cell = (link_cell_t *) malloc (sizeof (link_cell_t)); cell->sock_desc = sockfd; cell->is_dead = false; cell->is_alive = true; cell->is_registered = false; cell->request_stamp = time(NULL); cell->answer_stamp = time(NULL); cell->name = (char *) malloc (sizeof (char) * LINK_CELL_NAME_SIZE); memset(cell->name, 0, LINK_CELL_NAME_SIZE); sb->data[ sb->next ] = cell; for (i = sb->next; i < sb->size; i++){ if (sb->data[ i ] == NULL){ sb->next = i; } } return; } link_basket_t * link_basket_create (int size){ link_basket_t * result = (link_basket_t *) malloc (sizeof (link_basket_t)); result->size = size; result->data = (link_cell_t **) malloc (sizeof (link_cell_t *) * size); result->next = 0; int i; for (i = 0; i < result->size; i++){ result->data[i] = NULL; } return result; } int main (){ link_basket_t * basket = link_basket_create(10); int main_sock ; int port; char * hostname; char * portstr; struct hostent * server; struct sockaddr_in * myaddr; main_sock = socket (AF_INET, SOCK_STREAM, 0); if (main_sock < 0){ perror ("Creation de la socket impossible"); fprintf (stderr,"BOOM at %s:%d\n",__FILE__,__LINE__); exit (-1); } hostname = getenv ("DETECTOR_HOSTNAME"); portstr = getenv ("DETECTOR_PORT"); if ((NULL == hostname) || (NULL == portstr)){ printf("Environment variables DETECTOR_PORT and DETECTOR_HOSTNAME should have been defined.\n"); exit(-1); } sscanf (portstr, "%d", &port); printf ("Lookup for %s...\n", hostname); server = gethostbyname(hostname); if (server == NULL){ fprintf (stderr, "Error, no such host\n"); exit (-1); } printf ("Host found\n"); myaddr = (struct sockaddr_in *) malloc (sizeof(struct sockaddr_in)); bzero (myaddr, sizeof(struct sockaddr_in)); myaddr->sin_family = AF_INET; myaddr->sin_port = htons(port); memcpy (&(myaddr->sin_addr.s_addr), server->h_addr, sizeof(u_long)); //myaddr->sin_addr.s_addr = INADDR_ANY; // on bind sur le port voulu... if (bind(main_sock, (struct sockaddr *)myaddr, sizeof(struct sockaddr)) < 0){ perror("Unable to bind"); fprintf(stderr,"Cannot bind on port %d of %s\n",port, inet_ntoa(myaddr->sin_addr)); exit(-1); } else { printf("Bind on port %d of %s\n",port, inet_ntoa(myaddr->sin_addr)); } // on fait un listen if (listen(main_sock, 10) < 0){ perror("Unable to listen"); exit(-1); } else { printf("Ready to accept connexions.\n"); } fd_set sock_set; struct timeval tv; int sock_set_size = 0; FD_ZERO(&sock_set); FD_SET(main_sock, &sock_set); sock_set_size++; while(1){ printf("\r"); show_progress(); tv.tv_sec = 0; tv.tv_usec = SELECT_TIMEOUT; // // si on recoit qqchose sur la socket principale, on fait un accept // et on ajoute la nouvelle socket au set link_cell_t * link; int i; fd_set read_set;//= sock_set; FD_ZERO(&read_set); FD_SET(main_sock, &read_set); KEEP_SOCK_MAX(sock_set_size, main_sock); // on ajoute tous les socket ouverts au read_set FOREACH_SOCKET_IN_SOCKBASKET (i, basket, link) { time_t curtime = time(NULL); bool must_add = false; if (link->is_registered){ if (link->is_alive){ // si le programme est en vie if (difftime(curtime, link->answer_stamp) > REQUEST_INTERVAL){ // si le temps nécessaire est écoulé depuis la derniere réponse printf ("\nRequesting liveness for '%s'\n", link->name); send(link->sock_desc,"status?", 7, 0); link->request_stamp = curtime; link->is_alive = false; must_add = true; } else { // sinon on attend le temps nécessaire avant de douter... // printf ("On attend que le temps s'écoule pour '%s'\n", link->name); } } else { // sinon if (difftime(curtime, link->request_stamp) > REQUEST_TIMEOUT){ // ou on le tue... // FIXME: retirer de la corbeille a liens... printf ("\nTimeout: closing connection of '%s'\n", link->name); close(link->sock_desc); link_basket_remove(basket, i); } else { // on est toujours dans les temps de réponse... //printf ("\nWaiting for '%s'\n", link->name); must_add = true; } } } else { must_add = true; } if (must_add){ //printf ("Adding '%s' (%d) to the set\n", link->name, link->sock_desc); FD_SET (link->sock_desc, &read_set); KEEP_SOCK_MAX (sock_set_size, link->sock_desc); } } printf("Select..."); fflush(stdout); select(sock_set_size, &read_set, NULL, NULL, &tv); printf("done"); fflush(stdout); FOREACH_SOCKET_IN_SOCKBASKET(i, basket, link) { time_t curtime = time(NULL); if (FD_ISSET(link->sock_desc, &read_set)){ // on lit les données recues, et sinon on ferme la socket. // on vérifie qu'elle sont égales à ... int buffer_len = 1024; char * buffer = (char *) malloc (sizeof(char) * buffer_len); int recv_len; recv_len = recv(link->sock_desc, buffer, buffer_len, 0); if (recv_len == 0){ printf("\nClient '%s' has disconnected !\n", link->name); link_basket_remove(basket, i); } else { if ((recv_len >= 9) && (!strncmp("register:", buffer, 9))) { buffer[MIN(recv_len, buffer_len - 1)] = '\0'; int i=0; while(i < buffer_len -1){ if ((buffer[i] == 0xD) || (buffer[i] == 0xA)) { buffer[i] = '\0'; } i++; } strncpy(link->name, buffer + 9, MIN(recv_len - 9, LINK_CELL_NAME_SIZE)); printf("\nRegistering client : %s\n", link->name); link->is_registered = true; link->answer_stamp = curtime; } else if ((recv_len >= 6) && (!strncmp("alive!", buffer, 6))) { //printf("Process on fd %d is alive !\n", link->sock_desc); link->is_alive = true; link->answer_stamp = curtime; } else { buffer[99] = '\0'; printf("\nUnknow message %s", buffer); } } } else { // printf("Rien sur %d\n", link->sock_desc); } } if (FD_ISSET(main_sock, &read_set)){ unsigned int sin_size = sizeof(struct sockaddr_in); struct sockaddr_in their_addr; /* Adresse du connecté */ int sockfd; sockfd = accept(main_sock, (struct sockaddr *)&their_addr, &sin_size); link_basket_add(basket, sockfd); printf("\nGot new connection at %d\n", sockfd); }; // lors d'un accept, on ajoute au tableau des entrées // possibles l'hote qui se connecte // regarder les entrées... // faire un select dessus // garde des timer pour chaque entrée // lorsque le temps D est écoule pour un timer // on envoie un paquet "WIEGEHTS" a la machine // et on attend une réponse en moins de 60 sec; } return EXIT_SUCCESS; }