Question n°3 : multi-client / multi-thread

Changements par rapport à la question n°1

La fonction receive_from() de base peut être reprise sans changement. Par contre la version améliorée n'est pas compatible avec le multi-thread à cause de l'usage des variables statiques. Chaque client utilisant la même fonction, il faut que les variables globales (ou statiques qui ne sont autre que des variables globales accessibles uniquement par la fonction) soient indépendantes pour chaque client. On pourra définir, en global, un tableau contenant pour chaque valeur du descripteur de fichier un pointeur vers une structure mémorisant les informations que l'on souhaitait mettre en static.

La procédure send_to() peut être reprise sans changement.

La procédure do_client() doit être transformée en fonction pour pouvoir être utilisée par la fonction de création de thread (thread_create) :

#define BUFSIZE 2048

struct tdata {
	int pos;
	char buf[BUFSIZE];
}	*data[FD_SETSIZE];

char* receive_from(int fd) {
    int len;
    char *ret;

    while (1) {
        for(len=0; len<data[fd]->pos ; len++) if(data[fd]->buf[len]=='\n') break;
        if(data[fd]->buf[len]=='\n') break;
        len=BUFSIZE-data[fd]->pos-1;
        if (len==0) {
            shutdown(fd, SHUT_RDWR);
            return NULL;
        }
        len=read(fd,data[fd]->buf+data[fd]->pos,len);
        if (len==0) return NULL;
        data[fd]->pos+=len;
    }
    data[fd]->buf[len]=0x00;
    ret=(char*) malloc(len+1);
    memcpy(ret,data[fd]->buf,len+1);
    memcpy(data[fd]->buf,data[fd]->buf+len+1,BUFSIZE-len-1);
    data[fd]->pos-=len+1;
    return(ret);
}

void *do_client(void *arg) {
	int fd;
    char *buf;
	
	fd= *((int*) arg);
	data[fd]=malloc(sizeof(struct tdata));
	data[fd]->pos=0;
    while (1) {
        buf=receive_from(fd);
        if (buf == NULL) break;
        send_to(fd,buf);
        free(buf);
    }
    shutdown(fd, SHUT_RDWR);
    close(fd);
    printf("Connexion fermée !\n");
	return NULL;
}
	

Le code source complet

#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
 
#include <pthread.h>      // pour la fonction pthread_create
 
#define BUFSIZE 2048

struct tdata {
	int pos;
	char buf[BUFSIZE];
}	*data[FD_SETSIZE];


char* receive_from(int fd) {
    int len;
    char *ret;

    while (1) {
        for(len=0; len<data[fd]->pos ; len++) if(data[fd]->buf[len]=='\n') break;
        if(data[fd]->buf[len]=='\n') break;
        len=BUFSIZE-data[fd]->pos-1;
        if (len==0) {
            shutdown(fd, SHUT_RDWR);
            return NULL;
        }
        len=read(fd,data[fd]->buf+data[fd]->pos,len);
        if (len==0) return NULL;
        data[fd]->pos+=len;
    }
    data[fd]->buf[len]=0x00;
    ret=(char*) malloc(len+1);
    memcpy(ret,data[fd]->buf,len+1);
    memcpy(data[fd]->buf,data[fd]->buf+len+1,BUFSIZE-len-1);
    data[fd]->pos-=len+1;
    return(ret);
}

void send_to(int fd, char* buf) {
    int i;
    for (i=0;i<strlen(buf);i++) {
        write(fd,buf+i,1);
        usleep(200000);
    }
    write(fd,"\n",1);
}

void *do_client(void *arg) {
	int fd;
    char *buf;
	
	fd= *((int*) arg);
	data[fd]=malloc(sizeof(struct tdata));
	data[fd]->pos=0;
    while (1) {
        buf=receive_from(fd);
        if (buf == NULL) break;
        send_to(fd,buf);
        free(buf);
    }
    shutdown(fd, SHUT_RDWR);
    close(fd);
    printf("Connexion fermée !\n");
	return NULL;
}
 
int main()
{
    int fd_srv, fd_con;
    struct sockaddr_in  adr_srv, adr_cli;
    int err;
    socklen_t adr_cli_len;
    pthread_t thread_id;
 
    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");
            err = pthread_create(&thread_id, NULL, do_client, &fd_con);
            pthread_detach(thread_id);
        }
    }
    return 0;
}