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(®[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, ®[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..
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).