Oppure

Loading
Questo topic e' stato chiuso dal moderatore.
30/04/11 21:48
salvo1988
Sto provando a sviluppare una semplice chat in c usando le socket di Berkeley.
Questo sono le richieste di progetto:

Realizzare una chat. Un server contiene una lista dei client che si sono connessi. Il client si connette al server per chiedere una lista degli altri client con cui potersi connettere ma resta anche in attesa di connessioni di altri client (queste connessioni saranno comunicazioni di chat).

Il programma è praticamente pronto ed è stato realizzato in modo che un client apre una connessione UDP con il server, il quale per ogni nuovo utente aggiorna una lista dei client connessi e per ciascuno di questi client prende informazioni sulla porta TCP sulla quale questi restano in attesa di nuove richieste di connessioni da parte di altri client.Una volta connesso al server il client può richiedere la lista degli utenti connessi al server e connettersi con uno di questi aprendo una sessione di chat privata(creando quindi una socket che usa la porta TCP diversa rispetto a quella con cui è connesso al server che invece usa l'UDP) in cui lo scambio di messaggi tra i due utenti avviene senza passare dal server(per cui ciascun client può anche funzionare da server).A questo punto si apre un canale diretto tra i due utenti i quali senza problemi possono procedere allo scambio di messaggi.
Il problema che nasce è invece il seguente:
nel momento in cui il client che ha aperto la sessione di chat privata decide di chiudere la connessione tutto funziona correttamente, viceversa se è il client che ha accettato la richiesta di sessione di chat(cioè se il client sta funzionando da server)a chiudere la connessione tra i due il programma crasha.
AIUTATEMI PER FAVORE NON SO PIÙ CHE FARE PER RISOLVERE IL PROBLEMA!

Ecco il codice.

SERVER

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

/* true non esiste in c, bisogna definirlo */
#define TRUE 1
#define BUFLEN 512
#define PORT 9930

/*
* substring workaround per C
*/
char * substring(const char* str, size_t begin, size_t len) {
if (str == 0 || strlen(str) == 0 || strlen(str) < begin || strlen(str) < (begin+len))
return "";
return strndup(str + begin, len);
}

/* struttura client, contenente:
* ip -> l'ip da dove proviene il client
* portaudp -> la porta di connessione server centrale <-> client
* la coppia ip:portaudp definisce univocamente un client, è necessario in caso di diversi client su localhost
* portatcp -> la porta su cui il client aspetta un altro client per la connessione diretta
* nick -> il nick del client
*/
struct client {
char * ip;
int portaudp;
int portatcp;
char * nick;
struct client *next;
};
typedef struct client Tnodo;

/* la lista in cui manteniamo i client e la sua dimensione */
Tnodo * lista = NULL;
int lista_size = 0;

/* aggiunta di un client alla lista conoscendo ip, porta e nick */
int add_client(char * ip, int portaudp, char * nick) {
Tnodo * L;
if(lista == NULL) {
/* se la lista è vuota creiamo il primo nodo con i dati necessari */
lista = (Tnodo*) malloc (sizeof(Tnodo));
lista->ip = ip;
lista->portaudp = portaudp;
lista->portatcp = 0;
lista->nick = nick;
lista->next = NULL;
lista_size++;
} else {
L = lista;
while(L != NULL) {
/* se la lista non è vuota scandiamo i nodi alla ricerca di quello del client */
if(strcmp(L->ip, ip) == 0 && L->portaudp == portaudp) {
/* se viene trovato cambiamo nick al client senza creare un nuovo nodo */
L->nick = nick;
return 1;
}
L = L->next;
}
L = lista;
/* se non viene trovato scandiamo la lista fino all'ultimo nodo e appendiamo in coda un nuovo nodo per il nuovo client */
while(L->next != NULL) {
L = L->next;
}
L->next = (Tnodo*) malloc (sizeof(Tnodo));
L->next->ip = ip;
L->next->portaudp = portaudp;
L->next->portatcp = 0;
L->next->nick = nick;
L->next->next = NULL;
lista_size++;
}
return 1;
}

/* dopo aver inserito il client nella lista dobbiamo inserire la porta tcp su cui è in ascolto di altri client */
int set_tcp_port(char * ip, int portaudp, int portatcp) {
Tnodo * L = lista;
while(L != NULL) {
if(strcmp(L->ip, ip) == 0 && L->portaudp == portaudp) {
L->portatcp = portatcp;
return 1;
}
L = L->next;
}
return 0;
}

/* dopo il comando 'LISTA' creiamo la lista dei client e la inviamo */
char * get_clients(char * ip, int portaudp) {
Tnodo * L;
char *result = NULL;
int i = 0, len = 0;

/* la prima scansione serve a calcolare lo spazio da allocare alla stringa, per evitare buffer di migliaia di posizioni
* forse inutili o anche insufficienti */
L = lista;
while (L != NULL) {
if(strcmp(L->ip, ip) != 0 || L->portaudp != portaudp) {
len += strlen(L->nick) + 1;
}
L = L->next;
}

/* allochiamo lo spazio con malloc */
result = malloc(len * sizeof(char) + 1);
if(result == NULL) {
fprintf(stderr, "Error - mkconcat -> malloc()\n";);
return NULL;
}

/* con questa seconda scansione inseriamo effettivamente i nick dei client nella lista evitando il nick che ha fatto la richiesta */
L = lista;
while (L != NULL) {
if(strcmp(L->ip, ip) != 0 || L->portaudp != portaudp) {
if(strcat(result, "\n";) == NULL) {
fprintf(stderr, "Error - strcat()\n";);
return NULL;
}
if(strcat(result, L->nick) == NULL) {
fprintf(stderr, "Error - strcat()\n";);
return NULL;
}
}
L = L->next;
}
return result;
}

/* dopo 'CONNECT|nick' recuperiamo ip e porta del client cercato e li inviamo al client che li ha chiesti */
char * get_client(char * nick) {
Tnodo * L = lista;
while(L != NULL) {
if(strcmp(L->nick, nick) == 0) {
sprintf(nick, "CLIENT|%s|%d", L->ip, L->portatcp);
return nick;
}
L = L->next;
}
return "NOT_FOUND";
}

/* dopo 'DELETE' troviamo il client e lo eliminiamo dalla lista */
char * del_client(char * ip, int portaudp) {
Tnodo * L = lista;
Tnodo * LP = NULL;
while (L != NULL) {
if(L->ip != NULL && L->portaudp != 0 && strcmp(L->ip, ip) == 0 && L->portaudp == portaudp) {
/* dopo aver trovato il nodo da eliminare: */
if(L->next != NULL) {
/* se next è diverso da null settiamo L uguale al next, escludendo di fatti il nodo da eliminare */
*L = *L->next;
} else {
/* se next è null abbiamo due possibilità */
if(LP != NULL) {
/* se il precedente del nodo da eliminare è diverso da null il nodo da eliminare è l'ultimo, quindi settiamo il next del penultimo a null */
LP->next = NULL;
} else {
/* se il precedente del nodo da eliminare è null (e il next è null) abbiamo un nodo solo, quindi annulliamo tutta la lista */
lista = NULL;
}
}
return "Utente eliminato.";
}
LP = L;
L = L->next;
}
return "Utente non presente.";
}

/* dopo 'DEBUG' stampo tutta la lista a video senza inviarla ai client
* qui si possono inserire altri comandi per verificare lo stato del serve in esecuzione */
void * debug_lista() {
Tnodo * L = lista;
while (L != NULL) {
printf("ip: %s, porta: %d, nick: %s, tcp: %d\n", L->ip, L->portaudp, L->nick, L->portatcp);
L = L->next;
}
fflush(stdout);
}



int main(void) {
struct sockaddr_in server_socket, client_socket;
int s, i, client_lenght = sizeof(client_socket);
char buf[BUFLEN];
Tnodo tmp;

if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
perror("socket";);
exit(1);
}

memset((void *)&server_socket, 0, sizeof(server_socket));
server_socket.sin_family = AF_INET;
server_socket.sin_port = htons(PORT);
server_socket.sin_addr.s_addr = htonl(INADDR_ANY);

if (bind(s, (struct sockaddr *) &server_socket, sizeof(server_socket))==-1) {
perror("bind";);
exit(1);
}
printf("Server attivo e in ascolto sulla porta %d\n",PORT);

/* il server centrale resta sempre attivo */
while(TRUE) {
/* svuoto tutto il buffer altrimenti succederebbe:
* invio 'ciao' -> leggo 'ciao'
* invio 'come' -> leggo 'come'
* invio 'xx' -> leggo 'xxme'
* invio 'z' -> leggo 'zxme'
* da qui la necessità di svuotare il buffer.
*/
memset(buf,'Sto provando a sviluppare una semplice chat in c usando le socket di Berkeley.
Questo sono le richieste di progetto:

Realizzare una chat. Un server contiene una lista dei client che si sono connessi. Il client si connette al server per chiedere una lista degli altri client con cui potersi connettere ma resta anche in attesa di connessioni di altri client (queste connessioni saranno comunicazioni di chat).

Il programma è praticamente pronto ed è stato realizzato in modo che un client apre una connessione UDP con il server, il quale per ogni nuovo utente aggiorna una lista dei client connessi e per ciascuno di questi client prende informazioni sulla porta TCP sulla quale questi restano in attesa di nuove richieste di connessioni da parte di altri client.Una volta connesso al server il client può richiedere la lista degli utenti connessi al server e connettersi con uno di questi aprendo una sessione di chat privata(creando quindi una socket che usa la porta TCP diversa rispetto a quella con cui è connesso al server che invece usa l'UDP) in cui lo scambio di messaggi tra i due utenti avviene senza passare dal server(per cui ciascun client può anche funzionare da server).A questo punto si apre un canale diretto tra i due utenti i quali senza problemi possono procedere allo scambio di messaggi.
Il problema che nasce è invece il seguente:
nel momento in cui il client che ha aperto la sessione di chat privata decide di chiudere la connessione tutto funziona correttamente, viceversa se è il client che ha accettato la richiesta di sessione di chat(cioè se il client sta funzionando da server)a chiudere la connessione tra i due il programma crasha.
AIUTATEMI PER FAVORE NON SO PIÙ CHE FARE PER RISOLVERE IL PROBLEMA!

Ecco il codice.

SERVER

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

/* true non esiste in c, bisogna definirlo */
#define TRUE 1
#define BUFLEN 512
#define PORT 9930

/*
* substring workaround per C
*/
char * substring(const char* str, size_t begin, size_t len) {
if (str == 0 || strlen(str) == 0 || strlen(str) < begin || strlen(str) < (begin+len))
return "";
return strndup(str + begin, len);
}

/* struttura client, contenente:
* ip -> l'ip da dove proviene il client
* portaudp -> la porta di connessione server centrale <-> client
* la coppia ip:portaudp definisce univocamente un client, è necessario in caso di diversi client su localhost
* portatcp -> la porta su cui il client aspetta un altro client per la connessione diretta
* nick -> il nick del client
*/
struct client {
char * ip;
int portaudp;
int portatcp;
char * nick;
struct client *next;
};
typedef struct client Tnodo;

/* la lista in cui manteniamo i client e la sua dimensione */
Tnodo * lista = NULL;
int lista_size = 0;

/* aggiunta di un client alla lista conoscendo ip, porta e nick */
int add_client(char * ip, int portaudp, char * nick) {
Tnodo * L;
if(lista == NULL) {
/* se la lista è vuota creiamo il primo nodo con i dati necessari */
lista = (Tnodo*) malloc (sizeof(Tnodo));
lista->ip = ip;
lista->portaudp = portaudp;
lista->portatcp = 0;
lista->nick = nick;
lista->next = NULL;
lista_size++;
} else {
L = lista;
while(L != NULL) {
/* se la lista non è vuota scandiamo i nodi alla ricerca di quello del client */
if(strcmp(L->ip, ip) == 0 && L->portaudp == portaudp) {
/* se viene trovato cambiamo nick al client senza creare un nuovo nodo */
L->nick = nick;
return 1;
}
L = L->next;
}
L = lista;
/* se non viene trovato scandiamo la lista fino all'ultimo nodo e appendiamo in coda un nuovo nodo per il nuovo client */
while(L->next != NULL) {
L = L->next;
}
L->next = (Tnodo*) malloc (sizeof(Tnodo));
L->next->ip = ip;
L->next->portaudp = portaudp;
L->next->portatcp = 0;
L->next->nick = nick;
L->next->next = NULL;
lista_size++;
}
return 1;
}

/* dopo aver inserito il client nella lista dobbiamo inserire la porta tcp su cui è in ascolto di altri client */
int set_tcp_port(char * ip, int portaudp, int portatcp) {
Tnodo * L = lista;
while(L != NULL) {
if(strcmp(L->ip, ip) == 0 && L->portaudp == portaudp) {
L->portatcp = portatcp;
return 1;
}
L = L->next;
}
return 0;
}

/* dopo il comando 'LISTA' creiamo la lista dei client e la inviamo */
char * get_clients(char * ip, int portaudp) {
Tnodo * L;
char *result = NULL;
int i = 0, len = 0;

/* la prima scansione serve a calcolare lo spazio da allocare alla stringa, per evitare buffer di migliaia di posizioni
* forse inutili o anche insufficienti */
L = lista;
while (L != NULL) {
if(strcmp(L->ip, ip) != 0 || L->portaudp != portaudp) {
len += strlen(L->nick) + 1;
}
L = L->next;
}

/* allochiamo lo spazio con malloc */
result = malloc(len * sizeof(char) + 1);
if(result == NULL) {
fprintf(stderr, "Error - mkconcat -> malloc()\n";);
return NULL;
}

/* con questa seconda scansione inseriamo effettivamente i nick dei client nella lista evitando il nick che ha fatto la richiesta */
L = lista;
while (L != NULL) {
if(strcmp(L->ip, ip) != 0 || L->portaudp != portaudp) {
if(strcat(result, "\n";) == NULL) {
fprintf(stderr, "Error - strcat()\n";);
return NULL;
}
if(strcat(result, L->nick) == NULL) {
fprintf(stderr, "Error - strcat()\n";);
return NULL;
}
}
L = L->next;
}
return result;
}

/* dopo 'CONNECT|nick' recuperiamo ip e porta del client cercato e li inviamo al client che li ha chiesti */
char * get_client(char * nick) {
Tnodo * L = lista;
while(L != NULL) {
if(strcmp(L->nick, nick) == 0) {
sprintf(nick, "CLIENT|%s|%d", L->ip, L->portatcp);
return nick;
}
L = L->next;
}
return "NOT_FOUND";
}

/* dopo 'DELETE' troviamo il client e lo eliminiamo dalla lista */
char * del_client(char * ip, int portaudp) {
Tnodo * L = lista;
Tnodo * LP = NULL;
while (L != NULL) {
if(L->ip != NULL && L->portaudp != 0 && strcmp(L->ip, ip) == 0 && L->portaudp == portaudp) {
/* dopo aver trovato il nodo da eliminare: */
if(L->next != NULL) {
/* se next è diverso da null settiamo L uguale al next, escludendo di fatti il nodo da eliminare */
*L = *L->next;
} else {
/* se next è null abbiamo due possibilità */
if(LP != NULL) {
/* se il precedente del nodo da eliminare è diverso da null il nodo da eliminare è l'ultimo, quindi settiamo il next del penultimo a null */
LP->next = NULL;
} else {
/* se il precedente del nodo da eliminare è null (e il next è null) abbiamo un nodo solo, quindi annulliamo tutta la lista */
lista = NULL;
}
}
return "Utente eliminato.";
}
LP = L;
L = L->next;
}
return "Utente non presente.";
}

/* dopo 'DEBUG' stampo tutta la lista a video senza inviarla ai client
* qui si possono inserire altri comandi per verificare lo stato del serve in esecuzione */
void * debug_lista() {
Tnodo * L = lista;
while (L != NULL) {
printf("ip: %s, porta: %d, nick: %s, tcp: %d\n", L->ip, L->portaudp, L->nick, L->portatcp);
L = L->next;
}
fflush(stdout);
}



int main(void) {
struct sockaddr_in server_socket, client_socket;
int s, i, client_lenght = sizeof(client_socket);
char buf[BUFLEN];
Tnodo tmp;

if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
perror("socket";);
exit(1);
}

memset((void *)&server_socket, 0, sizeof(server_socket));
server_socket.sin_family = AF_INET;
server_socket.sin_port = htons(PORT);
server_socket.sin_addr.s_addr = htonl(INADDR_ANY);

if (bind(s, (struct sockaddr *) &server_socket, sizeof(server_socket))==-1) {
perror("bind";);
exit(1);
}
printf("Server attivo e in ascolto sulla porta %d\n",PORT);

/* il server centrale resta sempre attivo */
while(TRUE) {
/* svuoto tutto il buffer altrimenti succederebbe:
* invio 'ciao' -> leggo 'ciao'
* invio 'come' -> leggo 'come'
* invio 'xx' -> leggo 'xxme'
* invio 'z' -> leggo 'zxme'
* da qui la necessità di svuotare il buffer.
*/
memset(buf,'{parsed_message}',BUFLEN);

/* aspetto di ricevere un messaggio da qualunque client */
if(recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &client_socket, &client_lenght) == -1) {
perror("recvfrom()";);
}

if(strcmp("NAME|", substring(buf, 0, 5)) == 0) {
/* se il client sta inviando il suo nick lo inserisco nella lista, e chiedo la porta tcp */
char * nick = substring(buf, 5, strlen(buf) - 5);
     printf("Un nuovo utente si è connesso:\n";);
printf("IP ADDRESS -> %s\t UDP PORT -> %d \t New nick -> %s\n", inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port), nick);

if(add_client(inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port), nick)) {
if(sendto(s, "PROVIDE_TCP_PORT", BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else {
if(sendto(s, "Nick non accettato.", BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
}
} else if(strcmp("TCP|", substring(buf, 0, 4)) == 0) {
/* se il client sta inviando la sua porta tcp, solitamente chiesta in automatico la inserisco nella lista */
int porta = atoi(substring(buf, 4, strlen(buf) - 4));
printf("TCP PORT ASSIGNED -> %d\n",porta);

set_tcp_port(inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port), porta);
if(sendto(s, " ", 1, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else if(strcmp("LISTA", buf) == 0) {
/* se un client sta chiedendo la lista dei client noti la invio */
if(sendto(s, get_clients(inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port)), BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else if(strcmp("CONNECT|", substring(buf, 0, 8)) == 0) {
/* se un client sta chiedendo i parametri di un client li cerco nella lista e li invio */
char * nick = substring(buf, 8, strlen(buf) - 8);
if(sendto(s, get_client(nick), BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else if(strcmp("DELETE", buf) == 0) {
/* se un client sta chiedendo di essere eliminato lo cancello dalla lista */
if(sendto(s, del_client(inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port)), BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else if(strcmp("DEBUG", buf) == 0) {
/* eseguo il debug */
debug_lista();
if(sendto(s, buf, BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else {
/* stampo a video qualunque comando non riconosciuto, il printf può essere eliminato, il sendto assolutamente no.
* anche dopo un comando errato il client si aspetta una risposta. */
printf("%s:%d -> %s\n", inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port), buf);
if(sendto(s, " ", BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
}
fflush(stdout);
}

close(s);
return 0;
}


CLIENT

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

#define BUFLEN 512
#define PORT 9930
#define SRV_IP "127.0.0.1"

/* ciclo principale del programma = true */
int ciclo = 1;

/*
* parametri di configurazione della socket in ascolto di altri client
* rispettivamente descrittore della socket e porta.
* se la porta è occupata viene incrementata prima di riprovare
* questo è necessario per mettere in ascolto due client entrambi su localhost
*/
int connected_ts = 0;
int ts_port = 9930;

/*
* parametri di configurazione della socket in uscita verso un altro client
* per connettersi ogni client deve prima chiedere al server quali client sono disponibili
* e i loro ip:porta
*/
int connected_tc = 0;

/*
* il nick che inviamo al server viene mantenuto qui
*/
char * nick;

/*
* substring workaround per C
*/
char * substring(const char* str, size_t begin, size_t len) {
if (str == 0 || strlen(str) == 0 || strlen(str) < begin || strlen(str) < (begin+len))
return "";
return strndup(str + begin, len);
}

/*
* come detto ogni client ha una socket aperta in ascolto di altri client
* la socket viene aperta in un thread e resta aperta per tutto il ciclo di vita del client
*/
void * thread_server(void *x) {
int sock, bytes_recieved, i = 1;
char s_buffer[BUFLEN], r_buffer[BUFLEN];

struct sockaddr_in server_addr, client_addr;
int sin_size = sizeof(struct sockaddr_in);

if((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
printf("Socket\n";);
exit(-1);
}

if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(int)) == -1) {
printf("Setsockopt\n";);
exit(-1);
}

memset((void *)&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;

do {
ts_port++;
server_addr.sin_port = htons(ts_port);
i = bind(sock, (struct sockaddr *) & server_addr, sizeof(struct sockaddr));
} while(i == -1 && ts_port <= 10930);

if(i == -1) {
printf("Unable to bind\n";);
exit(-1);
}

if(listen(sock, 5) == -1) {
printf("Listen\n";);
exit(-1);
}

connected_ts = accept(sock, (struct sockaddr *)&client_addr,&sin_size);

printf("I got a connection from (%s , %d)\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

while(1) {
memset(r_buffer,'{parsed_message}',BUFLEN);
bytes_recieved = recv(connected_ts,r_buffer,BUFLEN,0);
if(strcmp(r_buffer , "DISCONNECT";) == 0) {
printf("I got a disconnect request from (%s , %d) ************** Disconnection performed\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
break;
} else {
printf("%s: %s\n" , nick, r_buffer);
}
}
close(sock);
sock = 0;
connected_ts = 0;
}

/*
* la socket verso un altro client viene aperta nel main,
* successivamente viene creato il thread che cicla sulla lettura di questa socket
*/
void * thread_client(void *x) {
int bytes_recieved;
char s_buffer[BUFLEN], r_buffer[BUFLEN];
while(1) {
memset(r_buffer,'{parsed_message}',BUFLEN);
bytes_recieved=recv(connected_tc, r_buffer, BUFLEN,0);
if(strcmp(r_buffer, "DISCONNECT";) == 0) {
printf("Disconnecting from remote host\n";);
break;
} else {
printf("%s: %s\n" , nick, r_buffer);
}
}
close(connected_tc);
connected_tc = 0;
}


int main(void) {
/* i thread client/server */
pthread_t p_thread_server;
pthread_t p_thread_client;

/* i parametri di connessione verso il server centrale */
struct sockaddr_in server_socket;
int s, i, server_lenght = sizeof(server_socket);
char buf[BUFLEN];

/* creo la socket in ascolto di altri client */
if(pthread_create(&p_thread_server, NULL, thread_server, NULL) != 0) {
fprintf(stderr, "errore non posso creare il thread 1\n";);
exit(-1);
}

if((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
printf("socket";);
exit(-1);
}

/* imposto i parametri di connessione al server centrale */
memset((void *)&server_socket, 0, sizeof(server_socket));
server_socket.sin_family = AF_INET;
server_socket.sin_port = htons(PORT);

if(inet_aton(SRV_IP, &server_socket.sin_addr)==0) {
fprintf(stderr, "inet_aton() failed\n";);
exit(-1);
}
printf("****************************************************************************\n";);
printf("****************************************************************************\n";);
printf("****************************************************************************\n";);
printf("**** ****\n";);
printf("**** ****\n";);
printf("**** ********* **** **** ******** ************ **** \n";);
printf("**** ********* **** **** ********** ************ **** \n";);
printf("**** **** ************ **** **** **** ****\n";);
printf("**** **** ************ ************ **** ****\n";);
printf("**** ********* **** **** **** **** **** ****\n";);
printf("**** ********* **** **** **** **** **** ****\n";);
printf("**** ****\n";);
printf("**** ****\n";);
printf("****************************************************************************\n";);
printf("****************************************************************************\n";);
printf("****************************************************************************\n";);
printf("Digita il comando NAME|<nick utente> per inserire il tuo nickname\n";);
printf("Digita il comando exit per uscire\n";);
printf("Digita il comando LISTA per richiedere la lista dei client connessi al server\n";);
printf("Digita il comando CONNECT|<nick utente> per aprire una sessione di chat con <nick utente>\n";);
printf("Digita il comando DISCONNECT per chiudere la sessione di chat\n";);
/* while 1 il programma resta connesso al server centrale */
while(ciclo) {
/*
* svuoto completamente il buffer prima di ogni lettura
* questo serve per evitare di leggere vecchie stringhe concatenate a quella corrente
*/
memset(buf,'{parsed_message}',BUFLEN);

/* leggo da standard in nel buffer */
scanf("%s", buf);

/*
* se siamo connessi a un altro client come client
* sto chattando direttamente con qualcuno,
* invio i messaggi a lui.
*/
if(connected_tc > 0) {
if(send(connected_tc, buf, BUFLEN, 0) == -1) {
printf("send()";);
}
if(strcmp(buf, "DISCONNECT";) == 0) {
close(connected_tc);
connected_tc = 0;
}
} else
/*
* se siamo connessi a un altro client come server
* sto chattando direttamente con qualcuno,
* invio i messaggi a lui.
*/
if(connected_ts > 0) {
if(strcmp(buf, "exit";) == 0) {
if(send(connected_ts, buf, BUFLEN, 0) == -1) {
printf("send()";);
}
close(connected_ts);
connected_ts = 0;
} else {
char res[BUFLEN];
if(send(connected_ts, buf, BUFLEN, 0) == -1) {
printf("send()";);
}
}
} else
/*
* se non siamo connessi direttamente a nessuno client
* invio i messaggi al server centrale.
* in assenza di messaggi validi il server centrale agisce come un echo server.
* i comandi validi sono:
* 'exit' -> chiude il programma
* 'DELETE' -> rimuove il client dalla lista dei client noti contenuta nel server centrale, digitando exit questo comando viene inviato automaticamente
* 'NAME|xxxx' -> inserisce il client nella lista dei client conosciuti contenuta nel server centrale e setta il nick del client a xxxx, per cambiare nick digitare di nuovo il comando
* 'TCP|xx' -> invia al server centrale la porta su cui il client è in ascolto di connessioni dirette, questo comando viene inviato automaticamente dopo il comando NAME
* 'LISTA' -> ottiene dal server centrale la lista dei client conosciuti
* 'CONNECT|xxxx' -> chiede al server centrale ip e porta del client con nick xxxx e esegue la connessione diretta, quindi fa partire il thread di lettura della socket client
* 'DEBUG' -> fa stampare al server informazioni utili non inviate al client
*/
{
if(strcmp(buf, "exit";) == 0) {
/* in caso di uscita setto ciclo a 0 e invio 'DELETE' */
ciclo = 0;
if(sendto(s, "DELETE", 6, 0, (struct sockaddr *) &server_socket, server_lenght) == -1) {
printf("sendto()";);
}
} else if(strcmp("NAME|", substring(buf, 0, 5)) == 0) {
/* salvo il nick nella variabile all'inizio e lo invio al server centrale per essere inserito nella lista */
nick = substring(buf, 5, strlen(buf) - 5);
if(sendto(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, server_lenght) == -1) {
printf("sendto()";);
}
} else {
/* invio al server centrale tutto quello che leggo */
if(sendto(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, server_lenght) == -1) {
printf("sendto()";);
}
}
/*
* dopo l'invio al server centrale di qualcosa aspetto che il server mi risponde, il server agisce come un echo server, sia in caso di comandi validi che di comandi non validi, quindi ci sarà sempre un ritorno */
if(recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, &server_lenght) == -1) {
printf("recvfrom()";);
}
if(strcmp(buf, "PROVIDE_TCP_PORT";) == 0) {
/* 'PROVIDE_TCP_PORT' -> il server vuole conoscere la porta su cui stiamo aspettando i client diretti, questo comando arriva automaticamente dopo l'invio di 'NAME' e rispondiamo quindi automaticamente col comando 'TCP' */
sprintf(buf,"TCP|%d",ts_port);
if(sendto(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, server_lenght) == -1) {
printf("sendto()";);
}
if(recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, &server_lenght) == -1) {
printf("recvfrom()";);
}
} else if(strcmp("CLIENT|", substring(buf, 0, 7)) == 0) {
/* 'CLIENT|xxxx|yy' -> il server ci comunica ip e porta del client con cui abbiamo scelto di connetterci, dopo vari split avvio la socket diretta verso il client che farà da server nella connessione diretta */
char * info = substring(buf, 7, strlen(buf) - 7);
char * _info = strstr(info, "|";);
int pos = _info - info;
char * ip = substring(info, 0, pos);
int porta = atoi(substring(info, pos + 1, strlen(info) - pos - 1));
printf("CONNECTING TO %s PORT %d\t", ip, porta);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("*\t";);
struct sockaddr_in server_addr;

if ((connected_tc = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
printf("Socket";);
break;
}

memset((void *)&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(porta);
server_addr.sin_addr.s_addr = inet_addr(ip);

if(connect(connected_tc, (struct sockaddr *) & server_addr, sizeof(struct sockaddr)) == -1) {
printf("Connect";);
break;
}
/* se tutto è andato bene apro il thread che cicla sulla socket in lettura */
printf("CONNECTED\n";);
if(pthread_create(&p_thread_client, NULL, thread_client, NULL) != 0) {
fprintf(stderr, "errore non posso creare il thread 2\n";);

}
} else {
/* questo viene eseguito quando un comando non valido ritorna dal server, può essere eliminato */
printf("> %s\n", buf);
}
}
fflush(stdout);
}

close(s);
return 0;
}',BUFLEN);

/* aspetto di ricevere un messaggio da qualunque client */
if(recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &client_socket, &client_lenght) == -1) {
perror("recvfrom()";);
}

if(strcmp("NAME|", substring(buf, 0, 5)) == 0) {
/* se il client sta inviando il suo nick lo inserisco nella lista, e chiedo la porta tcp */
char * nick = substring(buf, 5, strlen(buf) - 5);
     printf("Un nuovo utente si è connesso:\n";);
printf("IP ADDRESS -> %s\t UDP PORT -> %d \t New nick -> %s\n", inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port), nick);

if(add_client(inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port), nick)) {
if(sendto(s, "PROVIDE_TCP_PORT", BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else {
if(sendto(s, "Nick non accettato.", BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
}
} else if(strcmp("TCP|", substring(buf, 0, 4)) == 0) {
/* se il client sta inviando la sua porta tcp, solitamente chiesta in automatico la inserisco nella lista */
int porta = atoi(substring(buf, 4, strlen(buf) - 4));
printf("TCP PORT ASSIGNED -> %d\n",porta);

set_tcp_port(inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port), porta);
if(sendto(s, " ", 1, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else if(strcmp("LISTA", buf) == 0) {
/* se un client sta chiedendo la lista dei client noti la invio */
if(sendto(s, get_clients(inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port)), BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else if(strcmp("CONNECT|", substring(buf, 0, 8)) == 0) {
/* se un client sta chiedendo i parametri di un client li cerco nella lista e li invio */
char * nick = substring(buf, 8, strlen(buf) - 8);
if(sendto(s, get_client(nick), BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else if(strcmp("DELETE", buf) == 0) {
/* se un client sta chiedendo di essere eliminato lo cancello dalla lista */
if(sendto(s, del_client(inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port)), BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else if(strcmp("DEBUG", buf) == 0) {
/* eseguo il debug */
debug_lista();
if(sendto(s, buf, BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else {
/* stampo a video qualunque comando non riconosciuto, il printf può essere eliminato, il sendto assolutamente no.
* anche dopo un comando errato il client si aspetta una risposta. */
printf("%s:%d -> %s\n", inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port), buf);
if(sendto(s, " ", BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
}
fflush(stdout);
}

close(s);
return 0;
}


CLIENT

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

#define BUFLEN 512
#define PORT 9930
#define SRV_IP "127.0.0.1"

/* ciclo principale del programma = true */
int ciclo = 1;

/*
* parametri di configurazione della socket in ascolto di altri client
* rispettivamente descrittore della socket e porta.
* se la porta è occupata viene incrementata prima di riprovare
* questo è necessario per mettere in ascolto due client entrambi su localhost
*/
int connected_ts = 0;
int ts_port = 9930;

/*
* parametri di configurazione della socket in uscita verso un altro client
* per connettersi ogni client deve prima chiedere al server quali client sono disponibili
* e i loro ip:porta
*/
int connected_tc = 0;

/*
* il nick che inviamo al server viene mantenuto qui
*/
char * nick;

/*
* substring workaround per C
*/
char * substring(const char* str, size_t begin, size_t len) {
if (str == 0 || strlen(str) == 0 || strlen(str) < begin || strlen(str) < (begin+len))
return "";
return strndup(str + begin, len);
}

/*
* come detto ogni client ha una socket aperta in ascolto di altri client
* la socket viene aperta in un thread e resta aperta per tutto il ciclo di vita del client
*/
void * thread_server(void *x) {
int sock, bytes_recieved, i = 1;
char s_buffer[BUFLEN], r_buffer[BUFLEN];

struct sockaddr_in server_addr, client_addr;
int sin_size = sizeof(struct sockaddr_in);

if((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
printf("Socket\n";);
exit(-1);
}

if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(int)) == -1) {
printf("Setsockopt\n";);
exit(-1);
}

memset((void *)&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;

do {
ts_port++;
server_addr.sin_port = htons(ts_port);
i = bind(sock, (struct sockaddr *) & server_addr, sizeof(struct sockaddr));
} while(i == -1 && ts_port <= 10930);

if(i == -1) {
printf("Unable to bind\n";);
exit(-1);
}

if(listen(sock, 5) == -1) {
printf("Listen\n";);
exit(-1);
}

connected_ts = accept(sock, (struct sockaddr *)&client_addr,&sin_size);

printf("I got a connection from (%s , %d)\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

while(1) {
memset(r_buffer,'Sto provando a sviluppare una semplice chat in c usando le socket di Berkeley.
Questo sono le richieste di progetto:

Realizzare una chat. Un server contiene una lista dei client che si sono connessi. Il client si connette al server per chiedere una lista degli altri client con cui potersi connettere ma resta anche in attesa di connessioni di altri client (queste connessioni saranno comunicazioni di chat).

Il programma è praticamente pronto ed è stato realizzato in modo che un client apre una connessione UDP con il server, il quale per ogni nuovo utente aggiorna una lista dei client connessi e per ciascuno di questi client prende informazioni sulla porta TCP sulla quale questi restano in attesa di nuove richieste di connessioni da parte di altri client.Una volta connesso al server il client può richiedere la lista degli utenti connessi al server e connettersi con uno di questi aprendo una sessione di chat privata(creando quindi una socket che usa la porta TCP diversa rispetto a quella con cui è connesso al server che invece usa l'UDP) in cui lo scambio di messaggi tra i due utenti avviene senza passare dal server(per cui ciascun client può anche funzionare da server).A questo punto si apre un canale diretto tra i due utenti i quali senza problemi possono procedere allo scambio di messaggi.
Il problema che nasce è invece il seguente:
nel momento in cui il client che ha aperto la sessione di chat privata decide di chiudere la connessione tutto funziona correttamente, viceversa se è il client che ha accettato la richiesta di sessione di chat(cioè se il client sta funzionando da server)a chiudere la connessione tra i due il programma crasha.
AIUTATEMI PER FAVORE NON SO PIÙ CHE FARE PER RISOLVERE IL PROBLEMA!

Ecco il codice.

SERVER

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

/* true non esiste in c, bisogna definirlo */
#define TRUE 1
#define BUFLEN 512
#define PORT 9930

/*
* substring workaround per C
*/
char * substring(const char* str, size_t begin, size_t len) {
if (str == 0 || strlen(str) == 0 || strlen(str) < begin || strlen(str) < (begin+len))
return "";
return strndup(str + begin, len);
}

/* struttura client, contenente:
* ip -> l'ip da dove proviene il client
* portaudp -> la porta di connessione server centrale <-> client
* la coppia ip:portaudp definisce univocamente un client, è necessario in caso di diversi client su localhost
* portatcp -> la porta su cui il client aspetta un altro client per la connessione diretta
* nick -> il nick del client
*/
struct client {
char * ip;
int portaudp;
int portatcp;
char * nick;
struct client *next;
};
typedef struct client Tnodo;

/* la lista in cui manteniamo i client e la sua dimensione */
Tnodo * lista = NULL;
int lista_size = 0;

/* aggiunta di un client alla lista conoscendo ip, porta e nick */
int add_client(char * ip, int portaudp, char * nick) {
Tnodo * L;
if(lista == NULL) {
/* se la lista è vuota creiamo il primo nodo con i dati necessari */
lista = (Tnodo*) malloc (sizeof(Tnodo));
lista->ip = ip;
lista->portaudp = portaudp;
lista->portatcp = 0;
lista->nick = nick;
lista->next = NULL;
lista_size++;
} else {
L = lista;
while(L != NULL) {
/* se la lista non è vuota scandiamo i nodi alla ricerca di quello del client */
if(strcmp(L->ip, ip) == 0 && L->portaudp == portaudp) {
/* se viene trovato cambiamo nick al client senza creare un nuovo nodo */
L->nick = nick;
return 1;
}
L = L->next;
}
L = lista;
/* se non viene trovato scandiamo la lista fino all'ultimo nodo e appendiamo in coda un nuovo nodo per il nuovo client */
while(L->next != NULL) {
L = L->next;
}
L->next = (Tnodo*) malloc (sizeof(Tnodo));
L->next->ip = ip;
L->next->portaudp = portaudp;
L->next->portatcp = 0;
L->next->nick = nick;
L->next->next = NULL;
lista_size++;
}
return 1;
}

/* dopo aver inserito il client nella lista dobbiamo inserire la porta tcp su cui è in ascolto di altri client */
int set_tcp_port(char * ip, int portaudp, int portatcp) {
Tnodo * L = lista;
while(L != NULL) {
if(strcmp(L->ip, ip) == 0 && L->portaudp == portaudp) {
L->portatcp = portatcp;
return 1;
}
L = L->next;
}
return 0;
}

/* dopo il comando 'LISTA' creiamo la lista dei client e la inviamo */
char * get_clients(char * ip, int portaudp) {
Tnodo * L;
char *result = NULL;
int i = 0, len = 0;

/* la prima scansione serve a calcolare lo spazio da allocare alla stringa, per evitare buffer di migliaia di posizioni
* forse inutili o anche insufficienti */
L = lista;
while (L != NULL) {
if(strcmp(L->ip, ip) != 0 || L->portaudp != portaudp) {
len += strlen(L->nick) + 1;
}
L = L->next;
}

/* allochiamo lo spazio con malloc */
result = malloc(len * sizeof(char) + 1);
if(result == NULL) {
fprintf(stderr, "Error - mkconcat -> malloc()\n";);
return NULL;
}

/* con questa seconda scansione inseriamo effettivamente i nick dei client nella lista evitando il nick che ha fatto la richiesta */
L = lista;
while (L != NULL) {
if(strcmp(L->ip, ip) != 0 || L->portaudp != portaudp) {
if(strcat(result, "\n";) == NULL) {
fprintf(stderr, "Error - strcat()\n";);
return NULL;
}
if(strcat(result, L->nick) == NULL) {
fprintf(stderr, "Error - strcat()\n";);
return NULL;
}
}
L = L->next;
}
return result;
}

/* dopo 'CONNECT|nick' recuperiamo ip e porta del client cercato e li inviamo al client che li ha chiesti */
char * get_client(char * nick) {
Tnodo * L = lista;
while(L != NULL) {
if(strcmp(L->nick, nick) == 0) {
sprintf(nick, "CLIENT|%s|%d", L->ip, L->portatcp);
return nick;
}
L = L->next;
}
return "NOT_FOUND";
}

/* dopo 'DELETE' troviamo il client e lo eliminiamo dalla lista */
char * del_client(char * ip, int portaudp) {
Tnodo * L = lista;
Tnodo * LP = NULL;
while (L != NULL) {
if(L->ip != NULL && L->portaudp != 0 && strcmp(L->ip, ip) == 0 && L->portaudp == portaudp) {
/* dopo aver trovato il nodo da eliminare: */
if(L->next != NULL) {
/* se next è diverso da null settiamo L uguale al next, escludendo di fatti il nodo da eliminare */
*L = *L->next;
} else {
/* se next è null abbiamo due possibilità */
if(LP != NULL) {
/* se il precedente del nodo da eliminare è diverso da null il nodo da eliminare è l'ultimo, quindi settiamo il next del penultimo a null */
LP->next = NULL;
} else {
/* se il precedente del nodo da eliminare è null (e il next è null) abbiamo un nodo solo, quindi annulliamo tutta la lista */
lista = NULL;
}
}
return "Utente eliminato.";
}
LP = L;
L = L->next;
}
return "Utente non presente.";
}

/* dopo 'DEBUG' stampo tutta la lista a video senza inviarla ai client
* qui si possono inserire altri comandi per verificare lo stato del serve in esecuzione */
void * debug_lista() {
Tnodo * L = lista;
while (L != NULL) {
printf("ip: %s, porta: %d, nick: %s, tcp: %d\n", L->ip, L->portaudp, L->nick, L->portatcp);
L = L->next;
}
fflush(stdout);
}



int main(void) {
struct sockaddr_in server_socket, client_socket;
int s, i, client_lenght = sizeof(client_socket);
char buf[BUFLEN];
Tnodo tmp;

if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
perror("socket";);
exit(1);
}

memset((void *)&server_socket, 0, sizeof(server_socket));
server_socket.sin_family = AF_INET;
server_socket.sin_port = htons(PORT);
server_socket.sin_addr.s_addr = htonl(INADDR_ANY);

if (bind(s, (struct sockaddr *) &server_socket, sizeof(server_socket))==-1) {
perror("bind";);
exit(1);
}
printf("Server attivo e in ascolto sulla porta %d\n",PORT);

/* il server centrale resta sempre attivo */
while(TRUE) {
/* svuoto tutto il buffer altrimenti succederebbe:
* invio 'ciao' -> leggo 'ciao'
* invio 'come' -> leggo 'come'
* invio 'xx' -> leggo 'xxme'
* invio 'z' -> leggo 'zxme'
* da qui la necessità di svuotare il buffer.
*/
memset(buf,'{parsed_message}',BUFLEN);

/* aspetto di ricevere un messaggio da qualunque client */
if(recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &client_socket, &client_lenght) == -1) {
perror("recvfrom()";);
}

if(strcmp("NAME|", substring(buf, 0, 5)) == 0) {
/* se il client sta inviando il suo nick lo inserisco nella lista, e chiedo la porta tcp */
char * nick = substring(buf, 5, strlen(buf) - 5);
     printf("Un nuovo utente si è connesso:\n";);
printf("IP ADDRESS -> %s\t UDP PORT -> %d \t New nick -> %s\n", inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port), nick);

if(add_client(inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port), nick)) {
if(sendto(s, "PROVIDE_TCP_PORT", BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else {
if(sendto(s, "Nick non accettato.", BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
}
} else if(strcmp("TCP|", substring(buf, 0, 4)) == 0) {
/* se il client sta inviando la sua porta tcp, solitamente chiesta in automatico la inserisco nella lista */
int porta = atoi(substring(buf, 4, strlen(buf) - 4));
printf("TCP PORT ASSIGNED -> %d\n",porta);

set_tcp_port(inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port), porta);
if(sendto(s, " ", 1, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else if(strcmp("LISTA", buf) == 0) {
/* se un client sta chiedendo la lista dei client noti la invio */
if(sendto(s, get_clients(inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port)), BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else if(strcmp("CONNECT|", substring(buf, 0, 8)) == 0) {
/* se un client sta chiedendo i parametri di un client li cerco nella lista e li invio */
char * nick = substring(buf, 8, strlen(buf) - 8);
if(sendto(s, get_client(nick), BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else if(strcmp("DELETE", buf) == 0) {
/* se un client sta chiedendo di essere eliminato lo cancello dalla lista */
if(sendto(s, del_client(inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port)), BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else if(strcmp("DEBUG", buf) == 0) {
/* eseguo il debug */
debug_lista();
if(sendto(s, buf, BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else {
/* stampo a video qualunque comando non riconosciuto, il printf può essere eliminato, il sendto assolutamente no.
* anche dopo un comando errato il client si aspetta una risposta. */
printf("%s:%d -> %s\n", inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port), buf);
if(sendto(s, " ", BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
}
fflush(stdout);
}

close(s);
return 0;
}


CLIENT

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

#define BUFLEN 512
#define PORT 9930
#define SRV_IP "127.0.0.1"

/* ciclo principale del programma = true */
int ciclo = 1;

/*
* parametri di configurazione della socket in ascolto di altri client
* rispettivamente descrittore della socket e porta.
* se la porta è occupata viene incrementata prima di riprovare
* questo è necessario per mettere in ascolto due client entrambi su localhost
*/
int connected_ts = 0;
int ts_port = 9930;

/*
* parametri di configurazione della socket in uscita verso un altro client
* per connettersi ogni client deve prima chiedere al server quali client sono disponibili
* e i loro ip:porta
*/
int connected_tc = 0;

/*
* il nick che inviamo al server viene mantenuto qui
*/
char * nick;

/*
* substring workaround per C
*/
char * substring(const char* str, size_t begin, size_t len) {
if (str == 0 || strlen(str) == 0 || strlen(str) < begin || strlen(str) < (begin+len))
return "";
return strndup(str + begin, len);
}

/*
* come detto ogni client ha una socket aperta in ascolto di altri client
* la socket viene aperta in un thread e resta aperta per tutto il ciclo di vita del client
*/
void * thread_server(void *x) {
int sock, bytes_recieved, i = 1;
char s_buffer[BUFLEN], r_buffer[BUFLEN];

struct sockaddr_in server_addr, client_addr;
int sin_size = sizeof(struct sockaddr_in);

if((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
printf("Socket\n";);
exit(-1);
}

if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(int)) == -1) {
printf("Setsockopt\n";);
exit(-1);
}

memset((void *)&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;

do {
ts_port++;
server_addr.sin_port = htons(ts_port);
i = bind(sock, (struct sockaddr *) & server_addr, sizeof(struct sockaddr));
} while(i == -1 && ts_port <= 10930);

if(i == -1) {
printf("Unable to bind\n";);
exit(-1);
}

if(listen(sock, 5) == -1) {
printf("Listen\n";);
exit(-1);
}

connected_ts = accept(sock, (struct sockaddr *)&client_addr,&sin_size);

printf("I got a connection from (%s , %d)\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

while(1) {
memset(r_buffer,'{parsed_message}',BUFLEN);
bytes_recieved = recv(connected_ts,r_buffer,BUFLEN,0);
if(strcmp(r_buffer , "DISCONNECT";) == 0) {
printf("I got a disconnect request from (%s , %d) ************** Disconnection performed\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
break;
} else {
printf("%s: %s\n" , nick, r_buffer);
}
}
close(sock);
sock = 0;
connected_ts = 0;
}

/*
* la socket verso un altro client viene aperta nel main,
* successivamente viene creato il thread che cicla sulla lettura di questa socket
*/
void * thread_client(void *x) {
int bytes_recieved;
char s_buffer[BUFLEN], r_buffer[BUFLEN];
while(1) {
memset(r_buffer,'{parsed_message}',BUFLEN);
bytes_recieved=recv(connected_tc, r_buffer, BUFLEN,0);
if(strcmp(r_buffer, "DISCONNECT";) == 0) {
printf("Disconnecting from remote host\n";);
break;
} else {
printf("%s: %s\n" , nick, r_buffer);
}
}
close(connected_tc);
connected_tc = 0;
}


int main(void) {
/* i thread client/server */
pthread_t p_thread_server;
pthread_t p_thread_client;

/* i parametri di connessione verso il server centrale */
struct sockaddr_in server_socket;
int s, i, server_lenght = sizeof(server_socket);
char buf[BUFLEN];

/* creo la socket in ascolto di altri client */
if(pthread_create(&p_thread_server, NULL, thread_server, NULL) != 0) {
fprintf(stderr, "errore non posso creare il thread 1\n";);
exit(-1);
}

if((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
printf("socket";);
exit(-1);
}

/* imposto i parametri di connessione al server centrale */
memset((void *)&server_socket, 0, sizeof(server_socket));
server_socket.sin_family = AF_INET;
server_socket.sin_port = htons(PORT);

if(inet_aton(SRV_IP, &server_socket.sin_addr)==0) {
fprintf(stderr, "inet_aton() failed\n";);
exit(-1);
}
printf("****************************************************************************\n";);
printf("****************************************************************************\n";);
printf("****************************************************************************\n";);
printf("**** ****\n";);
printf("**** ****\n";);
printf("**** ********* **** **** ******** ************ **** \n";);
printf("**** ********* **** **** ********** ************ **** \n";);
printf("**** **** ************ **** **** **** ****\n";);
printf("**** **** ************ ************ **** ****\n";);
printf("**** ********* **** **** **** **** **** ****\n";);
printf("**** ********* **** **** **** **** **** ****\n";);
printf("**** ****\n";);
printf("**** ****\n";);
printf("****************************************************************************\n";);
printf("****************************************************************************\n";);
printf("****************************************************************************\n";);
printf("Digita il comando NAME|<nick utente> per inserire il tuo nickname\n";);
printf("Digita il comando exit per uscire\n";);
printf("Digita il comando LISTA per richiedere la lista dei client connessi al server\n";);
printf("Digita il comando CONNECT|<nick utente> per aprire una sessione di chat con <nick utente>\n";);
printf("Digita il comando DISCONNECT per chiudere la sessione di chat\n";);
/* while 1 il programma resta connesso al server centrale */
while(ciclo) {
/*
* svuoto completamente il buffer prima di ogni lettura
* questo serve per evitare di leggere vecchie stringhe concatenate a quella corrente
*/
memset(buf,'{parsed_message}',BUFLEN);

/* leggo da standard in nel buffer */
scanf("%s", buf);

/*
* se siamo connessi a un altro client come client
* sto chattando direttamente con qualcuno,
* invio i messaggi a lui.
*/
if(connected_tc > 0) {
if(send(connected_tc, buf, BUFLEN, 0) == -1) {
printf("send()";);
}
if(strcmp(buf, "DISCONNECT";) == 0) {
close(connected_tc);
connected_tc = 0;
}
} else
/*
* se siamo connessi a un altro client come server
* sto chattando direttamente con qualcuno,
* invio i messaggi a lui.
*/
if(connected_ts > 0) {
if(strcmp(buf, "exit";) == 0) {
if(send(connected_ts, buf, BUFLEN, 0) == -1) {
printf("send()";);
}
close(connected_ts);
connected_ts = 0;
} else {
char res[BUFLEN];
if(send(connected_ts, buf, BUFLEN, 0) == -1) {
printf("send()";);
}
}
} else
/*
* se non siamo connessi direttamente a nessuno client
* invio i messaggi al server centrale.
* in assenza di messaggi validi il server centrale agisce come un echo server.
* i comandi validi sono:
* 'exit' -> chiude il programma
* 'DELETE' -> rimuove il client dalla lista dei client noti contenuta nel server centrale, digitando exit questo comando viene inviato automaticamente
* 'NAME|xxxx' -> inserisce il client nella lista dei client conosciuti contenuta nel server centrale e setta il nick del client a xxxx, per cambiare nick digitare di nuovo il comando
* 'TCP|xx' -> invia al server centrale la porta su cui il client è in ascolto di connessioni dirette, questo comando viene inviato automaticamente dopo il comando NAME
* 'LISTA' -> ottiene dal server centrale la lista dei client conosciuti
* 'CONNECT|xxxx' -> chiede al server centrale ip e porta del client con nick xxxx e esegue la connessione diretta, quindi fa partire il thread di lettura della socket client
* 'DEBUG' -> fa stampare al server informazioni utili non inviate al client
*/
{
if(strcmp(buf, "exit";) == 0) {
/* in caso di uscita setto ciclo a 0 e invio 'DELETE' */
ciclo = 0;
if(sendto(s, "DELETE", 6, 0, (struct sockaddr *) &server_socket, server_lenght) == -1) {
printf("sendto()";);
}
} else if(strcmp("NAME|", substring(buf, 0, 5)) == 0) {
/* salvo il nick nella variabile all'inizio e lo invio al server centrale per essere inserito nella lista */
nick = substring(buf, 5, strlen(buf) - 5);
if(sendto(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, server_lenght) == -1) {
printf("sendto()";);
}
} else {
/* invio al server centrale tutto quello che leggo */
if(sendto(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, server_lenght) == -1) {
printf("sendto()";);
}
}
/*
* dopo l'invio al server centrale di qualcosa aspetto che il server mi risponde, il server agisce come un echo server, sia in caso di comandi validi che di comandi non validi, quindi ci sarà sempre un ritorno */
if(recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, &server_lenght) == -1) {
printf("recvfrom()";);
}
if(strcmp(buf, "PROVIDE_TCP_PORT";) == 0) {
/* 'PROVIDE_TCP_PORT' -> il server vuole conoscere la porta su cui stiamo aspettando i client diretti, questo comando arriva automaticamente dopo l'invio di 'NAME' e rispondiamo quindi automaticamente col comando 'TCP' */
sprintf(buf,"TCP|%d",ts_port);
if(sendto(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, server_lenght) == -1) {
printf("sendto()";);
}
if(recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, &server_lenght) == -1) {
printf("recvfrom()";);
}
} else if(strcmp("CLIENT|", substring(buf, 0, 7)) == 0) {
/* 'CLIENT|xxxx|yy' -> il server ci comunica ip e porta del client con cui abbiamo scelto di connetterci, dopo vari split avvio la socket diretta verso il client che farà da server nella connessione diretta */
char * info = substring(buf, 7, strlen(buf) - 7);
char * _info = strstr(info, "|";);
int pos = _info - info;
char * ip = substring(info, 0, pos);
int porta = atoi(substring(info, pos + 1, strlen(info) - pos - 1));
printf("CONNECTING TO %s PORT %d\t", ip, porta);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("*\t";);
struct sockaddr_in server_addr;

if ((connected_tc = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
printf("Socket";);
break;
}

memset((void *)&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(porta);
server_addr.sin_addr.s_addr = inet_addr(ip);

if(connect(connected_tc, (struct sockaddr *) & server_addr, sizeof(struct sockaddr)) == -1) {
printf("Connect";);
break;
}
/* se tutto è andato bene apro il thread che cicla sulla socket in lettura */
printf("CONNECTED\n";);
if(pthread_create(&p_thread_client, NULL, thread_client, NULL) != 0) {
fprintf(stderr, "errore non posso creare il thread 2\n";);

}
} else {
/* questo viene eseguito quando un comando non valido ritorna dal server, può essere eliminato */
printf("> %s\n", buf);
}
}
fflush(stdout);
}

close(s);
return 0;
}',BUFLEN);
bytes_recieved = recv(connected_ts,r_buffer,BUFLEN,0);
if(strcmp(r_buffer , "DISCONNECT";) == 0) {
printf("I got a disconnect request from (%s , %d) ************** Disconnection performed\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
break;
} else {
printf("%s: %s\n" , nick, r_buffer);
}
}
close(sock);
sock = 0;
connected_ts = 0;
}

/*
* la socket verso un altro client viene aperta nel main,
* successivamente viene creato il thread che cicla sulla lettura di questa socket
*/
void * thread_client(void *x) {
int bytes_recieved;
char s_buffer[BUFLEN], r_buffer[BUFLEN];
while(1) {
memset(r_buffer,'Sto provando a sviluppare una semplice chat in c usando le socket di Berkeley.
Questo sono le richieste di progetto:

Realizzare una chat. Un server contiene una lista dei client che si sono connessi. Il client si connette al server per chiedere una lista degli altri client con cui potersi connettere ma resta anche in attesa di connessioni di altri client (queste connessioni saranno comunicazioni di chat).

Il programma è praticamente pronto ed è stato realizzato in modo che un client apre una connessione UDP con il server, il quale per ogni nuovo utente aggiorna una lista dei client connessi e per ciascuno di questi client prende informazioni sulla porta TCP sulla quale questi restano in attesa di nuove richieste di connessioni da parte di altri client.Una volta connesso al server il client può richiedere la lista degli utenti connessi al server e connettersi con uno di questi aprendo una sessione di chat privata(creando quindi una socket che usa la porta TCP diversa rispetto a quella con cui è connesso al server che invece usa l'UDP) in cui lo scambio di messaggi tra i due utenti avviene senza passare dal server(per cui ciascun client può anche funzionare da server).A questo punto si apre un canale diretto tra i due utenti i quali senza problemi possono procedere allo scambio di messaggi.
Il problema che nasce è invece il seguente:
nel momento in cui il client che ha aperto la sessione di chat privata decide di chiudere la connessione tutto funziona correttamente, viceversa se è il client che ha accettato la richiesta di sessione di chat(cioè se il client sta funzionando da server)a chiudere la connessione tra i due il programma crasha.
AIUTATEMI PER FAVORE NON SO PIÙ CHE FARE PER RISOLVERE IL PROBLEMA!

Ecco il codice.

SERVER

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

/* true non esiste in c, bisogna definirlo */
#define TRUE 1
#define BUFLEN 512
#define PORT 9930

/*
* substring workaround per C
*/
char * substring(const char* str, size_t begin, size_t len) {
if (str == 0 || strlen(str) == 0 || strlen(str) < begin || strlen(str) < (begin+len))
return "";
return strndup(str + begin, len);
}

/* struttura client, contenente:
* ip -> l'ip da dove proviene il client
* portaudp -> la porta di connessione server centrale <-> client
* la coppia ip:portaudp definisce univocamente un client, è necessario in caso di diversi client su localhost
* portatcp -> la porta su cui il client aspetta un altro client per la connessione diretta
* nick -> il nick del client
*/
struct client {
char * ip;
int portaudp;
int portatcp;
char * nick;
struct client *next;
};
typedef struct client Tnodo;

/* la lista in cui manteniamo i client e la sua dimensione */
Tnodo * lista = NULL;
int lista_size = 0;

/* aggiunta di un client alla lista conoscendo ip, porta e nick */
int add_client(char * ip, int portaudp, char * nick) {
Tnodo * L;
if(lista == NULL) {
/* se la lista è vuota creiamo il primo nodo con i dati necessari */
lista = (Tnodo*) malloc (sizeof(Tnodo));
lista->ip = ip;
lista->portaudp = portaudp;
lista->portatcp = 0;
lista->nick = nick;
lista->next = NULL;
lista_size++;
} else {
L = lista;
while(L != NULL) {
/* se la lista non è vuota scandiamo i nodi alla ricerca di quello del client */
if(strcmp(L->ip, ip) == 0 && L->portaudp == portaudp) {
/* se viene trovato cambiamo nick al client senza creare un nuovo nodo */
L->nick = nick;
return 1;
}
L = L->next;
}
L = lista;
/* se non viene trovato scandiamo la lista fino all'ultimo nodo e appendiamo in coda un nuovo nodo per il nuovo client */
while(L->next != NULL) {
L = L->next;
}
L->next = (Tnodo*) malloc (sizeof(Tnodo));
L->next->ip = ip;
L->next->portaudp = portaudp;
L->next->portatcp = 0;
L->next->nick = nick;
L->next->next = NULL;
lista_size++;
}
return 1;
}

/* dopo aver inserito il client nella lista dobbiamo inserire la porta tcp su cui è in ascolto di altri client */
int set_tcp_port(char * ip, int portaudp, int portatcp) {
Tnodo * L = lista;
while(L != NULL) {
if(strcmp(L->ip, ip) == 0 && L->portaudp == portaudp) {
L->portatcp = portatcp;
return 1;
}
L = L->next;
}
return 0;
}

/* dopo il comando 'LISTA' creiamo la lista dei client e la inviamo */
char * get_clients(char * ip, int portaudp) {
Tnodo * L;
char *result = NULL;
int i = 0, len = 0;

/* la prima scansione serve a calcolare lo spazio da allocare alla stringa, per evitare buffer di migliaia di posizioni
* forse inutili o anche insufficienti */
L = lista;
while (L != NULL) {
if(strcmp(L->ip, ip) != 0 || L->portaudp != portaudp) {
len += strlen(L->nick) + 1;
}
L = L->next;
}

/* allochiamo lo spazio con malloc */
result = malloc(len * sizeof(char) + 1);
if(result == NULL) {
fprintf(stderr, "Error - mkconcat -> malloc()\n";);
return NULL;
}

/* con questa seconda scansione inseriamo effettivamente i nick dei client nella lista evitando il nick che ha fatto la richiesta */
L = lista;
while (L != NULL) {
if(strcmp(L->ip, ip) != 0 || L->portaudp != portaudp) {
if(strcat(result, "\n";) == NULL) {
fprintf(stderr, "Error - strcat()\n";);
return NULL;
}
if(strcat(result, L->nick) == NULL) {
fprintf(stderr, "Error - strcat()\n";);
return NULL;
}
}
L = L->next;
}
return result;
}

/* dopo 'CONNECT|nick' recuperiamo ip e porta del client cercato e li inviamo al client che li ha chiesti */
char * get_client(char * nick) {
Tnodo * L = lista;
while(L != NULL) {
if(strcmp(L->nick, nick) == 0) {
sprintf(nick, "CLIENT|%s|%d", L->ip, L->portatcp);
return nick;
}
L = L->next;
}
return "NOT_FOUND";
}

/* dopo 'DELETE' troviamo il client e lo eliminiamo dalla lista */
char * del_client(char * ip, int portaudp) {
Tnodo * L = lista;
Tnodo * LP = NULL;
while (L != NULL) {
if(L->ip != NULL && L->portaudp != 0 && strcmp(L->ip, ip) == 0 && L->portaudp == portaudp) {
/* dopo aver trovato il nodo da eliminare: */
if(L->next != NULL) {
/* se next è diverso da null settiamo L uguale al next, escludendo di fatti il nodo da eliminare */
*L = *L->next;
} else {
/* se next è null abbiamo due possibilità */
if(LP != NULL) {
/* se il precedente del nodo da eliminare è diverso da null il nodo da eliminare è l'ultimo, quindi settiamo il next del penultimo a null */
LP->next = NULL;
} else {
/* se il precedente del nodo da eliminare è null (e il next è null) abbiamo un nodo solo, quindi annulliamo tutta la lista */
lista = NULL;
}
}
return "Utente eliminato.";
}
LP = L;
L = L->next;
}
return "Utente non presente.";
}

/* dopo 'DEBUG' stampo tutta la lista a video senza inviarla ai client
* qui si possono inserire altri comandi per verificare lo stato del serve in esecuzione */
void * debug_lista() {
Tnodo * L = lista;
while (L != NULL) {
printf("ip: %s, porta: %d, nick: %s, tcp: %d\n", L->ip, L->portaudp, L->nick, L->portatcp);
L = L->next;
}
fflush(stdout);
}



int main(void) {
struct sockaddr_in server_socket, client_socket;
int s, i, client_lenght = sizeof(client_socket);
char buf[BUFLEN];
Tnodo tmp;

if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
perror("socket";);
exit(1);
}

memset((void *)&server_socket, 0, sizeof(server_socket));
server_socket.sin_family = AF_INET;
server_socket.sin_port = htons(PORT);
server_socket.sin_addr.s_addr = htonl(INADDR_ANY);

if (bind(s, (struct sockaddr *) &server_socket, sizeof(server_socket))==-1) {
perror("bind";);
exit(1);
}
printf("Server attivo e in ascolto sulla porta %d\n",PORT);

/* il server centrale resta sempre attivo */
while(TRUE) {
/* svuoto tutto il buffer altrimenti succederebbe:
* invio 'ciao' -> leggo 'ciao'
* invio 'come' -> leggo 'come'
* invio 'xx' -> leggo 'xxme'
* invio 'z' -> leggo 'zxme'
* da qui la necessità di svuotare il buffer.
*/
memset(buf,'{parsed_message}',BUFLEN);

/* aspetto di ricevere un messaggio da qualunque client */
if(recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &client_socket, &client_lenght) == -1) {
perror("recvfrom()";);
}

if(strcmp("NAME|", substring(buf, 0, 5)) == 0) {
/* se il client sta inviando il suo nick lo inserisco nella lista, e chiedo la porta tcp */
char * nick = substring(buf, 5, strlen(buf) - 5);
     printf("Un nuovo utente si è connesso:\n";);
printf("IP ADDRESS -> %s\t UDP PORT -> %d \t New nick -> %s\n", inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port), nick);

if(add_client(inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port), nick)) {
if(sendto(s, "PROVIDE_TCP_PORT", BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else {
if(sendto(s, "Nick non accettato.", BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
}
} else if(strcmp("TCP|", substring(buf, 0, 4)) == 0) {
/* se il client sta inviando la sua porta tcp, solitamente chiesta in automatico la inserisco nella lista */
int porta = atoi(substring(buf, 4, strlen(buf) - 4));
printf("TCP PORT ASSIGNED -> %d\n",porta);

set_tcp_port(inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port), porta);
if(sendto(s, " ", 1, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else if(strcmp("LISTA", buf) == 0) {
/* se un client sta chiedendo la lista dei client noti la invio */
if(sendto(s, get_clients(inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port)), BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else if(strcmp("CONNECT|", substring(buf, 0, 8)) == 0) {
/* se un client sta chiedendo i parametri di un client li cerco nella lista e li invio */
char * nick = substring(buf, 8, strlen(buf) - 8);
if(sendto(s, get_client(nick), BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else if(strcmp("DELETE", buf) == 0) {
/* se un client sta chiedendo di essere eliminato lo cancello dalla lista */
if(sendto(s, del_client(inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port)), BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else if(strcmp("DEBUG", buf) == 0) {
/* eseguo il debug */
debug_lista();
if(sendto(s, buf, BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else {
/* stampo a video qualunque comando non riconosciuto, il printf può essere eliminato, il sendto assolutamente no.
* anche dopo un comando errato il client si aspetta una risposta. */
printf("%s:%d -> %s\n", inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port), buf);
if(sendto(s, " ", BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
}
fflush(stdout);
}

close(s);
return 0;
}


CLIENT

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

#define BUFLEN 512
#define PORT 9930
#define SRV_IP "127.0.0.1"

/* ciclo principale del programma = true */
int ciclo = 1;

/*
* parametri di configurazione della socket in ascolto di altri client
* rispettivamente descrittore della socket e porta.
* se la porta è occupata viene incrementata prima di riprovare
* questo è necessario per mettere in ascolto due client entrambi su localhost
*/
int connected_ts = 0;
int ts_port = 9930;

/*
* parametri di configurazione della socket in uscita verso un altro client
* per connettersi ogni client deve prima chiedere al server quali client sono disponibili
* e i loro ip:porta
*/
int connected_tc = 0;

/*
* il nick che inviamo al server viene mantenuto qui
*/
char * nick;

/*
* substring workaround per C
*/
char * substring(const char* str, size_t begin, size_t len) {
if (str == 0 || strlen(str) == 0 || strlen(str) < begin || strlen(str) < (begin+len))
return "";
return strndup(str + begin, len);
}

/*
* come detto ogni client ha una socket aperta in ascolto di altri client
* la socket viene aperta in un thread e resta aperta per tutto il ciclo di vita del client
*/
void * thread_server(void *x) {
int sock, bytes_recieved, i = 1;
char s_buffer[BUFLEN], r_buffer[BUFLEN];

struct sockaddr_in server_addr, client_addr;
int sin_size = sizeof(struct sockaddr_in);

if((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
printf("Socket\n";);
exit(-1);
}

if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(int)) == -1) {
printf("Setsockopt\n";);
exit(-1);
}

memset((void *)&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;

do {
ts_port++;
server_addr.sin_port = htons(ts_port);
i = bind(sock, (struct sockaddr *) & server_addr, sizeof(struct sockaddr));
} while(i == -1 && ts_port <= 10930);

if(i == -1) {
printf("Unable to bind\n";);
exit(-1);
}

if(listen(sock, 5) == -1) {
printf("Listen\n";);
exit(-1);
}

connected_ts = accept(sock, (struct sockaddr *)&client_addr,&sin_size);

printf("I got a connection from (%s , %d)\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

while(1) {
memset(r_buffer,'{parsed_message}',BUFLEN);
bytes_recieved = recv(connected_ts,r_buffer,BUFLEN,0);
if(strcmp(r_buffer , "DISCONNECT";) == 0) {
printf("I got a disconnect request from (%s , %d) ************** Disconnection performed\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
break;
} else {
printf("%s: %s\n" , nick, r_buffer);
}
}
close(sock);
sock = 0;
connected_ts = 0;
}

/*
* la socket verso un altro client viene aperta nel main,
* successivamente viene creato il thread che cicla sulla lettura di questa socket
*/
void * thread_client(void *x) {
int bytes_recieved;
char s_buffer[BUFLEN], r_buffer[BUFLEN];
while(1) {
memset(r_buffer,'{parsed_message}',BUFLEN);
bytes_recieved=recv(connected_tc, r_buffer, BUFLEN,0);
if(strcmp(r_buffer, "DISCONNECT";) == 0) {
printf("Disconnecting from remote host\n";);
break;
} else {
printf("%s: %s\n" , nick, r_buffer);
}
}
close(connected_tc);
connected_tc = 0;
}


int main(void) {
/* i thread client/server */
pthread_t p_thread_server;
pthread_t p_thread_client;

/* i parametri di connessione verso il server centrale */
struct sockaddr_in server_socket;
int s, i, server_lenght = sizeof(server_socket);
char buf[BUFLEN];

/* creo la socket in ascolto di altri client */
if(pthread_create(&p_thread_server, NULL, thread_server, NULL) != 0) {
fprintf(stderr, "errore non posso creare il thread 1\n";);
exit(-1);
}

if((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
printf("socket";);
exit(-1);
}

/* imposto i parametri di connessione al server centrale */
memset((void *)&server_socket, 0, sizeof(server_socket));
server_socket.sin_family = AF_INET;
server_socket.sin_port = htons(PORT);

if(inet_aton(SRV_IP, &server_socket.sin_addr)==0) {
fprintf(stderr, "inet_aton() failed\n";);
exit(-1);
}
printf("****************************************************************************\n";);
printf("****************************************************************************\n";);
printf("****************************************************************************\n";);
printf("**** ****\n";);
printf("**** ****\n";);
printf("**** ********* **** **** ******** ************ **** \n";);
printf("**** ********* **** **** ********** ************ **** \n";);
printf("**** **** ************ **** **** **** ****\n";);
printf("**** **** ************ ************ **** ****\n";);
printf("**** ********* **** **** **** **** **** ****\n";);
printf("**** ********* **** **** **** **** **** ****\n";);
printf("**** ****\n";);
printf("**** ****\n";);
printf("****************************************************************************\n";);
printf("****************************************************************************\n";);
printf("****************************************************************************\n";);
printf("Digita il comando NAME|<nick utente> per inserire il tuo nickname\n";);
printf("Digita il comando exit per uscire\n";);
printf("Digita il comando LISTA per richiedere la lista dei client connessi al server\n";);
printf("Digita il comando CONNECT|<nick utente> per aprire una sessione di chat con <nick utente>\n";);
printf("Digita il comando DISCONNECT per chiudere la sessione di chat\n";);
/* while 1 il programma resta connesso al server centrale */
while(ciclo) {
/*
* svuoto completamente il buffer prima di ogni lettura
* questo serve per evitare di leggere vecchie stringhe concatenate a quella corrente
*/
memset(buf,'{parsed_message}',BUFLEN);

/* leggo da standard in nel buffer */
scanf("%s", buf);

/*
* se siamo connessi a un altro client come client
* sto chattando direttamente con qualcuno,
* invio i messaggi a lui.
*/
if(connected_tc > 0) {
if(send(connected_tc, buf, BUFLEN, 0) == -1) {
printf("send()";);
}
if(strcmp(buf, "DISCONNECT";) == 0) {
close(connected_tc);
connected_tc = 0;
}
} else
/*
* se siamo connessi a un altro client come server
* sto chattando direttamente con qualcuno,
* invio i messaggi a lui.
*/
if(connected_ts > 0) {
if(strcmp(buf, "exit";) == 0) {
if(send(connected_ts, buf, BUFLEN, 0) == -1) {
printf("send()";);
}
close(connected_ts);
connected_ts = 0;
} else {
char res[BUFLEN];
if(send(connected_ts, buf, BUFLEN, 0) == -1) {
printf("send()";);
}
}
} else
/*
* se non siamo connessi direttamente a nessuno client
* invio i messaggi al server centrale.
* in assenza di messaggi validi il server centrale agisce come un echo server.
* i comandi validi sono:
* 'exit' -> chiude il programma
* 'DELETE' -> rimuove il client dalla lista dei client noti contenuta nel server centrale, digitando exit questo comando viene inviato automaticamente
* 'NAME|xxxx' -> inserisce il client nella lista dei client conosciuti contenuta nel server centrale e setta il nick del client a xxxx, per cambiare nick digitare di nuovo il comando
* 'TCP|xx' -> invia al server centrale la porta su cui il client è in ascolto di connessioni dirette, questo comando viene inviato automaticamente dopo il comando NAME
* 'LISTA' -> ottiene dal server centrale la lista dei client conosciuti
* 'CONNECT|xxxx' -> chiede al server centrale ip e porta del client con nick xxxx e esegue la connessione diretta, quindi fa partire il thread di lettura della socket client
* 'DEBUG' -> fa stampare al server informazioni utili non inviate al client
*/
{
if(strcmp(buf, "exit";) == 0) {
/* in caso di uscita setto ciclo a 0 e invio 'DELETE' */
ciclo = 0;
if(sendto(s, "DELETE", 6, 0, (struct sockaddr *) &server_socket, server_lenght) == -1) {
printf("sendto()";);
}
} else if(strcmp("NAME|", substring(buf, 0, 5)) == 0) {
/* salvo il nick nella variabile all'inizio e lo invio al server centrale per essere inserito nella lista */
nick = substring(buf, 5, strlen(buf) - 5);
if(sendto(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, server_lenght) == -1) {
printf("sendto()";);
}
} else {
/* invio al server centrale tutto quello che leggo */
if(sendto(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, server_lenght) == -1) {
printf("sendto()";);
}
}
/*
* dopo l'invio al server centrale di qualcosa aspetto che il server mi risponde, il server agisce come un echo server, sia in caso di comandi validi che di comandi non validi, quindi ci sarà sempre un ritorno */
if(recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, &server_lenght) == -1) {
printf("recvfrom()";);
}
if(strcmp(buf, "PROVIDE_TCP_PORT";) == 0) {
/* 'PROVIDE_TCP_PORT' -> il server vuole conoscere la porta su cui stiamo aspettando i client diretti, questo comando arriva automaticamente dopo l'invio di 'NAME' e rispondiamo quindi automaticamente col comando 'TCP' */
sprintf(buf,"TCP|%d",ts_port);
if(sendto(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, server_lenght) == -1) {
printf("sendto()";);
}
if(recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, &server_lenght) == -1) {
printf("recvfrom()";);
}
} else if(strcmp("CLIENT|", substring(buf, 0, 7)) == 0) {
/* 'CLIENT|xxxx|yy' -> il server ci comunica ip e porta del client con cui abbiamo scelto di connetterci, dopo vari split avvio la socket diretta verso il client che farà da server nella connessione diretta */
char * info = substring(buf, 7, strlen(buf) - 7);
char * _info = strstr(info, "|";);
int pos = _info - info;
char * ip = substring(info, 0, pos);
int porta = atoi(substring(info, pos + 1, strlen(info) - pos - 1));
printf("CONNECTING TO %s PORT %d\t", ip, porta);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("*\t";);
struct sockaddr_in server_addr;

if ((connected_tc = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
printf("Socket";);
break;
}

memset((void *)&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(porta);
server_addr.sin_addr.s_addr = inet_addr(ip);

if(connect(connected_tc, (struct sockaddr *) & server_addr, sizeof(struct sockaddr)) == -1) {
printf("Connect";);
break;
}
/* se tutto è andato bene apro il thread che cicla sulla socket in lettura */
printf("CONNECTED\n";);
if(pthread_create(&p_thread_client, NULL, thread_client, NULL) != 0) {
fprintf(stderr, "errore non posso creare il thread 2\n";);

}
} else {
/* questo viene eseguito quando un comando non valido ritorna dal server, può essere eliminato */
printf("> %s\n", buf);
}
}
fflush(stdout);
}

close(s);
return 0;
}',BUFLEN);
bytes_recieved=recv(connected_tc, r_buffer, BUFLEN,0);
if(strcmp(r_buffer, "DISCONNECT";) == 0) {
printf("Disconnecting from remote host\n";);
break;
} else {
printf("%s: %s\n" , nick, r_buffer);
}
}
close(connected_tc);
connected_tc = 0;
}


int main(void) {
/* i thread client/server */
pthread_t p_thread_server;
pthread_t p_thread_client;

/* i parametri di connessione verso il server centrale */
struct sockaddr_in server_socket;
int s, i, server_lenght = sizeof(server_socket);
char buf[BUFLEN];

/* creo la socket in ascolto di altri client */
if(pthread_create(&p_thread_server, NULL, thread_server, NULL) != 0) {
fprintf(stderr, "errore non posso creare il thread 1\n";);
exit(-1);
}

if((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
printf("socket";);
exit(-1);
}

/* imposto i parametri di connessione al server centrale */
memset((void *)&server_socket, 0, sizeof(server_socket));
server_socket.sin_family = AF_INET;
server_socket.sin_port = htons(PORT);

if(inet_aton(SRV_IP, &server_socket.sin_addr)==0) {
fprintf(stderr, "inet_aton() failed\n";);
exit(-1);
}
printf("****************************************************************************\n";);
printf("****************************************************************************\n";);
printf("****************************************************************************\n";);
printf("**** ****\n";);
printf("**** ****\n";);
printf("**** ********* **** **** ******** ************ **** \n";);
printf("**** ********* **** **** ********** ************ **** \n";);
printf("**** **** ************ **** **** **** ****\n";);
printf("**** **** ************ ************ **** ****\n";);
printf("**** ********* **** **** **** **** **** ****\n";);
printf("**** ********* **** **** **** **** **** ****\n";);
printf("**** ****\n";);
printf("**** ****\n";);
printf("****************************************************************************\n";);
printf("****************************************************************************\n";);
printf("****************************************************************************\n";);
printf("Digita il comando NAME|<nick utente> per inserire il tuo nickname\n";);
printf("Digita il comando exit per uscire\n";);
printf("Digita il comando LISTA per richiedere la lista dei client connessi al server\n";);
printf("Digita il comando CONNECT|<nick utente> per aprire una sessione di chat con <nick utente>\n";);
printf("Digita il comando DISCONNECT per chiudere la sessione di chat\n";);
/* while 1 il programma resta connesso al server centrale */
while(ciclo) {
/*
* svuoto completamente il buffer prima di ogni lettura
* questo serve per evitare di leggere vecchie stringhe concatenate a quella corrente
*/
memset(buf,'Sto provando a sviluppare una semplice chat in c usando le socket di Berkeley.
Questo sono le richieste di progetto:

Realizzare una chat. Un server contiene una lista dei client che si sono connessi. Il client si connette al server per chiedere una lista degli altri client con cui potersi connettere ma resta anche in attesa di connessioni di altri client (queste connessioni saranno comunicazioni di chat).

Il programma è praticamente pronto ed è stato realizzato in modo che un client apre una connessione UDP con il server, il quale per ogni nuovo utente aggiorna una lista dei client connessi e per ciascuno di questi client prende informazioni sulla porta TCP sulla quale questi restano in attesa di nuove richieste di connessioni da parte di altri client.Una volta connesso al server il client può richiedere la lista degli utenti connessi al server e connettersi con uno di questi aprendo una sessione di chat privata(creando quindi una socket che usa la porta TCP diversa rispetto a quella con cui è connesso al server che invece usa l'UDP) in cui lo scambio di messaggi tra i due utenti avviene senza passare dal server(per cui ciascun client può anche funzionare da server).A questo punto si apre un canale diretto tra i due utenti i quali senza problemi possono procedere allo scambio di messaggi.
Il problema che nasce è invece il seguente:
nel momento in cui il client che ha aperto la sessione di chat privata decide di chiudere la connessione tutto funziona correttamente, viceversa se è il client che ha accettato la richiesta di sessione di chat(cioè se il client sta funzionando da server)a chiudere la connessione tra i due il programma crasha.
AIUTATEMI PER FAVORE NON SO PIÙ CHE FARE PER RISOLVERE IL PROBLEMA!

Ecco il codice.

SERVER

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

/* true non esiste in c, bisogna definirlo */
#define TRUE 1
#define BUFLEN 512
#define PORT 9930

/*
* substring workaround per C
*/
char * substring(const char* str, size_t begin, size_t len) {
if (str == 0 || strlen(str) == 0 || strlen(str) < begin || strlen(str) < (begin+len))
return "";
return strndup(str + begin, len);
}

/* struttura client, contenente:
* ip -> l'ip da dove proviene il client
* portaudp -> la porta di connessione server centrale <-> client
* la coppia ip:portaudp definisce univocamente un client, è necessario in caso di diversi client su localhost
* portatcp -> la porta su cui il client aspetta un altro client per la connessione diretta
* nick -> il nick del client
*/
struct client {
char * ip;
int portaudp;
int portatcp;
char * nick;
struct client *next;
};
typedef struct client Tnodo;

/* la lista in cui manteniamo i client e la sua dimensione */
Tnodo * lista = NULL;
int lista_size = 0;

/* aggiunta di un client alla lista conoscendo ip, porta e nick */
int add_client(char * ip, int portaudp, char * nick) {
Tnodo * L;
if(lista == NULL) {
/* se la lista è vuota creiamo il primo nodo con i dati necessari */
lista = (Tnodo*) malloc (sizeof(Tnodo));
lista->ip = ip;
lista->portaudp = portaudp;
lista->portatcp = 0;
lista->nick = nick;
lista->next = NULL;
lista_size++;
} else {
L = lista;
while(L != NULL) {
/* se la lista non è vuota scandiamo i nodi alla ricerca di quello del client */
if(strcmp(L->ip, ip) == 0 && L->portaudp == portaudp) {
/* se viene trovato cambiamo nick al client senza creare un nuovo nodo */
L->nick = nick;
return 1;
}
L = L->next;
}
L = lista;
/* se non viene trovato scandiamo la lista fino all'ultimo nodo e appendiamo in coda un nuovo nodo per il nuovo client */
while(L->next != NULL) {
L = L->next;
}
L->next = (Tnodo*) malloc (sizeof(Tnodo));
L->next->ip = ip;
L->next->portaudp = portaudp;
L->next->portatcp = 0;
L->next->nick = nick;
L->next->next = NULL;
lista_size++;
}
return 1;
}

/* dopo aver inserito il client nella lista dobbiamo inserire la porta tcp su cui è in ascolto di altri client */
int set_tcp_port(char * ip, int portaudp, int portatcp) {
Tnodo * L = lista;
while(L != NULL) {
if(strcmp(L->ip, ip) == 0 && L->portaudp == portaudp) {
L->portatcp = portatcp;
return 1;
}
L = L->next;
}
return 0;
}

/* dopo il comando 'LISTA' creiamo la lista dei client e la inviamo */
char * get_clients(char * ip, int portaudp) {
Tnodo * L;
char *result = NULL;
int i = 0, len = 0;

/* la prima scansione serve a calcolare lo spazio da allocare alla stringa, per evitare buffer di migliaia di posizioni
* forse inutili o anche insufficienti */
L = lista;
while (L != NULL) {
if(strcmp(L->ip, ip) != 0 || L->portaudp != portaudp) {
len += strlen(L->nick) + 1;
}
L = L->next;
}

/* allochiamo lo spazio con malloc */
result = malloc(len * sizeof(char) + 1);
if(result == NULL) {
fprintf(stderr, "Error - mkconcat -> malloc()\n";);
return NULL;
}

/* con questa seconda scansione inseriamo effettivamente i nick dei client nella lista evitando il nick che ha fatto la richiesta */
L = lista;
while (L != NULL) {
if(strcmp(L->ip, ip) != 0 || L->portaudp != portaudp) {
if(strcat(result, "\n";) == NULL) {
fprintf(stderr, "Error - strcat()\n";);
return NULL;
}
if(strcat(result, L->nick) == NULL) {
fprintf(stderr, "Error - strcat()\n";);
return NULL;
}
}
L = L->next;
}
return result;
}

/* dopo 'CONNECT|nick' recuperiamo ip e porta del client cercato e li inviamo al client che li ha chiesti */
char * get_client(char * nick) {
Tnodo * L = lista;
while(L != NULL) {
if(strcmp(L->nick, nick) == 0) {
sprintf(nick, "CLIENT|%s|%d", L->ip, L->portatcp);
return nick;
}
L = L->next;
}
return "NOT_FOUND";
}

/* dopo 'DELETE' troviamo il client e lo eliminiamo dalla lista */
char * del_client(char * ip, int portaudp) {
Tnodo * L = lista;
Tnodo * LP = NULL;
while (L != NULL) {
if(L->ip != NULL && L->portaudp != 0 && strcmp(L->ip, ip) == 0 && L->portaudp == portaudp) {
/* dopo aver trovato il nodo da eliminare: */
if(L->next != NULL) {
/* se next è diverso da null settiamo L uguale al next, escludendo di fatti il nodo da eliminare */
*L = *L->next;
} else {
/* se next è null abbiamo due possibilità */
if(LP != NULL) {
/* se il precedente del nodo da eliminare è diverso da null il nodo da eliminare è l'ultimo, quindi settiamo il next del penultimo a null */
LP->next = NULL;
} else {
/* se il precedente del nodo da eliminare è null (e il next è null) abbiamo un nodo solo, quindi annulliamo tutta la lista */
lista = NULL;
}
}
return "Utente eliminato.";
}
LP = L;
L = L->next;
}
return "Utente non presente.";
}

/* dopo 'DEBUG' stampo tutta la lista a video senza inviarla ai client
* qui si possono inserire altri comandi per verificare lo stato del serve in esecuzione */
void * debug_lista() {
Tnodo * L = lista;
while (L != NULL) {
printf("ip: %s, porta: %d, nick: %s, tcp: %d\n", L->ip, L->portaudp, L->nick, L->portatcp);
L = L->next;
}
fflush(stdout);
}



int main(void) {
struct sockaddr_in server_socket, client_socket;
int s, i, client_lenght = sizeof(client_socket);
char buf[BUFLEN];
Tnodo tmp;

if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
perror("socket";);
exit(1);
}

memset((void *)&server_socket, 0, sizeof(server_socket));
server_socket.sin_family = AF_INET;
server_socket.sin_port = htons(PORT);
server_socket.sin_addr.s_addr = htonl(INADDR_ANY);

if (bind(s, (struct sockaddr *) &server_socket, sizeof(server_socket))==-1) {
perror("bind";);
exit(1);
}
printf("Server attivo e in ascolto sulla porta %d\n",PORT);

/* il server centrale resta sempre attivo */
while(TRUE) {
/* svuoto tutto il buffer altrimenti succederebbe:
* invio 'ciao' -> leggo 'ciao'
* invio 'come' -> leggo 'come'
* invio 'xx' -> leggo 'xxme'
* invio 'z' -> leggo 'zxme'
* da qui la necessità di svuotare il buffer.
*/
memset(buf,'{parsed_message}',BUFLEN);

/* aspetto di ricevere un messaggio da qualunque client */
if(recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &client_socket, &client_lenght) == -1) {
perror("recvfrom()";);
}

if(strcmp("NAME|", substring(buf, 0, 5)) == 0) {
/* se il client sta inviando il suo nick lo inserisco nella lista, e chiedo la porta tcp */
char * nick = substring(buf, 5, strlen(buf) - 5);
     printf("Un nuovo utente si è connesso:\n";);
printf("IP ADDRESS -> %s\t UDP PORT -> %d \t New nick -> %s\n", inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port), nick);

if(add_client(inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port), nick)) {
if(sendto(s, "PROVIDE_TCP_PORT", BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else {
if(sendto(s, "Nick non accettato.", BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
}
} else if(strcmp("TCP|", substring(buf, 0, 4)) == 0) {
/* se il client sta inviando la sua porta tcp, solitamente chiesta in automatico la inserisco nella lista */
int porta = atoi(substring(buf, 4, strlen(buf) - 4));
printf("TCP PORT ASSIGNED -> %d\n",porta);

set_tcp_port(inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port), porta);
if(sendto(s, " ", 1, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else if(strcmp("LISTA", buf) == 0) {
/* se un client sta chiedendo la lista dei client noti la invio */
if(sendto(s, get_clients(inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port)), BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else if(strcmp("CONNECT|", substring(buf, 0, 8)) == 0) {
/* se un client sta chiedendo i parametri di un client li cerco nella lista e li invio */
char * nick = substring(buf, 8, strlen(buf) - 8);
if(sendto(s, get_client(nick), BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else if(strcmp("DELETE", buf) == 0) {
/* se un client sta chiedendo di essere eliminato lo cancello dalla lista */
if(sendto(s, del_client(inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port)), BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else if(strcmp("DEBUG", buf) == 0) {
/* eseguo il debug */
debug_lista();
if(sendto(s, buf, BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
} else {
/* stampo a video qualunque comando non riconosciuto, il printf può essere eliminato, il sendto assolutamente no.
* anche dopo un comando errato il client si aspetta una risposta. */
printf("%s:%d -> %s\n", inet_ntoa(client_socket.sin_addr), ntohs(client_socket.sin_port), buf);
if(sendto(s, " ", BUFLEN, 0, (struct sockaddr *) &client_socket, client_lenght) == -1) {
perror("sendto()";);
}
}
fflush(stdout);
}

close(s);
return 0;
}


CLIENT

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

#define BUFLEN 512
#define PORT 9930
#define SRV_IP "127.0.0.1"

/* ciclo principale del programma = true */
int ciclo = 1;

/*
* parametri di configurazione della socket in ascolto di altri client
* rispettivamente descrittore della socket e porta.
* se la porta è occupata viene incrementata prima di riprovare
* questo è necessario per mettere in ascolto due client entrambi su localhost
*/
int connected_ts = 0;
int ts_port = 9930;

/*
* parametri di configurazione della socket in uscita verso un altro client
* per connettersi ogni client deve prima chiedere al server quali client sono disponibili
* e i loro ip:porta
*/
int connected_tc = 0;

/*
* il nick che inviamo al server viene mantenuto qui
*/
char * nick;

/*
* substring workaround per C
*/
char * substring(const char* str, size_t begin, size_t len) {
if (str == 0 || strlen(str) == 0 || strlen(str) < begin || strlen(str) < (begin+len))
return "";
return strndup(str + begin, len);
}

/*
* come detto ogni client ha una socket aperta in ascolto di altri client
* la socket viene aperta in un thread e resta aperta per tutto il ciclo di vita del client
*/
void * thread_server(void *x) {
int sock, bytes_recieved, i = 1;
char s_buffer[BUFLEN], r_buffer[BUFLEN];

struct sockaddr_in server_addr, client_addr;
int sin_size = sizeof(struct sockaddr_in);

if((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
printf("Socket\n";);
exit(-1);
}

if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(int)) == -1) {
printf("Setsockopt\n";);
exit(-1);
}

memset((void *)&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;

do {
ts_port++;
server_addr.sin_port = htons(ts_port);
i = bind(sock, (struct sockaddr *) & server_addr, sizeof(struct sockaddr));
} while(i == -1 && ts_port <= 10930);

if(i == -1) {
printf("Unable to bind\n";);
exit(-1);
}

if(listen(sock, 5) == -1) {
printf("Listen\n";);
exit(-1);
}

connected_ts = accept(sock, (struct sockaddr *)&client_addr,&sin_size);

printf("I got a connection from (%s , %d)\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

while(1) {
memset(r_buffer,'{parsed_message}',BUFLEN);
bytes_recieved = recv(connected_ts,r_buffer,BUFLEN,0);
if(strcmp(r_buffer , "DISCONNECT";) == 0) {
printf("I got a disconnect request from (%s , %d) ************** Disconnection performed\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
break;
} else {
printf("%s: %s\n" , nick, r_buffer);
}
}
close(sock);
sock = 0;
connected_ts = 0;
}

/*
* la socket verso un altro client viene aperta nel main,
* successivamente viene creato il thread che cicla sulla lettura di questa socket
*/
void * thread_client(void *x) {
int bytes_recieved;
char s_buffer[BUFLEN], r_buffer[BUFLEN];
while(1) {
memset(r_buffer,'{parsed_message}',BUFLEN);
bytes_recieved=recv(connected_tc, r_buffer, BUFLEN,0);
if(strcmp(r_buffer, "DISCONNECT";) == 0) {
printf("Disconnecting from remote host\n";);
break;
} else {
printf("%s: %s\n" , nick, r_buffer);
}
}
close(connected_tc);
connected_tc = 0;
}


int main(void) {
/* i thread client/server */
pthread_t p_thread_server;
pthread_t p_thread_client;

/* i parametri di connessione verso il server centrale */
struct sockaddr_in server_socket;
int s, i, server_lenght = sizeof(server_socket);
char buf[BUFLEN];

/* creo la socket in ascolto di altri client */
if(pthread_create(&p_thread_server, NULL, thread_server, NULL) != 0) {
fprintf(stderr, "errore non posso creare il thread 1\n";);
exit(-1);
}

if((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
printf("socket";);
exit(-1);
}

/* imposto i parametri di connessione al server centrale */
memset((void *)&server_socket, 0, sizeof(server_socket));
server_socket.sin_family = AF_INET;
server_socket.sin_port = htons(PORT);

if(inet_aton(SRV_IP, &server_socket.sin_addr)==0) {
fprintf(stderr, "inet_aton() failed\n";);
exit(-1);
}
printf("****************************************************************************\n";);
printf("****************************************************************************\n";);
printf("****************************************************************************\n";);
printf("**** ****\n";);
printf("**** ****\n";);
printf("**** ********* **** **** ******** ************ **** \n";);
printf("**** ********* **** **** ********** ************ **** \n";);
printf("**** **** ************ **** **** **** ****\n";);
printf("**** **** ************ ************ **** ****\n";);
printf("**** ********* **** **** **** **** **** ****\n";);
printf("**** ********* **** **** **** **** **** ****\n";);
printf("**** ****\n";);
printf("**** ****\n";);
printf("****************************************************************************\n";);
printf("****************************************************************************\n";);
printf("****************************************************************************\n";);
printf("Digita il comando NAME|<nick utente> per inserire il tuo nickname\n";);
printf("Digita il comando exit per uscire\n";);
printf("Digita il comando LISTA per richiedere la lista dei client connessi al server\n";);
printf("Digita il comando CONNECT|<nick utente> per aprire una sessione di chat con <nick utente>\n";);
printf("Digita il comando DISCONNECT per chiudere la sessione di chat\n";);
/* while 1 il programma resta connesso al server centrale */
while(ciclo) {
/*
* svuoto completamente il buffer prima di ogni lettura
* questo serve per evitare di leggere vecchie stringhe concatenate a quella corrente
*/
memset(buf,'{parsed_message}',BUFLEN);

/* leggo da standard in nel buffer */
scanf("%s", buf);

/*
* se siamo connessi a un altro client come client
* sto chattando direttamente con qualcuno,
* invio i messaggi a lui.
*/
if(connected_tc > 0) {
if(send(connected_tc, buf, BUFLEN, 0) == -1) {
printf("send()";);
}
if(strcmp(buf, "DISCONNECT";) == 0) {
close(connected_tc);
connected_tc = 0;
}
} else
/*
* se siamo connessi a un altro client come server
* sto chattando direttamente con qualcuno,
* invio i messaggi a lui.
*/
if(connected_ts > 0) {
if(strcmp(buf, "exit";) == 0) {
if(send(connected_ts, buf, BUFLEN, 0) == -1) {
printf("send()";);
}
close(connected_ts);
connected_ts = 0;
} else {
char res[BUFLEN];
if(send(connected_ts, buf, BUFLEN, 0) == -1) {
printf("send()";);
}
}
} else
/*
* se non siamo connessi direttamente a nessuno client
* invio i messaggi al server centrale.
* in assenza di messaggi validi il server centrale agisce come un echo server.
* i comandi validi sono:
* 'exit' -> chiude il programma
* 'DELETE' -> rimuove il client dalla lista dei client noti contenuta nel server centrale, digitando exit questo comando viene inviato automaticamente
* 'NAME|xxxx' -> inserisce il client nella lista dei client conosciuti contenuta nel server centrale e setta il nick del client a xxxx, per cambiare nick digitare di nuovo il comando
* 'TCP|xx' -> invia al server centrale la porta su cui il client è in ascolto di connessioni dirette, questo comando viene inviato automaticamente dopo il comando NAME
* 'LISTA' -> ottiene dal server centrale la lista dei client conosciuti
* 'CONNECT|xxxx' -> chiede al server centrale ip e porta del client con nick xxxx e esegue la connessione diretta, quindi fa partire il thread di lettura della socket client
* 'DEBUG' -> fa stampare al server informazioni utili non inviate al client
*/
{
if(strcmp(buf, "exit";) == 0) {
/* in caso di uscita setto ciclo a 0 e invio 'DELETE' */
ciclo = 0;
if(sendto(s, "DELETE", 6, 0, (struct sockaddr *) &server_socket, server_lenght) == -1) {
printf("sendto()";);
}
} else if(strcmp("NAME|", substring(buf, 0, 5)) == 0) {
/* salvo il nick nella variabile all'inizio e lo invio al server centrale per essere inserito nella lista */
nick = substring(buf, 5, strlen(buf) - 5);
if(sendto(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, server_lenght) == -1) {
printf("sendto()";);
}
} else {
/* invio al server centrale tutto quello che leggo */
if(sendto(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, server_lenght) == -1) {
printf("sendto()";);
}
}
/*
* dopo l'invio al server centrale di qualcosa aspetto che il server mi risponde, il server agisce come un echo server, sia in caso di comandi validi che di comandi non validi, quindi ci sarà sempre un ritorno */
if(recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, &server_lenght) == -1) {
printf("recvfrom()";);
}
if(strcmp(buf, "PROVIDE_TCP_PORT";) == 0) {
/* 'PROVIDE_TCP_PORT' -> il server vuole conoscere la porta su cui stiamo aspettando i client diretti, questo comando arriva automaticamente dopo l'invio di 'NAME' e rispondiamo quindi automaticamente col comando 'TCP' */
sprintf(buf,"TCP|%d",ts_port);
if(sendto(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, server_lenght) == -1) {
printf("sendto()";);
}
if(recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, &server_lenght) == -1) {
printf("recvfrom()";);
}
} else if(strcmp("CLIENT|", substring(buf, 0, 7)) == 0) {
/* 'CLIENT|xxxx|yy' -> il server ci comunica ip e porta del client con cui abbiamo scelto di connetterci, dopo vari split avvio la socket diretta verso il client che farà da server nella connessione diretta */
char * info = substring(buf, 7, strlen(buf) - 7);
char * _info = strstr(info, "|";);
int pos = _info - info;
char * ip = substring(info, 0, pos);
int porta = atoi(substring(info, pos + 1, strlen(info) - pos - 1));
printf("CONNECTING TO %s PORT %d\t", ip, porta);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("*\t";);
struct sockaddr_in server_addr;

if ((connected_tc = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
printf("Socket";);
break;
}

memset((void *)&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(porta);
server_addr.sin_addr.s_addr = inet_addr(ip);

if(connect(connected_tc, (struct sockaddr *) & server_addr, sizeof(struct sockaddr)) == -1) {
printf("Connect";);
break;
}
/* se tutto è andato bene apro il thread che cicla sulla socket in lettura */
printf("CONNECTED\n";);
if(pthread_create(&p_thread_client, NULL, thread_client, NULL) != 0) {
fprintf(stderr, "errore non posso creare il thread 2\n";);

}
} else {
/* questo viene eseguito quando un comando non valido ritorna dal server, può essere eliminato */
printf("> %s\n", buf);
}
}
fflush(stdout);
}

close(s);
return 0;
}',BUFLEN);

/* leggo da standard in nel buffer */
scanf("%s", buf);

/*
* se siamo connessi a un altro client come client
* sto chattando direttamente con qualcuno,
* invio i messaggi a lui.
*/
if(connected_tc > 0) {
if(send(connected_tc, buf, BUFLEN, 0) == -1) {
printf("send()";);
}
if(strcmp(buf, "DISCONNECT";) == 0) {
close(connected_tc);
connected_tc = 0;
}
} else
/*
* se siamo connessi a un altro client come server
* sto chattando direttamente con qualcuno,
* invio i messaggi a lui.
*/
if(connected_ts > 0) {
if(strcmp(buf, "exit";) == 0) {
if(send(connected_ts, buf, BUFLEN, 0) == -1) {
printf("send()";);
}
close(connected_ts);
connected_ts = 0;
} else {
char res[BUFLEN];
if(send(connected_ts, buf, BUFLEN, 0) == -1) {
printf("send()";);
}
}
} else
/*
* se non siamo connessi direttamente a nessuno client
* invio i messaggi al server centrale.
* in assenza di messaggi validi il server centrale agisce come un echo server.
* i comandi validi sono:
* 'exit' -> chiude il programma
* 'DELETE' -> rimuove il client dalla lista dei client noti contenuta nel server centrale, digitando exit questo comando viene inviato automaticamente
* 'NAME|xxxx' -> inserisce il client nella lista dei client conosciuti contenuta nel server centrale e setta il nick del client a xxxx, per cambiare nick digitare di nuovo il comando
* 'TCP|xx' -> invia al server centrale la porta su cui il client è in ascolto di connessioni dirette, questo comando viene inviato automaticamente dopo il comando NAME
* 'LISTA' -> ottiene dal server centrale la lista dei client conosciuti
* 'CONNECT|xxxx' -> chiede al server centrale ip e porta del client con nick xxxx e esegue la connessione diretta, quindi fa partire il thread di lettura della socket client
* 'DEBUG' -> fa stampare al server informazioni utili non inviate al client
*/
{
if(strcmp(buf, "exit";) == 0) {
/* in caso di uscita setto ciclo a 0 e invio 'DELETE' */
ciclo = 0;
if(sendto(s, "DELETE", 6, 0, (struct sockaddr *) &server_socket, server_lenght) == -1) {
printf("sendto()";);
}
} else if(strcmp("NAME|", substring(buf, 0, 5)) == 0) {
/* salvo il nick nella variabile all'inizio e lo invio al server centrale per essere inserito nella lista */
nick = substring(buf, 5, strlen(buf) - 5);
if(sendto(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, server_lenght) == -1) {
printf("sendto()";);
}
} else {
/* invio al server centrale tutto quello che leggo */
if(sendto(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, server_lenght) == -1) {
printf("sendto()";);
}
}
/*
* dopo l'invio al server centrale di qualcosa aspetto che il server mi risponde, il server agisce come un echo server, sia in caso di comandi validi che di comandi non validi, quindi ci sarà sempre un ritorno */
if(recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, &server_lenght) == -1) {
printf("recvfrom()";);
}
if(strcmp(buf, "PROVIDE_TCP_PORT";) == 0) {
/* 'PROVIDE_TCP_PORT' -> il server vuole conoscere la porta su cui stiamo aspettando i client diretti, questo comando arriva automaticamente dopo l'invio di 'NAME' e rispondiamo quindi automaticamente col comando 'TCP' */
sprintf(buf,"TCP|%d",ts_port);
if(sendto(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, server_lenght) == -1) {
printf("sendto()";);
}
if(recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &server_socket, &server_lenght) == -1) {
printf("recvfrom()";);
}
} else if(strcmp("CLIENT|", substring(buf, 0, 7)) == 0) {
/* 'CLIENT|xxxx|yy' -> il server ci comunica ip e porta del client con cui abbiamo scelto di connetterci, dopo vari split avvio la socket diretta verso il client che farà da server nella connessione diretta */
char * info = substring(buf, 7, strlen(buf) - 7);
char * _info = strstr(info, "|";);
int pos = _info - info;
char * ip = substring(info, 0, pos);
int porta = atoi(substring(info, pos + 1, strlen(info) - pos - 1));
printf("CONNECTING TO %s PORT %d\t", ip, porta);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("* ";);
printf("*\t";);
struct sockaddr_in server_addr;

if ((connected_tc = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
printf("Socket";);
break;
}

memset((void *)&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(porta);
server_addr.sin_addr.s_addr = inet_addr(ip);

if(connect(connected_tc, (struct sockaddr *) & server_addr, sizeof(struct sockaddr)) == -1) {
printf("Connect";);
break;
}
/* se tutto è andato bene apro il thread che cicla sulla socket in lettura */
printf("CONNECTED\n";);
if(pthread_create(&p_thread_client, NULL, thread_client, NULL) != 0) {
fprintf(stderr, "errore non posso creare il thread 2\n";);

}
} else {
/* questo viene eseguito quando un comando non valido ritorna dal server, può essere eliminato */
printf("> %s\n", buf);
}
}
fflush(stdout);
}

close(s);
return 0;
}
aaa
30/04/11 22:51
pierotofy
Questo topic è in violazione di una o più norme del regolamento: pierotofy.it/pages/extras/forum/9/3839-regolamento/ .
    
Dopo averlo letto riapri un nuovo topic assicurandoti di aver rispettato le regole. Grazie per la tua pazienza.
Il mio blog: piero.dev