Pour gérer le client une fois la connexion établie, je propose de construire la procédure suivante qui utilisera pour recevoir les caractères la fonction char* receive_from(int fd) et la procédure send_to(int fd, char *buf) que l'on détaillera ensuite.
void do_client(int fd) { char *buf; while (1) { buf=receive_from(fd); if (buf == NULL) break; send_to(fd,buf); free(buf); } }
La fonction receive_from() prend en unique paramètre le descripteur de fichier de la socket de communication. Elle retourne dans une chaîne de caractères, allouée dynamiquement en mémoire, la ligne reçue.
char* receive_from(int fd) { char *ret; int len; ret=(char*) malloc(320); len=read(fd,ret,319); if (len!=0){ ret[len]=0x00; return(ret); } free(ret); return(NULL); }
La fonction send_to() prend en premier paramètre le descripteur de fichier de la socket de communication et en second paramètre la chaine de caractères à envoyer caractère par caractère.
void send_to(int fd, char* buf) { int i; for (i=0;i<strlen(buf);i++) { if (write(fd,buf+i,1)==0) return; usleep(200000); } }
La fonction receive_from() ne fonctionnera pas avec tous les clients. En effet pour plus de sécurité, il ne faut pas faire confiance en la façon dont la ligne sera envoyée au serveur. Pour en améliorer le fonctionnement (ce qui n'était pas demandé dans le sujet) on doit rechercher le caractère \n (le marqueur de fin de ligne) et lire la suite si on ne l'a pas trouvé. On doit également conserver, pour la ligne suivante, ce que l'on a déjà reçu après le caractère \n.
Dans la proposition qui suit, on utilise un buffer static de 2048 octets pour recevoir ce qui vient du client et on alloue dynamiquement la ligne que l'on y trouve. Par sécurité : si on reçoit une ligne de taille supérieure à la taille du buffer, on clôt la connexion.
char* receive_from(int fd) { static char buf[2048]=""; static int pos=0; int len; char *ret; while (1) { for(len=0; len<pos ; len++) if(buf[len]=='\n') break; if(buf[len]=='\n') break; len=sizeof(buf)-pos-1; if (len==0) { shutdown(fd, SHUT_RDWR); return NULL; } len=read(fd,buf+pos,len); if (len==0) return NULL; pos+=len; } buf[len]=0x00; ret=(char*) malloc(len+1); memcpy(ret,buf,len+1); memcpy(buf,buf+len+1,sizeof(buf)-len-1); pos-=len+1; return(ret); }
Ayant remplacer le marque de fin de ligne par un marqueur de fin de chaine de caractères, on doit ajouter à la procédure send_to() l'envoi de la fin de ligne. Son code source devient donc :
void send_to(int fd, char* buf) { int i; for (i=0;i<strlen(buf);i++) { if (write(fd,buf+i,1)==0) return; usleep(200000); } write(fd,"\n",1); }
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <netdb.h> #include <sys/socket.h> #include <string.h> // pour la fonction bzero #include <arpa/inet.h> // pour la fonction inet_addr char* receive_from(int fd) { static char buf[2048]=""; static int pos=0; int len; char *ret; while (1) { for(len=0; len<pos ; len++) if(buf[len]=='\n') break; if(buf[len]=='\n') break; len=sizeof(buf)-pos-1; if (len==0) { shutdown(fd, SHUT_RDWR); return NULL; } len=read(fd,buf+pos,len); if (len==0) return NULL; pos+=len; } buf[len]=0x00; ret=(char*) malloc(len+1); memcpy(ret,buf,len+1); memcpy(buf,buf+len+1,sizeof(buf)-len-1); pos-=len+1; return(ret); } void send_to(int fd, char* buf) { int i; for (i=0;i<strlen(buf);i++) { if (write(fd,buf+i,1)==0) return; usleep(200000); } write(fd,"\n",1); } void do_client(int fd) { char *buf; while (1) { buf=receive_from(fd); if (buf == NULL) break; send_to(fd,buf); free(buf); } } int main() { int fd_srv, fd_con; struct sockaddr_in adr_srv, adr_cli; int err; socklen_t adr_cli_len; fd_srv = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); if (fd_srv<0) { printf("Erreur de creation de la socket !\n"); exit(1); } bzero(&adr_srv, sizeof(adr_srv)); adr_srv.sin_family = AF_INET; err = inet_aton("0.0.0.0", &adr_srv.sin_addr); if (err == 0) { printf("Adresse IPv4 invalide !\n"); exit(1); } adr_srv.sin_port = htons(8080); err = bind(fd_srv, (struct sockaddr *) &adr_srv, sizeof(adr_srv)); if (err != 0) { printf("Erreur d'accès au port serveur !\n"); exit(1); } err = listen(fd_srv, 5); if (err != 0) { printf("Erreur de création de la file d'attente !\n"); exit(1); } printf("Serveur lancé !\n"); while(1) { adr_cli_len = sizeof(adr_cli); fd_con = accept(fd_srv, (struct sockaddr *) &adr_cli, &adr_cli_len); if (fd_con>=0) { printf("Connexion acceptée !\n"); do_client(fd_con); close(fd_con); printf("Connexion fermée !\n"); } } return 0; }