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

Changes compared to question n°1

The basic receive_from() function can be reused without any changes. However, the improved version is not compatible with multi-threading due to the use of static variables. Since each client uses the same function, global variables (or static variables which are simply global variables only accessible by the function) must be independent for each client. We can define a global array containing, for each file descriptor value, a pointer to a structure storing the information we intended to store in static variables.

The send_to() function can be reused without any changes.

The do_client() function must be transformed into a function to be used by the thread creation function (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("Connection closed!\n");
	return NULL;
}
	

The complete source code

#include <stdio.h>
#include <stdlib.h>
 
#include <sys/types.h>
#include <netdb.h>
#include <sys/socket.h>
 
#include <string.h>       // for the function bzero
#include <arpa/inet.h>    // for the function inet_addr
 
#include <pthread.h>      // for the function 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("Connection closed!\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("Socket creation error!\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("Invalid IPv4 address!\n");
        exit(1);
    }
    adr_srv.sin_port = htons(8080);
    err = bind(fd_srv, (struct sockaddr *) &adr_srv, sizeof(adr_srv));
    if (err != 0) {
        printf("Server port access error!\n");
        exit(1);
    }
    err = listen(fd_srv, 5);
    if (err != 0) {
        printf("Queue creation error!\n");
        exit(1);
    }
    printf("Server launched!\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("Connection accepted!\n");
            err = pthread_create(&thread_id, NULL, do_client, &fd_con);
            pthread_detach(thread_id);
        }
    }
    return 0;
}