Problema con server UDP in c

di il
8 risposte

Problema con server UDP in c

Salve buongiorno a tutti , sto lavorando su un'esercizio che riguarda scambio di stringhe tra client e server UDP, un client con il gethostbyname contatta il server e gli invia una stringa "Hello" e un stringa dallo stdin . Il server visualizzerà Hello e invierà al client la seconda stringa ricevuta senza vocali. Il punto focale di questo thread è un problema anomalo che non riesco a risolvere , ovvero, nel codice che posterò di seguito, funziona tutto e non ho nessun problema almeno fino a quando non devo far inviare la stringa senza vocali dal server al client. Per modificare la stringa ho provato cicli for , while solo che all'esecuzione del file exe del server ,non so per quale motivo, si chiude in modo anomalo non entrando nel while(1) e quindi non ricevendo nessuna stringa e non potendo quindi modificarla. Il problema è capire il perchè cicli sulla stringa mi chiude il server in modo anomalo,non avendo né warning né errori. Vi ringrazio in anticipo e buona giornata a tutti e spero di non aver fatto un thread banale risolvibile con poco.
#if defined WIN32
#include <winsock.h>
#else
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFERSIZE 30
#define PORT 48000

void ErrorHandler(char *errorMessage) {
printf("%s",errorMessage);
}
void ClearWinSock() {
#if defined WIN32
WSACleanup();
#endif
}



int main(void){


	#if defined WIN32
	WSADATA wsaData;
	int iResult = WSAStartup(MAKEWORD(2,2),&wsaData);
	if(iResult != 0){
		ErrorHandler("Errore inizio WSAStartup\n");
	}
	#endif

	int sock;
	struct sockaddr_in ServerAddr;
	struct sockaddr_in ClientAddr;
	struct hostent *host;
	const	char*	ip	=	"127.0.0.1";
	int ClientAddrLen;
	char buf[BUFFERSIZE];
	char buf1[BUFFERSIZE];
	memset(buf, 0, sizeof(buf));
	memset(buf1, 0, sizeof(buf));

	if((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0){
		ErrorHandler("Errore inizializzazione socket()\n");
	}

	memset(&ServerAddr, 0, sizeof(ClientAddr));
	ServerAddr.sin_family = AF_INET;
	ServerAddr.sin_port = htons(PORT);
	ServerAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

	if((bind(sock,(struct sockaddr *) &ServerAddr, sizeof(ServerAddr))) < 0){
			ErrorHandler("Errore con la bind()\n");
	}

	host =	gethostbyaddr((char*)&ClientAddr, sizeof(ClientAddr),  PF_INET);
	char* canonical_name =	host->h_name;

	printf("Aspettando una richiesta client\n");

	while(1){
		ClientAddrLen = sizeof(ClientAddr);
		recvfrom(sock, buf, BUFFERSIZE, 0, (struct sockaddr*)&ClientAddr, &ClientAddrLen);
		printf("Ricevuto: %s\n", buf);

		printf("Ricevuti dati dal client:%s con indirizzo: %s\n", canonical_name, ip);

		recvfrom(sock, buf1, BUFFERSIZE, 0, (struct sockaddr*)&ClientAddr, &ClientAddrLen);
		printf("Ricevuto: %s\n", buf1);

		for(int i=0; i<BUFFERSIZE; i++){
			if(buf1[i]=='a'||buf1[i]=='e'||buf1[i]=='i'||buf1[i]=='o'||buf1[i]=='u' ||buf1[i]=='A'||buf1[i]=='E'||buf1[i]=='I'||buf1[i]=='O'||buf1[i]=='U'){
				buf1[i]=' ';
			}
		}

		printf("Questa e' la stringa aggiornata %s", buf1);

	}




}

8 Risposte

  • Re: Problema con server UDP in c

    Non ho capito se la stringa "Aspettando..." la vedi o no
  • Re: Problema con server UDP in c

    oregon ha scritto:


    Non ho capito se la stringa "Aspettando..." la vedi o no
    La printf prima del while non la vedo, ma il problema non è ciò che avviene prima del while, rimuovendo il ciclo for per la modifica della stringa ricevuta dal client , la printf funziona .
  • Re: Problema con server UDP in c

    Ma la struttura

    ClientAddr

    non è inizializzata e poi host ovviamente è null e genera un errore ... sicuro del codice che hai scritto?

    Guarda

    https://www.tenouk.com/Winsock/Winsock2example9.htm
    https://www.binarytides.com/udp-socket-programming-in-winsock/
  • Re: Problema con server UDP in c

    oregon ha scritto:


    Ma la struttura

    ClientAddr

    non è inizializzata e poi host ovviamente è null e genera un errore ... sicuro del codice che hai scritto?

    Guarda

    https://www.tenouk.com/Winsock/Winsock2example9.htm
    https://www.binarytides.com/udp-socket-programming-in-winsock/
    La struttura ClientAddr non ha bisogno di essere inizializzata perchè ciò avviene automaticamente a livello di trasporto , anche perchè inizialmente io non conosco le info del client che mi contatterà, è quindi una struttura che ospiterà le informazione del client che contatterà il server. Ho risolto alla fine , ovviamente host era null perchè ClientAddr acquisisce i campi solo dopo la recvfrom e essendo l'assegnazione antecedente a essa , host era null . Grazie lo stesso
  • Re: Problema con server UDP in c

    Sono nuovo in questo forum,
    nel caso in cui dov'essi sbagliare nel postare correggetemi
    voglio solo aggiungere delle precisazioni:
    la mia macchina è ubuntu 20.04 kerlen 5.4.0-58, probabilemte per macchine con os e/o kernel diversi le mie precisazioni sono nulle.

    .forse aggiungere #include <sys/types.h>
    per portabilità sia su sistemi BSD,
    sia per versioni minori POSIX.1


    .forse aggiungere include
     #include <netdb.h>

    senza l'include netdb.h nella mia macchina (se a te funziona probabilemente non è necessario aggiungerlo)
    quando compilo il puntatore a struct hostent* lo riconosce
    ma poi mi dà errore di compilazione quando uso il puntatore a hostent host->h_name;

    error: dereferencing pointer to incomplete type ‘struct hostent’
    canonical_name=host->h_name;
    https://man7.org/linux/man-pages/man3/gethostbyaddr.3.html

    .sostituire
    ServerAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    con
    ServerAddr.sin_addr.s_addr = INADDR_ANY;
    altrimenti sei vincolato in loopback,
    così puoi farlo girare sia in loopback, macchina virtuale, o su un'altra macchina target.

    .la doppia chiamta recvfrom, ne basta una. altrimenti il client deve spedire 2 volte il pacchetto.

    .se è possibile, aggiungere
    #include <errno.h>
    e ulteriori stampe su ErrorHandler, così da avere un immediato riscronto su possibili malfunzionamenti,questo è solo un suggerimento, senza alcun impatto sul funzionamento
    	void ErrorHandler(char *errorMessage) {
    		printf("%s",errorMessage);
    		printf(" error code: %d\n",errno);
    		printf(" error string: %s\n",strerror(errno));
    	}


    queste sono solo precisazioni/consigii
    il problema principale è già stato risolto che la ClientAddr che deve essere richiamata dopo la recvfrom.
    spero di aver dato un contributo positivo.

    che la forza sia con voi
  • Re: Problema con server UDP in c

    Mi sono dimenticato di dirti altre due cosette

    1. la funzione ClearWinSock non viene richiamata

    2. non c'è bisogno che il for arrivi a BUFFERSIZE. aggiungi una variabile come return della recvfrom
    ssize_t numbrx = recvfrom(sock, buf1, BUFFERSIZE, 0, (struct sockaddr*)&ClientAddr, &ClientAddrLen);
    così il for lo fai girare
    for(int i=0; i<numbrx; i++)
    ti risparmi delle operazioni in più
    immagina che il buffer è dimensionato su KB e ricevi dal client delle risposte di Acknoledge grandi 2 bytes
    al posto di loopare su 2 bytes il for gira sempre per la dimensione massima
    se lo vincoli al numero di bytes realmente ricevuti il server diventa più efficiente.

    posso sembrarti pignolo, ma purtroppo ci vuole tanta , tanta, tanta pazienza e tanta umiltà ,
    anche se scriviamo codice quando è nato il C64 c'è sempre qualcosa che sfugge ai nostri occhi e non a quello di una persona esterna.
    se vuoi ti posto il codice ripulito.

    A presto
  • Re: Problema con server UDP in c

    LukeSkyWalker ha scritto:


    Mi sono dimenticato di dirti altre due cosette

    1. la funzione ClearWinSock non viene richiamata

    2. non c'è bisogno che il for arrivi a BUFFERSIZE. aggiungi una variabile come return della recvfrom
    ssize_t numbrx = recvfrom(sock, buf1, BUFFERSIZE, 0, (struct sockaddr*)&ClientAddr, &ClientAddrLen);
    così il for lo fai girare
    for(int i=0; i<numbrx; i++)
    ti risparmi delle operazioni in più
    immagina che il buffer è dimensionato su KB e ricevi dal client delle risposte di Acknoledge grandi 2 bytes
    al posto di loopare su 2 bytes il for gira sempre per la dimensione massima
    se lo vincoli al numero di bytes realmente ricevuti il server diventa più efficiente.

    posso sembrarti pignolo, ma purtroppo ci vuole tanta , tanta, tanta pazienza e tanta umiltà ,
    anche se scriviamo codice quando è nato il C64 c'è sempre qualcosa che sfugge ai nostri occhi e non a quello di una persona esterna.
    se vuoi ti posto il codice ripulito.

    A presto
    Figurati mi sei stato di grande aiuto, alla fine quello che sto facendo è a scopo didattico , sono andato a rivedermi la sintassi del c e le socket hanno una sintassi un pò confusionaria . E' molto più utile essere "pignoli" ed essere esaustivi che lasciare cose al caso . Ti ringrazio ancora e mi farebbe piacere vedere il codice ripulito anche per imparare qualcosa di nuovo che non fa mai male.
  • Re: Problema con server UDP in c

    Rieccoci

    il codice che ti posto non è tanto diverso dal tuo, ci sono un po' più di controlli.

    sia il client, che il server, sono parametrici.
    Setto gli argomenti, e quindi le variabili, quando lancio il processo.
    altrimenti se cambio porta, messaggio, o indirizzi lan, mi tocca riscrivere il codice.

    Importante:

    nel client ho messo un controllo sulla lunghezza del messaggio, strlen(_argv1[3]) > BUFFERSIZE
    non è necessario perchè non chiamo ne memcpy, ne strcpy, ma setto direttamente il puntatore char* buffer_to_send = argv[3];
    il messaggio puo' essere lungo quanto vuoi, però dovra essere minore di 65535 bytes che è la lunghezza massimo del protocollo udp, (se non ricordo male).
    il controllo serve solo per avere un riscontro immediato su quello che spedisce il client e quello che riceve il server,
    per evitare il caso in cui il client spedisce un buffer di 35 caratteri e il server cmq sia ne visualizza solo 30.
    questo perchè nel server la recvfrom() è settata con BUFFERSIZE, e più di BUFFERSIZE non riceve.

    nel server ho settato la cella numbrx del buffer con il carattere di fine stringa recv_buf[numbrx]='\0';
    questo perchè se il client spedisce prima HELLOUDP e poi spedisce ONE
    la printf del server , al secondo messaggio, visualizzerà ONELOUDP perchè rimane il refuso del messaggio precedente.
    si potrebbe anche resettare il buffer recv_buf con memset, ma lo sconsiglio
    perchè, sempre per il discorso di prima, se vengono spediti
    messaggi lunghi o a raffica la memset inizia ad avere un peso.
    cosi facendo setti direttamente l'ennesimo bytes ricevuto con la '\0' carattere di fine stringa, e la printf sarà corretta.

    se proprio ti vuoi leggere qualcosa
    su internet c'è tanto a volte pure troppo e non si sa' dove cercare
    ce ne sono milioni di testi, questo è un buon manuale che volendo in rete si trova

    The Linux Programming Interface di Michael KerrisK

    cmq l'esempio che hai fatto va' benissimo come inizio.
    dovresti ampliarlo mettendo dei signal handler che richiamano dei thread che processano il messagio
    così ricopri vari argomenti.

    Se hai domande o dubbi non aver timore.

    The Force May Be With You.

    Client:
    
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <errno.h>
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define BUFFERSIZE 	30
    
    int in_arg_check(int _argci,char* _argv1[]){
    	const char* help_txt={"./udp_client (address to send) (port) \"(message)\" \n"
    							"ex: ./udp_client 192.168.1.5 48000 \"HELLO_UDP\" \a"};
    	const int num_of_arg=4; //(processname) (address to send) (port) (message)
    
    	if(_argci < num_of_arg){
    		printf("too few argument, how to launch: %s\n",help_txt);
    		return EXIT_FAILURE;
    	}else if(_argci > num_of_arg){
    		printf("too many argument, how to launch: %s\n",help_txt);
    		return EXIT_FAILURE;
    	}else if (strlen(_argv1[3]) > BUFFERSIZE){ // check lenght of message
    		printf("too long message less than 30 bytes or chars %d\n",strlen(_argv1[3]));
    		return EXIT_FAILURE;
    	}
    	return EXIT_SUCCESS;
    }
    
    void ErrorHandler(char *errorMessage) {
    	printf("%s",errorMessage);
    	printf(" error code: %d\n",errno);
    	printf(" error string: %s\n",strerror(errno));
    }
    
    int main(int argc,char* argv[]){
    
    	if(EXIT_FAILURE == in_arg_check(argc,argv))
    		return EXIT_FAILURE;
    
    	int sock;
    	struct sockaddr_in ServerAddr;
    
    	// set udp param
    	char* addrip_server = argv[1]; // address server ip
    	uint16_t remote_port = atoi(argv[2]); // remote port
    	char* buffer_to_send = argv[3]; // buffer to sent
    	size_t lenght_msg = strlen(buffer_to_send);
    
    	sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
    	if(sock < 0){
    		ErrorHandler("Errore inizializzazione socket()\n");
    		return EXIT_FAILURE;
    	}
    
    	memset(&ServerAddr, 0, sizeof(ServerAddr));
    	ServerAddr.sin_family = PF_INET;
    	ServerAddr.sin_port = htons(remote_port);
    	ServerAddr.sin_addr.s_addr = inet_addr(addrip_server);
    
    	int sent_bytes = sendto(sock,buffer_to_send,lenght_msg,0,(struct sockaddr *)&ServerAddr,sizeof(ServerAddr));
    	if( sent_bytes < 0 ){
    		ErrorHandler("Errore con la sendto()\n");
    		return EXIT_FAILURE;
    	}
    	printf("sent message:\"%s\" num. bytes:%d \nto ---->>> address ip:%s remote port:%d\n",buffer_to_send,sent_bytes,addrip_server,remote_port);
    
    	return EXIT_SUCCESS;
    }

    Server:
    
    #include <sys/types.h> // socket BSD o release minori di POSIX.1
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <netdb.h> // hostent
    #include <errno.h> // errno
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define BUFFERSIZE 	30
    
    int in_arg_check(int _argci,char* _argv1[]){
    	const char* help_txt={"./udp_server (port) \a\n"
    							"ex: ./udp_server 48000 "};
    	const int num_of_arg=2; //(processname) (address to send) (port) (message)
    
    	if(_argci < num_of_arg){
    		printf("too few argument, how to launch: %s\n",help_txt);
    		return EXIT_FAILURE;
    	}else if( _argci > num_of_arg){
    		printf("too many argument, how to launch: %s\n",help_txt);
    		return EXIT_FAILURE;
    	}
    
    	printf("set local port: %s\n",_argv1[1]);
    	return EXIT_SUCCESS;
    }
    
    void ErrorHandler(char *errorMessage) {
    	printf("%s",errorMessage);
    	printf(" error code: %d\n",errno);
    	printf(" error string: %s\n",strerror(errno));
    }
    
    int main(int argc,char* argv[]){
    
    	if(EXIT_FAILURE == in_arg_check(argc,argv))
    		return EXIT_FAILURE;
    
    	struct sockaddr_in ServerAddr;
    	struct sockaddr_in ClientAddr;
    
    	// set udp param
    	uint16_t local_port = atoi(argv[1]); // local port
    	unsigned int ClientAddrLen = sizeof(ClientAddr);
    	char recv_buf[BUFFERSIZE];
    
    	memset(&ServerAddr, 0, sizeof(struct sockaddr_in));
    	memset(&ClientAddr, 0, sizeof(struct sockaddr_in));
    	memset(recv_buf, 0, sizeof(recv_buf));
    
    	int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
    	if(sock < 0){
    		ErrorHandler("Errore inizializzazione socket()\n");
    		return EXIT_FAILURE;
    	}
    
    	ServerAddr.sin_family = AF_INET;
    	ServerAddr.sin_port = htons(local_port);
    	ServerAddr.sin_addr.s_addr = INADDR_ANY;
    
    	int ret_bind = bind(sock,(struct sockaddr *) &ServerAddr, sizeof(ServerAddr));
    	if(ret_bind < 0){
    		ErrorHandler("Error bind()\n");
    		return EXIT_FAILURE;
    	}
    
    	while(1){
    		printf("wait message from client...\n");
    		ssize_t numbrx = recvfrom(sock, recv_buf, BUFFERSIZE, 0,
    									(struct sockaddr*)&ClientAddr,	&ClientAddrLen);
    		if(numbrx <= 0){
    			ErrorHandler("Error recvfrom()\n");
    			return EXIT_FAILURE;
    		}
    
    		printf("received bytes :%lu\n",numbrx);
    		recv_buf[numbrx]='\0'; // truncate buffer at current bytes received to delete previous message
    		printf("received message: \"%s\"\n",recv_buf);
    
    		for(int i=0; i<numbrx; i++){
    			if(	recv_buf[i]=='a' || recv_buf[i]=='e' || recv_buf[i]=='i' || recv_buf[i]=='o' || recv_buf[i]=='u' ||
    				recv_buf[i]=='A' || recv_buf[i]=='E' || recv_buf[i]=='I' || recv_buf[i]=='O' || recv_buf[i]=='U'){
    				recv_buf[i]=' ';
    			}
    		}
    		printf("modify message: \"%s\"\n", recv_buf);
    
    		struct hostent* client = gethostbyaddr((char *)&ClientAddr.sin_addr.s_addr,sizeof(struct in_addr), PF_INET);
    		if ( client != NULL ){
    			printf("client name: %s\n\n",client->h_name);
    		}
    		else{
    		   ErrorHandler("Error gethostbyaddr()\n");
    		   return EXIT_SUCCESS;
    		}
    	}
    }
    
Devi accedere o registrarti per scrivere nel forum
8 risposte