Question n°1: single-client

The simplified version

To handle the client once the connection is established, I propose to construct the following procedure that will use the char* receive_from(int fd) function to receive characters and the send_to(int fd, char *buf) procedure that will be detailed later.

void do_client(int fd) {
    char *buf;
    while (1) {
        buf=receive_from(fd);
        if (buf == NULL) break;
        send_to(fd,buf);
        free(buf);
    }
}
    

The receive_from() function takes as its sole parameter the file descriptor of the communication socket. It returns the received line in a dynamically allocated character string in memory.

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);
}
    

The send_to() function takes the file descriptor of the communication socket as its first parameter, and the character string to be sent character by character as its second parameter.

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);
    }
}
    

Improvement

The receive_from() function will not work with all clients. Indeed, for greater security, we should not trust the way the line will be sent to the server. To improve its operation (which was not requested in the subject), we must search for the \n character (the end-of-line marker) and read the rest if it is not found. We must also keep, for the next line, what we have already received after the \n character.

In the following proposal, we use a static buffer of 2048 bytes to receive what comes from the client and dynamically allocate the line that we find in it. For safety: if we receive a line larger than the size of the buffer, we close the connection.

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);
}
    

Having replaced the end-of-line marker with a string termination marker, we must add sending the end-of-line to the send_to() procedure. Its source code becomes:

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);
}
    

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
 
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("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");
            do_client(fd_con);
            close(fd_con);
            printf("Connection closed!\n");
        }
    }
    return 0;
}