Search code examples
ctcpudpports

C - Sending UDP messages client-client through a socket


I have a simple question. I think it is simple, at least!

I'm doing an university project and I've built a server-client instant messagging service, structured like so:
-The server create a TCP socket to listen for client requests
-Each client, besides the other functions, can send messagges to another client (even offline) using UDP

I've used port 4242 for the server, then tried to use 4243 and 4244 for two example clients.

My question is: are these ports not acceptable for UDP communications?
I'm asking because when I try to send messages to those ports, the receiving client gets and prints "strange" strings.
To be precise, it is as it stored precedent messages and prints first the new message, then a chunk of precedent messages.
On the other hand, if I use for example 4303 and 4486, it works as expected.

I got those numbers looking at Wikipedia, but since I'm not sure how to interpret that table (and so, if I interpreted it correctly) and since I know Wikipedia can be not-so-reliable, I figured I could ask here.

On a side note, is there any command or resource to check for this things, if the link I provided is not reliable?

Thanks in advance!

EDIT:

Here's my code! I couldn't upload it before.. I'll try to give a brief explanation of it as well.
As I mentioned in a comment, I thought I had it but one client does not receive the message from the other. So all works, except for the !send functionality, I guess...

Client
The client is an user that connects to the server with "!register username"; this command:
-Registers the client to the service if it's the first time that he's connecting to it, and of course set it as "online"
-Re-connects the client if he was already registered to the service; in this case, if there are some offline messages the servers delivers it to te client

Once connected, the client can use the following commands:
1)!help: displays the available commands
2)!who: shows online users
3)!deregister: as the nam suggests, de-register the client from the server
4)!send username: if the destination client is online, the server returns the UDP destination address and port and the client sends the message directly; if the destination client is offline, the servers stores the message to send it to the destination client once it will be connected

    #include <stdlib.h>
    #include <string.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <unistd.h>
    #include <stdio.h>

    #define DIM 1024
    #define ADDR_DIM 16
    #define PORT_DIM 5
    #define CMD_DIM 12
    #define NAME_DIM 21

    void cl_help(int sckt) {
    int rv, length, l;
    char* text = "!help";
    char answer[DIM];

    length = strlen(text);
    l = htons(length);

    //Sending command dimension
    rv = send(sckt, (void*) &l, sizeof(l), 0);
    if(rv < 0) {};

    rv = send(sckt, (void*) text, length, 0);
    if(rv < 0) {};

    //Sending the command itself
    rv = recv(sckt, (void*) &l, sizeof(l), 0);
    if(rv < 0) {};

    length = ntohs(l);

    rv = recv(sckt, (void*) answer, length, 0);
    if(rv < 0) {};

    answer[length] = '\0';
    puts(answer);
    }

    void cl_quit(int sckt) {
    int rv, length, l;
    char* text = "!quit";
    char answer[DIM];

    length = strlen(text);
    l = htons(length);

    //Sending command dimension
    rv = send(sckt, (void*) &l, sizeof(l), 0);
    if(rv < 0) {};

    rv = send(sckt, (void*) text, length, 0);
    if(rv < 0) {};

    //Sending the command itself
    rv = recv(sckt, (void*) &l, sizeof(l), 0);
    if(rv < 0) {};

    length = ntohs(l);

    rv = recv(sckt, (void*) answer, length, 0);
    if(rv < 0) {};

    answer[length] = '\0';
    puts(answer);
    }

    void cl_who(int sckt) {
    int rv, length, l, n_net, n, i;
    char* text = "!who";
    char answer[DIM];

    length = strlen(text);
    l = htons(length);

    //Sending the dimension of the command
    rv = send(sckt, (void*) &l, sizeof(l), 0);
    if(rv < 0) {};

    rv = send(sckt, (void*) text, length, 0);
    if(rv < 0) {};

    //Sending the command itself
    rv = recv(sckt, (void*) &l, sizeof(l), 0);
    if(rv < 0) {};

    length = ntohs(l);

    rv = recv(sckt, (void*) answer, length, 0);
    if(rv < 0) {};

    answer[length] = '\0';
    puts(answer);

    if( (strcmp(answer, "Nessun utente in linea") != 0)\
        && (strcmp(answer, "Devi essere connesso per poter utilizzare questa funzione") != 0) ) { //No user online; Yuo have to be registered

        //Receiving number of users online
        rv = recv(sckt, (void*) &l, sizeof(l), 0);
        if(rv < 0) {};

        length = ntohs(l);

        rv = recv(sckt, (void*) &n_net, length, 0);
        if(rv < 0) {};

        n = ntohs(n_net);

        //Getting every username
        for(i=0; i<n; i++) {
            rv = recv(sckt, (void*) &l, sizeof(l), 0);
            if(rv < 0) {};

            length = ntohs(l);

            rv = recv(sckt, (void*) answer, length, 0);
            if(rv < 0) {};

            answer[length] = '\0';
            puts(answer);
        };
    };

    }

    void cl_deregister(int sckt) {
    int rv, length, l;
    char* text = "!deregister";
    char answer[DIM];

    //Sending command dimension
    length = strlen(text);
    l = htons(length);

    rv = send(sckt, (void*) &l, sizeof(l), 0);
    if(rv < 0) {};

    rv = send(sckt, (void*) text, length, 0);
    if(rv < 0) {};

    //Receiving answer
    rv = recv(sckt, (void*) &l, sizeof(l), 0);
    if(rv < 0) {};

    length = ntohs(l);

    rv = recv(sckt, (void*) answer, length, 0);
    if(rv < 0) {};

    answer[length] = '\0';
    puts(answer);
    }

    void cl_register(int sckt, char* name, char* port) {
    int rv, length, l, n, n_net, i;
    char msg[DIM+NAME_DIM+4];
    char answer[DIM];

    sprintf(msg, "!register %s %s", name, port);

    //Sending command
    length = strlen(msg);
    l = htons(length);

    rv = send(sckt, (void*) &l, sizeof(l), 0);
    if(rv < 0) {};

    rv = send(sckt, (void*) msg, length, 0);
    if(rv < 0) {};

    //Receiving answer
    rv = recv(sckt, (void*) &l, sizeof(l), 0);
    if(rv < 0) {};

    length = ntohs(l);

    rv = recv(sckt, (void*) answer, length, 0);
    if(rv < 0) {};

    answer[length] = '\0';
    puts(answer);

    if(strcmp(answer, "L'utente era gia' registrato al servizio: riconnessione completata.\n") == 0) { //Users already registered: reconnection
        //Receiving info
        rv = recv(sckt, (void*) &l, sizeof(l), 0);
        if(rv < 0) {};

        length = ntohs(l);

        rv = recv(sckt, (void*) answer, length, 0);
        if(rv < 0) {};

        answer[length] = '\0';
        puts(answer);

        if(strcmp(answer, "Messaggi offline presenti.") == 0) { //Offline messages found
            //Receiving the number of messages
            rv = recv(sckt, (void*) &l, sizeof(l), 0);
            if(rv < 0) {};

            length = ntohs(l);

            rv = recv(sckt, (void*) &n_net, length, 0);
            if(rv < 0) {};

            n = ntohs(n_net);

            //Recevingi messages
            for(i=0; i<n; i++) {
                rv = recv(sckt, (void*) &l, sizeof(l), 0);
                if(rv < 0) {};

                length = ntohs(l);

                rv = recv(sckt, (void*) msg, length, 0);
                if(rv < 0) {};

                msg[length] = '\0';
                puts(msg);
            };
        };
    };  
    }

    void cl_send(int sckt, int udp_sd, char* name, char* info, char* source) {
    struct sockaddr_in dest;
    int rv, length, l,p;
    char msg[DIM+NAME_DIM+4];
    char address[ADDR_DIM];
    char port[PORT_DIM];
    char answer[DIM];
    char *pointer;

    sprintf(msg, "!send %s", name);

    //Sending command
    length = strlen(msg);
    l = htons(length);

    rv = send(sckt, (void*) &l, sizeof(l), 0);
    if(rv < 0) {};

    rv = send(sckt, (void*) msg, length, 0);
    if(rv < 0) {};

    //Receiving answer
    rv = recv(sckt, (void*) &l, sizeof(l), 0);
    if(rv < 0) {};

    length = ntohs(l);

    rv = recv(sckt, (void*) answer, length, 0);
    if(rv < 0) {};

    answer[length] = '\0';

    puts(answer);

    if(strcmp(answer, "Devi essere connesso per poter utilizzare questa funzione.\n") != 0) { //You have to be registered
        if(strncmp(answer, "Client attualmente connesso", 27) == 0) { //User connected right now
            pointer = answer+29; //to get only the address and the port number
            sscanf(pointer, "%s %s", address, port);
            address[ADDR_DIM] = '\0';
            port[PORT_DIM] = '\0';
            p = ntohs(atoi(port));

            memset(&dest, 0, sizeof(dest));
            memset(&msg, 0, sizeof(msg));f

            //Destination UDP socket
            dest.sin_family = AF_INET;
            dest.sin_port = htons(p);
            inet_pton(AF_INET, address, &dest.sin_addr);

            //Sending direct message using UDP
            sprintf(msg, "%s > ", source);
            strcat(msg, info);

            length = strlen(msg);
            l = htons(length);

            //Sending message
            rv = sendto(udp_sd, (void*) &l, sizeof(l), 0, (struct sockaddr*) &dest, sizeof(dest));
            if(rv < 0) {};

            rv = sendto(udp_sd, (void*) msg, length, 0, (struct sockaddr*) &dest, sizeof(dest));
            if(rv < 0) {};

        } else { //Offline transmission: transmits message to server using TCP
            length = strlen(info);
            l = htons(length);

            rv = send(sckt, (void*) &l, sizeof(l), 0);
            if(rv < 0) {};

            rv = send(sckt, (void*) info, length, 0);
            if(rv < 0) {};

            //Getting info from the server
            rv = recv(sckt, (void*) &l, sizeof(l), 0);
            if(rv < 0) {};

            length = ntohs(l);

            rv = recv(sckt, (void*) answer, length, 0);
            if(rv < 0) {};

            answer[length] = '\0';
            puts(answer);
        };
    };
    }

    int main(int argc, char* argv[]) {
    struct sockaddr_in srv_addr;
    struct sockaddr_in my_addr;
    int sd, udp;
    int err;
    int pid;
    int length, l;
    char buffer[DIM];
    char cmd[CMD_DIM];
    char dest[NAME_DIM];
    char my_name[NAME_DIM];
    char in[DIM];
    char to_send[DIM+NAME_DIM+4];
    char part[DIM] = "";

    if(argc != 5) {
        //Check argument
        exit(EXIT_FAILURE);
    };

    //TCP socket
    sd = socket(AF_INET, SOCK_STREAM, 0);
    //UDP socket
    udp = socket(AF_INET, SOCK_DGRAM, 0);

    //Pulizia
    memset(&srv_addr, 0, sizeof(srv_addr));
    memset(&my_addr, 0, sizeof(my_addr));

    //TCP server parameters
    srv_addr.sin_family = AF_INET;
    srv_addr.sin_port = htons(atoi(argv[4]));
    inet_pton(AF_INET, argv[3], &srv_addr.sin_addr);

    //my UDP parameters
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(atoi(argv[2]));
    inet_pton(AF_INET, argv[1], &my_addr.sin_addr);

    //Bind for UDP socket
    err = bind(udp, (struct sockaddr*) &my_addr, sizeof(my_addr));
    if(err < 0) {};

    //Connecting to the server
    err = connect(sd, (struct sockaddr*) &srv_addr, sizeof(srv_addr));
    if(err < 0) {};

    //Auto-sending help command to display available operations
    strcpy(cmd, "!help");

    length = strlen(cmd);
    l = htons(length);

    err = send(sd, (void*) &l, sizeof(l), 0);
    if(err < 0) {};

    err = send(sd, (void*) cmd, length, 0);
    if(err < 0) {};

    //Receiving info from the server -> available operations
    err = recv(sd, (void*) &l, sizeof(l), 0);
    if(err < 0) {};

    length = ntohs(l);

    err = recv(sd, (void*) buffer, length, 0);
    if(err < 0) {};

    buffer[strlen(buffer)] = '\0';
    puts(buffer);

    pid = fork();
    if(pid == 0) {
        //Child
        close(sd);

        while(1) {
            memset(to_send, 0, sizeof(to_send));

            err = recvfrom(udp, (void*) &l, sizeof(l), 0, 0, 0);
            if(err < 0) {
                puts("error recvfrom");
                exit(EXIT_FAILURE);
            };

            length = ntohs(l);

            err = recvfrom(udp, (void*) to_send, length, MSG_WAITALL, 0, 0);
            if(err < 0) {
                puts("error recvfrom");
                exit(EXIT_FAILURE);
            };

            to_send[length] = '\0';
            puts(to_send);
        };
    } else {
        //Parent

        while(1) {
            //Asking the user to prompt the command
            memset(cmd, 0, sizeof(cmd));
            memset(in, 0, sizeof(in)); 
            memset(dest, 0, sizeof(dest));
            puts("\nInserisci un comando: "); //Insert a command
            fgets(in, DIM, stdin);

            in[strlen(in)-1] = '\0';
            sscanf(in, "%s", cmd);
            cmd[CMD_DIM-1] = '\0';

            if(strcmp(cmd, "!help") == 0) {
                cl_help(sd);
                continue;
            };

            if(strcmp(cmd, "!deregister") == 0) {
                cl_deregister(sd);
            };

            if(strcmp(cmd, "!register") == 0) {
                sscanf(in, "%s %s", cmd, dest);
                cmd[CMD_DIM-1] = '\0';
                dest[NAME_DIM-1] = '\0';
                strcpy(my_name, dest);
                if(strlen(dest) == 0) { //to avoid an empty username
                    puts("Errore: non hai inserito alcun username; riprova.\n"); //Please insert an username
                } else {
                    cl_register(sd, dest, argv[2]);
                };
                continue;
            };

            if(strcmp(cmd, "!who") == 0) {
                cl_who(sd);
                continue;
            };

            if(strcmp(cmd, "!send") == 0) {
                sscanf(in, "%s %s", cmd, dest);
                cmd[CMD_DIM-1] = '\0';
                dest[NAME_DIM-1] = '\0';

                while(1) {
                    memset(part, 0, sizeof(part));

                    fgets(part, DIM-(strlen(to_send)-NAME_DIM-4), stdin);
                    l = strlen(part);

                    if((part[l-2] == '.') && (part[l-1] == '\n') && (strlen(part) == 2)) {
                        part[l-2] = '\0';
                        break;
                    } else {
                        strcat(to_send, part);
                    };

                    if(strlen(to_send) >= DIM+NAME_DIM+3) {
                        to_send[DIM+NAME_DIM+3] = '\0';
                        break;
                    };
                };
                strcat(to_send, "\0"); 
                cl_send(sd, udp, dest, to_send, my_name);
                memset(to_send, 0, sizeof(to_send)); //to empty the buffer used to store the message sent
                continue;
            };

            if(strcmp(cmd, "!quit") == 0) {
                cl_quit(sd);
                close(sd);
                break;
            } else{
                puts("Comando non valido.\n"); //Not a valid command
            }; 
        };
    };

    return 0;
    }

Server
The server answers the requests from the client and is structured as a concurrent server.

#include <sys/mman.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>

#define MAX_CONN 100
#define DIM 1024
#define ADDR_DIM 16
#define CMD_DIM 12
#define NAME_DIM 20
#define MSG_DIM DIM+NAME_DIM+4
#define PORT_DIM 5
#define MAX_UTN 1024

struct users {
    char username[NAME_DIM];
    char status; //c = connected, o = offline
    struct sockaddr_in address;
    char pendent[DIM][DIM]; 
    char sender[DIM][NAME_DIM];
    int msg; //last message to be sent
};

void srv_help(int sckt) {
    int rv, length, l;

    char* text = "Sono disponibili i seguenti comandi: \n\
 !help --> mostra l'elenco dei comandi disponibili \n\
 !register username --> registra il client presso il server\n\
 !deregister --> de-registra il client presso il server\n\
 !who --> mostra l'elenco degli utenti disponibili\n\
 !send username --> invia un messaggio ad un altro utente\n\
                (messaggio subito dopo comando\n\
 !quit --> disconnette il client dal server ed esce\n\
 ('username': max 20 caratteri)\n\
 (per terminare messaggio inserisci un . da solo)\n";//info about the available commands

    length = strlen(text);
    l = htons(length);

    rv = send(sckt, (void*) &l, sizeof(l), 0);
    if(rv < 0) {};

    rv = send(sckt, (void*) text, length, 0);
    if(rv < 0) {};  
};

void srv_who(int sckt, struct users* reg, int last) {
    int i, rv, length, l;
    char text[DIM];

    strcpy(text, "Nessun utente in linea\n");//No user online

    if(last == 0) { //if no user is online
        length = strlen(text);
        l = htons(length);

        rv = send(sckt, (void*) &l, sizeof(l), 0);
        if(rv < 0) {};

        rv = send(sckt, (void*) text, length, 0);
        if(rv < 0) {};
    } else { 
        strcpy(text, "Utenti in linea:\n"); //Users online:

        length = strlen(text);
        l = htons(length);

        rv = send(sckt, (void*) &l, sizeof(l), 0);
        if(rv < 0) {};

        rv = send(sckt, (void*) text, length, 0);
        if(rv < 0) {};

        //Here I send the number of users online, to synchronize server and client
        length = sizeof(last);
        l = htons(length);

        rv = send(sckt, (void*) &l, sizeof(l), 0);
        if(rv < 0) {};

        l = htons(last);
        rv = send(sckt, (void*) &l, length, 0);
        if(rv < 0) {};

        //Transmitting users
        for(i=0; i<last; i++) {
            if(reg[i].status == 'c') {
                length = strlen(reg[i].username);
                l = htons(length);

                rv = send(sckt, (void*) &l, sizeof(l), 0);
                if(rv < 0) {};

                rv = send(sckt, (void*) reg[i].username, length, 0);
                if(rv < 0) {};
            };
        };
    };
};

void srv_deregister(int sckt, struct users* reg, char* name, int* last) {
    int i, rv, length, l;
    char text[DIM];
    strcpy(text, "Deregistrazione completata.\n"); //De-registration completed

    for(i=0; i<*last; i++) {
        if(strcmp(reg[i].username, name) == 0) { //user found
            reg[i] = reg[*last];
            (*last)--;

            length = strlen(text);
            l = htons(length);

            rv = send(sckt, (void*) &l, sizeof(l), 0);
            if(rv < 0) {};

            rv = send(sckt, (void*) text, length, 0);
            if(rv < 0) {};

            return;
        };
    };
}

int srv_register(int sckt, struct users* reg, char* name, struct sockaddr_in client, int p, int* last) { //char*
    int i, rv, length, l, j, pos;
    //char pos[MSG_DIM]; // = "";
    char text[DIM];
    char buffer[MSG_DIM];

    memset(text, 0, DIM);
    strcpy(text, "Registrazione effettuata con successo.\n"); //Registration completed


    for(i=0; i<*last; i++) {

        if(strcmp(reg[i].username, name) == 0) { //User already registered: re-connection
            strcpy(text, "L'utente era gia' registrato al servizio: riconnessione completata.\n"); //Re-connection completed
            reg[i].status = 'c';
            reg[i].address.sin_family = client.sin_family;
            reg[i].address.sin_port = p;
            reg[i].address.sin_addr.s_addr = client.sin_addr.s_addr;

            length = strlen(text);
            l = htons(length);

            rv = send(sckt, (void*) &l, sizeof(l), 0);
            if(rv < 0) {};

            rv = send(sckt, (void*) text, length, 0);
            if(rv < 0) {};

            if(reg[i].msg > 0) { //messaggi offline
                strcpy(text, "Messaggi offline presenti.\n"); //Offline messages found

                length = strlen(text);
                l = htons(length);

                rv = send(sckt, (void*) &l, sizeof(l), 0);
                if(rv < 0) {};

                rv = send(sckt, (void*) text, length, 0);
                if(rv < 0) {};

                //Sending number of messages to be sent
                length = sizeof(*last);
                l = htons(length);

                rv = send(sckt, (void*) &l, sizeof(l), 0);
                if(rv < 0) {};

                l = htons(*last);

                rv = send(sckt, (void*) &l, length, 0);
                if(rv < 0) {};

                for(j=0; j<reg[i].msg; j++) {

                    sprintf(buffer, "%s > ", reg[i].sender[j]);
                    strcat(buffer, reg[i].pendent[j]);

                    length = strlen(buffer);
                    l = htons(length);

                    rv = send(sckt, (void*) &l, sizeof(l), 0);
                    if(rv < 0) {};

                    rv = send(sckt, (void*) buffer, length, 0);
                    if(rv < 0) {};
                };

                //Set number of messages to send to 0
                reg[i].msg = 0;
            } else {
                strcpy(text, "Nessun messaggio offline presente.\n"); //No offline messages

                rv = send(sckt, (void*) &l, sizeof(l), 0);
                if(rv < 0) {};

                rv = send(sckt, (void*) text, length, 0);
                if(rv < 0) {};
            };
            return 1;
        };
    };

    //If the user is not already registered

    if(*last == MAX_UTN) { //max number of users reached
        strcpy(text, "Impossibile registrarsi al servizio: numero limite di utenti raggiunto.\n"); //Max number of users reached
        pos = -1;
    } else {
        strcpy(reg[*last].username, name);
        reg[*last].address.sin_family = client.sin_family;
        reg[*last].address.sin_port = p;
        reg[*last].address.sin_addr.s_addr = client.sin_addr.s_addr;
        reg[*last].status = 'c';
        reg[*last].msg = 0;
        (*last)++;
        pos = 1;
    };

    length = strlen(text);
    l = htons(length);

    rv = send(sckt, (void*) &l, sizeof(l), 0);
    if(rv < 0) {}

    rv = send(sckt, (void*) text, length, 0);
    if(rv < 0) {}

    return pos;
}

void srv_quit(int sckt, struct users* reg, char* user, int num) {
    int i, length, l, rv;
    char text[DIM];
    memset(text, 0, DIM);
    strcpy(text, "Utente disconnesso.\n"); //_User disconnected

    for(i=0; i<num; i++) {
        if(strcmp(reg[i].username, user) == 0) { //user actually registered to the service
            if(reg[i].status == 'c') {
                reg[i].status = 'o';
            };
            memset(&reg[i].address, 0, sizeof(reg[i].address));

            length = strlen(text);
            l = htons(length);

            rv = send(sckt, (void*) &l, sizeof(l), 0);
            if(rv < 0) {};

            rv = send(sckt, (void*) text, length, 0);
            if(rv < 0) {};

            printf("%s disconnesso.\n", user); //<User> disconnected

            return;
            };
        }; 
    //if here, user is not registered to the service
    strcpy(text, "L'utente non era registrato al servizio.\n"); //User not registered

    length = strlen(text);
    l = htons(length);

    rv = send(sckt, (void*) &l, sizeof(l), 0);
    if(rv < 0) {};

    rv = send(sckt, (void*) text, length, 0);
    if(rv < 0) {};
}

void srv_send(int sckt, struct users* reg, int num, char* dest, char* source) {
    int i, rv, length, l;
    char text[DIM];
    char addr[ADDR_DIM];
    memset(text, 0, DIM);

    for(i=0; i<num; i++) {
        if(strcmp(reg[i].username, dest) == 0) { //User registered

            if(reg[i].status == 'c') { //user connected: provides destination UDP address to the sender (it will directly send the message)
                inet_ntop(AF_INET, &reg[i].address.sin_addr.s_addr, addr, sizeof(addr));
                sprintf(text, "Client attualmente connesso: %s %d \n", addr , ntohs(reg[i].address.sin_port));
                break;
            } else { //user disconnected: save offline message
                //Getting the message
                rv = recv(sckt, (void*) &l, sizeof(l), 0);
                if(rv < 0) {};

                length = ntohs(l);

                rv = recv(sckt, (void*) reg[i].pendent, length, 0);
                if(rv < 0) {};

                reg[i].pendent[reg[i].msg][length] = '\0';
                strcpy(reg[i].sender[reg[i].msg], source);
                reg[i].msg++;

                //Sending info to te sender
                strcpy(text, "Messaggio salvato per l'invio offline.\n"); //Message saved offline

                length = strlen(text);
                l = htons(length);

                rv = send(sckt, (void*) &l, sizeof(l), 0);
                if(rv < 0) {};

                rv = send(sckt, (void*) text, length, 0);
                if(rv < 0) {};

                return;
            };
        };
    };
    //Here if got into the if statement
    length = strlen(text);
    l = htons(length);

    rv = send(sckt, (void*) &l, sizeof(l), 0);
    if(rv < 0) {};

    rv = send(sckt, (void*) text, length, 0);
    if(rv < 0) {};
}

int main(int argc, char* argv[]) {
    //shared (mapped) memory
    struct users* registered = mmap(0, MAX_CONN*sizeof(struct users), PROT_READ | PROT_WRITE,  MAP_ANONYMOUS | MAP_SHARED, 0, 0);
    //last index with user struct to be used
    int* last = mmap(0, sizeof(int), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0); 

    int sd; //socket descriptor
    int conn_sd; //socket descriptor after connect()
    struct sockaddr_in my_addr;
    struct sockaddr_in cl_addr;
    int err; //for errors
    int udp_port;
    char check[NAME_DIM]; //will containg the username of the client connected
    socklen_t len;
    int pid;
    int dim_rcvd_net, dim_rcvd, dim_sent_net, dim_sent;
    char buffer[DIM];
    char cmd[CMD_DIM];
    char name[NAME_DIM];
    char port[PORT_DIM];

    *last = 0;
    memset(registered, 0, MAX_CONN*sizeof(struct users));
    memset(check,0,  NAME_DIM);
    puts("Avvio del servizio...\n"); //Service starting..


    if(argc != 2) {
        printf("Errore: troppi argomenti. \n Utilizzare solamente un argomento."); //Please use only one argument
        exit(EXIT_FAILURE);
    };

    //Maybe check the argument itself?

    sd = socket(AF_INET, SOCK_STREAM, 0);

    memset(&my_addr, 0, sizeof(my_addr));

    //Socket parameters
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(atoi(argv[1]));
    my_addr.sin_addr.s_addr = INADDR_ANY;

    err = bind(sd, (struct sockaddr*) &my_addr, sizeof(my_addr));
    if(err < 0) {};

    err = listen(sd, MAX_CONN);
    if(err < 0) {};
    puts("Servizio attivo.\n"); //Service activated

    //Concurrent server
    while(1) {

        len = sizeof(my_addr);
        conn_sd = accept(sd, (struct sockaddr*) &cl_addr, &len);
        //error check to be done

        pid = fork();
        if(pid == 0) {
            //Child         
            close(sd);

            while(1) {
                //Command dimension
                err = recv(conn_sd, (void*) &dim_rcvd_net, sizeof(dim_rcvd), 0);
                if(err < 0) {};

                dim_rcvd = ntohs(dim_rcvd_net);

                //Command
                err = recv(conn_sd, (void*) buffer, dim_rcvd, 0);
                if(err < 0) {};

                buffer[dim_rcvd] = '\0';

                sscanf(buffer, "%s", cmd);

                if(strcmp(cmd, "!help") == 0) {
                    srv_help(conn_sd);
                };

                if(strcmp(cmd, "!register") == 0) {
                    if(strlen(check) > 0) { //Already connected with another username
                        puts("Sei già connesso con altro nome.\n"); //Already connected
                    } else {
                        sscanf(buffer, "%s %s %s", cmd, name, port);
                        udp_port = atoi(port);

                        if(srv_register(conn_sd, registered, name, cl_addr, udp_port, last) == 1) {
                            strcpy(check, name);
                        };
                    };
                };

                if(strcmp(cmd, "!deregister") == 0) {
                    if(strlen(check) <= 0) { //if not registered
                        strcpy(buffer, "Devi essere connesso per poter utilizzare questa funzione.\n"); //You have to be registered

                        dim_sent = strlen(buffer);
                        dim_sent_net = htons(dim_sent);

                        err = send(conn_sd, (void*) &dim_sent_net, sizeof(dim_sent_net), 0);
                        if(err < 0) {};

                        err = send(conn_sd, (void*) buffer, dim_sent, 0);
                        if(err < 0) {};
                    } else {
                        srv_deregister(conn_sd, registered, check, last);
                        strcpy(check, "");
                    };
                };

                if(strcmp(cmd, "!send") == 0) {
                    if(strlen(check) <= 0) { //if not registered
                        strcpy(buffer, "Devi essere connesso per poter utilizzare questa funzione.\n"); //You have to be registered

                        dim_sent = strlen(buffer);
                        dim_sent_net = htons(dim_sent);

                        err = send(conn_sd, (void*) &dim_sent_net, sizeof(dim_sent_net), 0);
                        if(err < 0) {};

                        err = send(conn_sd, (void*) buffer, dim_sent, 0);
                        if(err < 0) {};
                    } else {
                        sscanf(buffer, "%s %s", cmd, name);
                        srv_send(conn_sd, registered, *last, name, check);
                    };
                };

                if(strcmp(cmd, "!who") == 0) {

                    if(strlen(check) <= 0) { //if not registered
                        strcpy(buffer, "Devi essere connesso per poter utilizzare questa funzione"); //You have to be registered

                        dim_sent = strlen(buffer);
                        dim_sent_net = htons(dim_sent);

                        err = send(conn_sd, (void*) &dim_sent_net, sizeof(dim_sent_net), 0);
                        if(err < 0) {};

                        err = send(conn_sd, (void*) buffer, dim_sent, 0);
                        if(err < 0) {};
                    } else {
                        srv_who(conn_sd, registered, *last);
                    };
                };

                if(strcmp(cmd, "!quit") == 0) {
                    srv_quit(conn_sd, registered, check, *last);
                    close(conn_sd);
                    exit(EXIT_SUCCESS);
                };
            };

        } else {

            //Parent
            close(conn_sd);
        };

    };

    close(sd);
    return 0;
}

EDIT 2 There's something I don't get. I've just re-executed the exactly same program I've pasted here, and it seems to send the messages correctly (only online for now).
Could it be some synchronization problem maybe?
EDIT 3 This is the text of the assignment: http://www2.ing.unipi.it/c.vallati/files/reti/Progetto2017.pdf
As I said in the comments, it's in italian..


Solution

  • OK, I've solved my problem!

    First of all, I moved the UDP socket creation. Previously, in the code I've pasted here, it was before the fork(). Thinking about it, I thought it was wrong because parent and child would have shared the socket and the child would have continuously used it, and it seems I was right. So I create a separate UDP for parent (to send to other clients) and child (to receive transmissions).

    Then I figured why sometimes the code worked and sometimes not: in my previous executions, when the program froze I was shutting it down with Ctrl+Z; looking at the output of htop, I noticed some of its execution (the children, to be precise) were still running and occupying the sockets. So I went ahead and terminate them.

    After adding some more error capture code and correcting what was wrong in the communication, everyting went fine and still does.
    And tis time I made sure to make the parent kill() the child when closed.

    So yeah, all works as expected regarding the approach with the "dual" transmissions (first the dimension of the text, and then the text itself).