recv() Empfangsproblem



  • Also der komplette Clientcode:

    /*
    ######################################################################################
    
    testversion TCP client
    
    date of change:12.03.13 00:04
    
    Version: v1
    
    ######################################################################################
    */
    
    #include <winsock.h>
    #include <ansi_c.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    //#define IP "10.149.136.25"
    #define IP "192.168.0.1"
    //#define IP "169.254.111.2"
    #define PORT 5025
    #define READBUF 1024 
    
    char a[2];
    
    int startWinsock(){
    	WSADATA wsa;
    	return WSAStartup(MAKEWORD(2,0),&wsa);
    }
    
    int Connection_build(SOCKET sock){
    
    	//SOCKET sock;	//Socketdescriptor
    	SOCKADDR_IN addr;
    	long ret;
    	int send_len;
    	char c_user_input;
    
    //start winsock
    	if((ret=startWinsock())!=0){
    		printf("failed to start winsock! code: %d\n",WSAGetLastError());
    	return 1;
    	}
    	else{
    		printf("winsock successfully started...\n");
    	}
    //genrate socket
    	if((sock = socket(AF_INET, SOCK_STREAM, 0))==INVALID_SOCKET){
    		printf("failed to generate sockets!\n");
    		return 1;
    	}
    	else{
    		printf("sockets successfully generated...\n");
    	}
    //fill struct SOCKADDR_IN with parameters: Adressfamilie, Ports(umgewandelt), IP
    	memset(&addr,0,sizeof(SOCKADDR_IN)); // zuerst alles auf 0 setzten 
    	addr.sin_family=AF_INET;
    	addr.sin_port=htons(PORT); 
    	addr.sin_addr.s_addr=inet_addr(IP); 
    
    //connecting
    	if((ret=connect(sock,(SOCKADDR*)&addr,sizeof(SOCKADDR)))==SOCKET_ERROR){
    		printf("failed to establish connection! code: %d\n",WSAGetLastError());
    		return 1;
    	}
    	else{
    		printf("connection established!\n");
    	}
    return sock;
    }
    
    int Connection_send(SOCKET sock, char* c_user_input){
    //send userinput
    	int send_len;
    	//char c_user_input[256]={"ACT-USER::root:1::root;"};
    	//char c_user_input[256];
    
    	//printf("Eingabe: ");
    	//gets(c_user_input);   //userinput
    	printf("\n");
    	//strcat(c_user_input,"\0");
    	//send_len=strlen(c_user_input)+2; //Länge herausfinden + Nullzeichen
    	send_len=strlen(c_user_input)+1; //Länge herausfinden + Nullzeichen
    	if(send_len > 256){
    		printf("wrong determined input length!\n");
    		return 1;
    	}
    
    	if (send(sock, c_user_input, send_len, 0) != send_len){
    		printf("send() sent a different number of bytes than expected! code:%d\n",WSAGetLastError());
    	}
    
    	printf("send_len: %d\n",send_len);
    	//gets(a);
    return sock;
    }
    
    int Connection_recv(SOCKET sock){
    
    	int size_recv;
    	char buffer[1024];
    
    	size_recv = recv(sock, buffer, 1024, 0);
    	if (size_recv > 0)
    		buffer[size_recv] = '\0';
    	printf("Recv: %s\n", buffer);
    
    return sock;
    }
    
    int Connection_close(SOCKET sock){
    
    	closesocket(sock);
    	printf("socket closed");
    return 0;
    }
    
    int main(){
    	SOCKET sock;
    	sock=Connection_build(sock);
    	if(sock==1){					 // break if connection failed
    		gets(a);
    		return 1;
    		}
    	sock=Connection_send(sock, ":oxc:swit:conn:only (@1), (@95);*opc?");
    	//sock=Connection_send(sock, "ENT-PATCH::1,1:81:;");
    	sock=Connection_recv(sock);
    	Connection_close(sock);
    	gets(a);
    
    return 0;
    }
    

    der Output dazu...

    winsock successfully started...
    sockets successfully generated...
    connection established!

    send_len: 38

    Der Cursor wartet dann eine Zeile unter send_len: 38 und da bleibt er auch

    Ich habe mit einem Portscanner das Gerät gescannt, offene ports sind 333, 5025, 9999 und 80. Mit dem fertigen Clientporgramm habe ich den Port 5025 getestet ,Commands senden funktioniert und ich bekomme eine Antwort.



  • hustbaer schrieb:

    Vielleicht will das Gerät ja den String gar nicht mit \0 am Ende sondern mit \n oder gar \r\n?

    klasse Kerl! es geht, ich brauchte ein new line damit der Command abgeschlossen wird, jetzt erhalte ich auch einen Response 😃

    tausend Dank 👍



  • Sehr gut. Und gleich merken.
    Bei textbasierten Protokollen ist \n oder \r\n als Abschluss sehr üblich.

    Und gleich nochwas: dein recv() ist mit an Wahrscheinlichkeit grenzender Sicherheit falsch. Standardfehler, macht jeder 2. Anfänger. Du musst, genau so wie der Server es macht, so lange lesen bis du den Terminator (vermutlich wieder \n ) empfangen hast. Einfach 1x recv() machen und hoffen dass alles in einem Rutsch ankommt funktioniert zwar oft - aber halt nur so lange bis es nicht mehr funktioniert. z.B. weil die Antwort mal etwas länger ausfällt, oder der Server etwas eigenwillig (aber nicht falsch) implementiert ist.



  • Wird gemerkt, thx!

    Das recv() wollte ich eh noch anpassen, hatte schon davon gelesen das manchmal etwas erst beim 2.Mal ankommen kann, mir war erstmal nur wichtig das überhaupt was ankommt 😃

    String-Literals modifiziert man nicht. Phöööse.

    Dass habe ich auf jedenfall noch nicht gerafft.



  • wäre dieser recv/select Aufbau so halbwegs in Ordnung?

    ...
    	struct timeval time;
    	fd_set fd;
    	time.tv_sec = 3;
    	time.tv_usec = 0;
    
       do{  
        	size_recv = recv(sock, buffer, sizeof(buffer), 0);
    
        	if (size_recv > 0){
    
        	for(c=0;c<size_recv; c++){
        			if(buffer[c]=='\n'){
        				printf("newline gefunden, raus aus der Schleife\n");
        				buffer[size_recv] = '\0';
            			printf("Bytes received: %d\n", size_recv);
            			printf("Recv: %s\n", buffer);
        				return sock;
        			}
        		}
        	}
        else if (size_recv == 0) 
            printf("Connection closed\n"); 
    
        else 
            printf("recv failed: %d\n", WSAGetLastError()); 
    
    	FD_ZERO(&fd);
    	FD_SET(sock,&fd);
    	retselect=select(sock, &fd, NULL, NULL, &time);   // null dann nichtmehr angekommen
    
    	}while(retselect != 0);
    


  • Nö, du musst schon die einzelnen empfangenen Bruchstücke zusammenhängen wenn du danach eine vollständige Nachricht haben willst.
    So überschreibst du ein empfangenes Bruchstück immer mit dem nächsten.

    Und das select() mit 3 Sekunden Timeout... huch?
    Willst du dass die Schleife abbricht wenn 3 Sekunden lang keine neuen Daten mehr ankommen?
    Wenn nicht, dann lass das select einfach weg.
    Und wenn schon, wieso verwendest du dann kein select() vor dem ersten recv() ? Was bringt mir ein cooles Receive-Timeout, wenn es nur nach dem ersten Paket greift?



  • Ok mit dem ersten Teil hab ich schon vermutet.

    Ich dachte mir 3 Sekunden werden wohl reichen damit die restlichen Paketdaten ankommen, wollt eigentlich noch runtersetzen. Brauch ich das select nicht, damit das recv beim 2. Aufruf nicht blockt, falls keine Daten mehr ankommen, so habe ich das jedenfalls verstanden. 😕



  • _Neuling schrieb:

    Ok mit dem ersten Teil hab ich schon vermutet.

    Vermutet?
    Wie programmierst du denn? "Ich vermute ich bräuchte hier ne Variable... schreiben wir mal eine hin"
    WTF?
    Ist doch vollkommen klar, was gibt es da zu vermuten?

    Ich dachte mir 3 Sekunden werden wohl reichen damit die restlichen Paketdaten ankommen, wollt eigentlich noch runtersetzen. Brauch ich das select nicht, damit das recv beim 2. Aufruf nicht blockt, falls keine Daten mehr ankommen, so habe ich das jedenfalls verstanden. 😕

    Es gibt überhaupt keine Möglichkeit rauszubekommen ob noch was kommt.
    Deswegen legt man die Protokolle auch so aus, dass der Empfänger anhand der Daten rausbekommen kann ob noch was kommt.

    Wenn der Sender auflegt kommt recv() ja sofort zurück.

    Und wenn der Sender einfach aufhört zu senden, oder einfach verschwindet (Kabel ab, Strom weg, ...) ... dann wartet man ewig.



  • Vermutung Driven Development 😉



  • so,select etwas angepasst, ohne Vermutungen diesmal 😃 Die 5Sekunden sind erstmal nur zu Testzwecken(genauso wie einige printf), welche Zeit wäre da realistisch?

    Passt das soweit?

    FD_ZERO(&fd);
    	FD_SET(sock,&fd);
    
        do{  
        //	
        retselect=select(sock, &fd, NULL, NULL, &time);   // null dann timeout,
        	if(retselect <1){
        		printf("no answer until %d seconds\n",time.tv_sec);
    
        	}
        	else{
    
        	printf("retselec: %d\n",retselect);
        	size_recv = recv(sock, buffer, sizeof(buffer), 0);
    
        	if (size_recv > 0){
    
        	strcat(data_recv, buffer);
    
        		for(c=0;c<size_recv; c++){
        			if(buffer[c]=='\n'){
        				printf("newline gefunden, raus aus der Schleife\n");
            			printf("Bytes received: %d\n", size_recv);
            			printf("Recv: %s\n", buffer);
                                    return sock;
        			}
        		}
        	}
        else if (size_recv == 0) 
            printf("Connection closed\n"); 
    
        else 
            printf("recv failed: %d\n", WSAGetLastError()); 
    
    	}
    	}while(retselect > 0 );
    
        //printf("Recv: %s\n", buffer);
        printf("data_recv: %s\n",data_recv);
    return sock;
    


  • Das "aneinanderhängen" der Empfangenen Daten ist falsch.
    Deine Einrückung ist inkonsistent und furchtbar.



  • Einrücken verbessert, und der Rest 🙄

    warum ist strcat so falsch? Sind doch alles Zeiger auf char mit den ich arbeite?!

    FD_ZERO(&fd);
    	FD_SET(sock,&fd);
    
        do{  
    
        	retselect=select(sock, &fd, NULL, NULL, &time);   // null dann timeout,
        	if(retselect <1)
    
        		printf("no answer until %d seconds\n",time.tv_sec);
    
        	else{
    
        		printf("retselec: %d\n",retselect);
        		size_recv = recv(sock, buffer, sizeof(buffer), 0);
        		if(size_recv > 0){
    
        			for(c=0;c<size_recv; c++){
        				data_recv[strlen(data_recv)]=data_recv[c+strlen(data_recv)]+buffer[c];
        				if(buffer[c]=='\n'){
    
        					printf("newline gefunden, raus aus der Schleife\n");
            				printf("Bytes received: %d\n", size_recv);
            				printf("Recv: %s\n", buffer);
            				return sock;
        				}
    
        			}
        		}
    
        		else if (size_recv == 0) 
    
            		printf("Connection closed\n"); 
    
        		else 
    
            		printf("recv failed: %d\n", WSAGetLastError()); 
    		}
    
    	}while(retselect > 0 );
        printf("data_recv: %s\n",data_recv);
    
    return sock;
    }
    


  • strcat war falsch, weil recv() keine Strings empfängt, und daher auch keinen Null-Terminator anhängt.

    Jetzt ist es noch falscher.

    Was habt ihr Noobs bloss alle immer mit den ganzen String-Funktionen?



  • warum hat dann mein Rückgabewert von recv() dann den Wert 6 bei 4 übertragenen Bytes?

    send() war "1234"

    buffer[0] 49
    buffer[1] 50
    buffer[2] 51
    buffer[3] 52
    buffer[4] 10
    buffer[5] 0
    buffer[6] -23
    ...

    "jetzt noch falscher" bringt mir irgendwie nicht viel 🙄



  • _Neuling schrieb:

    warum hat dann mein Rückgabewert von recv() dann den Wert 6 bei 4 übertragenen Bytes?

    send() war "1234"

    buffer[0] 49
    buffer[1] 50
    buffer[2] 51
    buffer[3] 52
    buffer[4] 10
    buffer[5] 0
    buffer[6] -23
    ...

    "jetzt noch falscher" bringt mir irgendwie nicht viel 🙄

    Das Problem ist, dass es auch sein kann, dass du zuerst 3 bytes und dann in einem zweiten Aufruf von recv(..) nochmals 3 bytes erhältst. TCP ist stream-orientiert, du bist selbst für die Meldungsgrezen zuständig.



  • Ich habe einen Breakpoint direkt nach recv gesetzt um mir die Werte anzuschauen, demnach wurde recv nur einmal aufgerufen.



  • Es kann, muss muss nicht...

    Es kann z.B. auch folgende Situation auftreten:

    Auf der Senderseite:
    send(..) 6 bytes (nennen wir sie b0 bis b5, zusammen Meldung 1, kurz m1)
    send(..) 4 bytes (nennen wir sie b6 bis b9, zusammen Meldung 2, kurz m2)

    Auf der Empfängerseite:
    recv(..) 3 bytes empfangen (b0 bis b2) m1 angefangen
    recv(..) 2 bytes empfangen (b3 bis b4)
    recv(..) 3 bytes empfangen (b5 bis b7) m1 komplett, m2 angefangen
    recv(..) 2 bytes empfangen (b8 bis b9) m2 komplett

    Aus diesem Grund musst Du deinen Code so schreiben, dass er immer funktioniert, auch wenn recv(..) nicht genau eine Meldung in einem Aufruf empfängt.

    Edit

    warum hat dann mein Rückgabewert von recv() dann den Wert 6 bei 4 übertragenen Bytes?

    Weil 1. nicht "1234" sondern "1234\r" übertragen wurde - inkl. terminierender Null. Es wurden also 6 bytes übertragen.



  • Das habe ich verstanden, falls nicht alles gesendet wurde beim ersten Mal schaut select() ob über dem socket noch was ankommt und macht dann noch einmal den recv()-Aufruf, das funktioniert soweit ich das testen kann. Nur scheint mein Abspeichern der Bufferwerte nicht korrekt, aber ich weiß nicht warum.

    hustbaer schrieb:

    strcat war falsch, weil recv() keine Strings empfängt, und daher auch keinen Null-Terminator anhängt.

    Ich denke recv() empfängt keine Nullzeichen?



  • recv(..) empfängt das was du sendest. Wenn du eine terminierende Null mitsendest, wird sie natürlich auf übertragen. Der Punkt ist aber, dass recv(..) nicht unbedingt in einem Aufruf all das empfängt, was du gesendet hast. Und deshalb ist die Benutzung von strcat(..), strlen(..) etc. blödsinn.



  • okok! also hat mein Server für diesen Fall dummerweise eine Null mitgesendet.

    Demnach eine Null an mein recv anhängen und dann mit strlen oder strcat zusammensetzen. Oder gibts da eine elegantere Variante?

    size_recv = recv(sock, buffer, sizeof(buffer), 0);
        		buffer[size_recv] = buffer[size_recv]+1;
        		if(size_recv > 0){
    
        			for(c=0;c<size_recv; c++){
    
        				data_recv[strlen(data_recv)]=data_recv[c+strlen(data_recv)]+buffer[c];
    

Anmelden zum Antworten