Verständnis Socket SOCK_DGRAM



  • d.h. ich sende einfach datenpakete und die clients müssen selber zusehen, dass sie die bekommen ?

    ich sende einfach an einen Port, nicht an einen bestimmten client ?
    es werden zwei sein ...

    ich bin verwirrt 😕



  • Es gibt verschiedene Protokolle, die verschiedene Einsatzgebiete haben.
    TCP (SOCK_STREAM) ist verbindungsorientiert, d.h. es besteht für die Dauer der Kommunikation eine permanente Verbindung zwischen den beiden Endpunkten. TCP gewährleistet Datenintegrität, d.h., die empfangenen Daten sind vollständig und korrekt.
    UDP (SOCK_DGRAM) ist verbindungslos. Es findet keinerlei Kontrolle statt, die Daten werden abgeschickt und fertig. Daher gibt es bei UDP auch keine Endpunktinformationen über das Ziel, weil´s dem Protokoll egal ist, ob eine Gegenstelle gibt oder nicht.

    TCP kannst du mit einem Telefonat vergleichen. Bei UDP schreist du, was du deinem Gesprächspartner sagen willst und hoffst, dass er grad in der Nähe ist und zuhört.



  • aaah, jetzt hab ichs verstanden.

    super erklärt, dankeschön 👍 👍



  • Hallo,

    ich habe jetzt mal versucht was ganz einfaches zu programmieren,
    indem der Server einfach was schickt und der Client es bekommen sollte und ausgeben. Aber irgendwas hab ich anscheinend noch nicht ganz verstanden 😞

    Client

    // C++ Standard Includes
    #include <iostream>
    
    /* UDP client in the internet domain */
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <netdb.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    
    #include "testclient.h"
    
    #define BUF 256
    
    using namespace std;
    
    int main()
    {
    	int 	clientSock;
    	char 	buffer[256];
    	struct sockaddr_in from;
    	unsigned int length;
    
    	cout << "Client Programm wurde gestartet !" << endl;
    
    	if ((clientSock = socket(AF_INET,SOCK_DGRAM,0)) > 0 ) 
    		cout << "Client Socket wurde erfolgreich angelegt !" << endl;
    	else
    		cout << "Fehler beim Anlegen des Client Socket." << endl << "Fehlercode: " << clientSock << endl;
    
    	length=sizeof(struct sockaddr_in);
    
    	while(1){
    		cout << "Warte auf Daten..." << endl;
          int size = recvfrom(clientSock, buffer, BUF, 0, (struct sockaddr *)&from, &length);
    
          if( size < 0)
             cout << "error: recvfrom" << endl;
    
          cout << buffer << endl;
    
      } 
    
    	cout << "Client Socket wird geschlossen ..." << endl;  
    
      if ((close (clientSock)) == 0 )
      	cout << "Socket erfolgreich geschlossen !" << endl << "Good Bye !!!" << endl;
      else
      	cout << "Fehler beim Schliessen des Socket" << endl;
    
      return EXIT_SUCCESS;  
    }
    

    Server

    // C++ Standard Includes
    #include <iostream>
    
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <signal.h>
    #include <syslog.h>
    
    /* includes headers */
    #include "testserver.h"
    
    CanReader_Socket	socket_Class;
    
    using namespace std;
    
    int main(int argc, char *argv[]) {
    
    	int ServerSock, length;
    	struct sockaddr_in server;
    
    	openlog("testprogram", 0, LOG_USER);
    
    	// 1. Socket create
    	if ( (!(ServerSock = socket(AF_INET,SOCK_DGRAM,0)) > 0) ) {
    		syslog( LOG_INFO, "Creating Server Socket failed");
    		exit(-1); 
    	}
    
    	length = sizeof(server);
    	bzero(&server,length);
    	server.sin_family = AF_INET;
    	server.sin_addr.s_addr = inet_addr("127.0.0.1");
    	server.sin_port = htons(34567);
    
    	// 3. bind address and port to socket
    	if ( ( bind(ServerSock, (struct sockaddr*)&server, length) ) == -1 ) {
    		syslog( LOG_INFO, "bind Server Socket failed");
    		exit(-1);
    	}
    
            string vstring = "Test";
    	struct sockaddr_in from;
    	socklen_t fromlen = sizeof(struct sockaddr_in);
    	int n = sendto(ServerSock , vstring.c_str() ,256,0,(struct sockaddr *)&from, fromlen);
    
    	closelog();
    
       return EXIT_SUCCESS;  
    }
    


  • Du musst den Empfänger wie bei TCP auch an einen Port binden.



  • Hallo,

    danke für den Tipp 🙂

    Jetzt hab ich folgendes hinzugefügt:

    ...
    length = sizeof(struct sockaddr_in);
    	bzero(&from,length);
       from.sin_family = AF_INET;
       from.sin_addr.s_addr = inet_addr("127.0.0.1");
       from.sin_port = htons(34567);
    
        // 3. bind address and port to socket
        if ( ( bind(clientSock, (struct sockaddr*)&from, length) ) == -1 ) {
            cout << "bind Client Socket failed" << endl;
            exit(-1);
        }
    
    	while(1){
    		cout << "Warte auf Daten..." << endl;
    ...
    

    bekomme aber immer einen Fehler, weil der bind nicht funktioniert 😞
    Was übersehe ich ?
    Ich hab es wie beim Server gemacht



  • roflo schrieb:

    Du musst den Empfänger wie bei TCP auch an einen Port binden.

    Ist das so? In den UDP Beispielen die ich kenne und auch auf die schnelle finde wird beim Client nichts gebunden. Allerdings ist es so, dass die erste Paket vom Client ausgeht.

    zimtikus schrieb:

    bekomme aber immer einen Fehler, weil der bind nicht funktioniert 😞
    Was übersehe ich ?
    Ich hab es wie beim Server gemacht

    Du kannst auf dem gleichen Computer auch nicht zwei Sockets an den gleichen Port binden. Immer nur einen! Statt auf dem Client auch ein bind zu machen versuche mal ein sendto zum Server und der Server antwortet dann auf die Adresse des Absenders.



  • Hallo,

    ich hab den Client nur durch

    from.sin_port = htons(34567);
    

    an den Port gebunden und habe das bind wieder weg.

    Jetzt funktioniert es, ABER ...

    ich bekomme noch keine Daten ... 😞

    Liegt es vllt doch auf der Serverseite ?

    @sebi707:
    Mein letztes Client-Server-Programm lief auch auf demselben Port, aber eben über SOCK_STREAM ... ist es bei udp was anderes ?

    Kann ich eigentlich auch PF_UNIX nehmen ?



  • Okay, ich hab mal eine ausgabe mehr eingebaut.

    Es liegt am sendto-Befehl. Der kommt mit einem Fehler zurück 😞 😞



  • Nur durch from.sin_port = htons(34567); wird noch nichts gebunden. Bei deinem TCP Code hattest du im Client doch auch kein bind oder? Nur ein connect, nehme ich an. Also hattest du nicht beides an den gleichen Port gebunden. Wie sieht dein Code denn jetzt aus?



  • Irgendwie hast du die Begriffe Client und Server vertauscht. Der Client sendet dem Server daten und verarbeitet diese, und sendet vielleicht auch was zurück.

    Bei UDP ist das so:
    Server:
    -> bind()en an eine IP und Port, um mit recvfrom etwas zu hören
    -> mit recvfrom daten und deren ursprung (IP/Port) erhaschen
    -> mit sendto vielleicht was an jenen ursprung zurücksenden

    Client:
    ->bind()en an eine IP(0 für besten Adapter) und Port (0 für freien), um die die Quelladresse in zu sendenden Paketen einzustellen.
    -> mit sendto senden und mit recvfrom daten erwarten, die der server an die Quelladresse sendet.



  • Hallo,

    Der Code hat sich nicht verändert, außer die eine zeile mit dem port 🙂

    Mir ist gesagt wurden, dass ich den server darstellen soll und den zwei Clients daten schicken soll.



  • roflo schrieb:

    ->bind()en an eine IP(0 für besten Adapter) und Port (0 für freien), um die die Quelladresse in zu sendenden Paketen einzustellen.

    Dieser Schritt ist optional, wenn man zuerst mit sendto Daten sendet. Dann wählt der Kernel automatisch einen freien Port aus. Außerdem meintest du hoffentlich man soll INADDR_ANY für einen beliebigen Adapter nehmen (gut das ist zwar wahrscheinlich definiert als 0, aber sicher nicht garantiert überall) und was bei einem Port 0 passiert konnte ich noch keiner Dokumentation entnehmen.

    Zimtikus schrieb:

    Mir ist gesagt wurden, dass ich den server darstellen soll und den zwei Clients daten schicken soll.

    Das geht so direkt aber nicht mit UDP. Dem Server sind die Clients schließlich nicht bekannt und einen Broadcast sollt ihr vermutlich auch nicht programmieren. Die Clients sollten daher zuerst irgendwas an den Server senden um zu signalisieren, dass sie existieren und bereit sind etwas zu empfangen. Wenn der Server diese Nachricht kriegt hat er auch automatisch die Adresse der Clients und kann an diese Adresse dann etwas zurück schicken.



  • hi leute,

    mmh, danke für die tipps.

    mein code sieht inzwischen so aus, aber das senden klappt nicht 😞

    // C++ Standard Includes
    #include <iostream>
    
    /* UDP client in the internet domain */
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <netdb.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    
    #include "testclient.h"
    
    #define BUF 256
    
    using namespace std;
    
    int main()
    {
    	int 	clientSock, n;
    	char 	buffer[256] = "Message from client";
    	struct sockaddr_in from, server;
    	socklen_t length;
    
    	cout << "Client Programm wurde gestartet !" << endl;
    
    	if ((clientSock = socket(AF_INET,SOCK_DGRAM,0)) > 0 ) 
    		cout << "Client Socket wurde erfolgreich angelegt !" << endl;
    	else
    		cout << "Fehler beim Anlegen des Client Socket." << endl << "Fehlercode: " << clientSock << endl;
    
    	server.sin_family = AF_INET;
    	server.sin_port = htons(34567);
    	length = sizeof(struct sockaddr_in);
       from.sin_port = htons(34567);
    
      	n = sendto(clientSock,buffer, strlen(buffer),0,(const struct sockaddr *)&server,length);
      	if (n == -1) { cout << "Fehler bei sendto" << endl; }
    
    	while(1){
    		cout << "Warte auf Daten..." << endl;
          int size = recvfrom(clientSock, buffer, BUF, 0, (struct sockaddr *)&from, &length);
    
          if( size < 0 )
             cout << "error: recvfrom" << endl;
    
          cout << buffer << endl;
    
      } 
    
    	cout << "Client Socket wird geschlossen ..." << endl;  
    
      if ((close (clientSock)) == 0 )
      	cout << "Socket erfolgreich geschlossen !" << endl << "Good Bye !!!" << endl;
      else
      	cout << "Fehler beim Schliessen des Socket" << endl;
    
      return EXIT_SUCCESS;  
    }
    


  • Was heißt das Senden klappt nicht? Wie sieht der Server Code dazu aus? Ich habe deinen vorhandenen Server mal schnell abgeändert und damit funktioniert bei mir alles. Im übrigen brauchst du hierfür

    int size = recvfrom(clientSock, buffer, BUF, 0, (struct sockaddr *)&from, &length);
    

    nicht nochmal ein neues sockaddr_in Struct anlegen. Du kannst die gleiche Adresse ( server ) nehmen die du schon zum Senden genommen hast.



  • hallo,

    okay danke, ich schau es mir gleich nochmal an 🙂

    hier mein servercode:

    // C++ Standard Includes
    #include <iostream>
    
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <signal.h>
    #include <syslog.h>
    
    using namespace std;
    
    int main(int argc, char *argv[]) {
    
    	int ServerSock, length, n;
    	socklen_t fromlen;
       struct sockaddr_in server;
       struct sockaddr_in from;
       char buf[1024];
    
    	openlog("reader", 0, LOG_USER);
    
    	// 1. Socket create
    	if ( (!(ServerSock = socket(AF_INET,SOCK_DGRAM,0)) > 0) ) {
    		syslog( LOG_INFO, "Creating Server Socket failed");
    		exit(-1); 
    	}
    
    	length = sizeof(server);
    	bzero(&server,length);
    	server.sin_family = AF_INET;
    	server.sin_addr.s_addr = INADDR_ANY;
    	server.sin_port = htons(34567);
    
    	// 3. bind address and port to socket
    	if ( ( bind(ServerSock, (struct sockaddr*)&server, length) ) == -1 ) {
    		syslog( LOG_INFO, "bind Server Socket failed");
    		exit(-1);
    	}
    
    	fromlen = sizeof(struct sockaddr_in);
    
        n = recvfrom(ServerSock,buf,1024,0,(struct sockaddr *)&from,&fromlen);
    
        if (n == -1){
    	    syslog( LOG_INFO, "recvfrom failed");
    		 exit(-1);
    	 }
    
    	closelog();
    
       return EXIT_SUCCESS;  
    }
    


  • hier nochmal mein testclient:

    // C++ Standard Includes
    #include <iostream>
    
    /* UDP client in the internet domain */
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <netdb.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    
    #include "testclient.h"
    
    #define BUF 256
    
    using namespace std;
    
    int main()
    {
    	int 	clientSock, n;
    	char 	buffer[256] = "Message from client";
    	struct sockaddr_in from;
    	socklen_t length;
    
    	cout << "Client Programm wurde gestartet !" << endl;
    
    	if ((clientSock = socket(AF_INET,SOCK_DGRAM,0)) > 0 ) 
    		cout << "Client Socket wurde erfolgreich angelegt !" << endl;
    	else
    		cout << "Fehler beim Anlegen des Client Socket." << endl << "Fehlercode: " << clientSock << endl;
    
    	from.sin_family = AF_INET;
    	from.sin_port = htons(34567);
    	length = sizeof(struct sockaddr_in);
    
      	n = sendto(clientSock,buffer, strlen(buffer),0,(const struct sockaddr *)&from,length);
      	if (n == -1) { cout << "Fehler bei sendto" << endl; }
    
    	while(1){
    		cout << "Warte auf Daten..." << endl;
          int size = recvfrom(clientSock, buffer, BUF, 0, (struct sockaddr *)&from, &length);
    
          if( size < 0 )
             cout << "error: recvfrom" << endl;
    
          cout << buffer << endl;
    
      } 
    
    	cout << "Client Socket wird geschlossen ..." << endl;  
    
      if ((close (clientSock)) == 0 )
      	cout << "Socket erfolgreich geschlossen !" << endl << "Good Bye !!!" << endl;
      else
      	cout << "Fehler beim Schliessen des Socket" << endl;
    
      return EXIT_SUCCESS;  
    }
    

    hab jetzt nur eine sock_addr struktur genommen - danke für den Tipp.
    Aber irgendwas übersehe ich, denn das Senden klappt nicht ...
    d.h. ich bekomme nach dem sendto Befehl die Ausgabe: Fehler bei sendto
    Der sendto kommt also mit -1 zurück.

    mich verwundert es, dass es bei dir anscheinend funktioniert 😞



  • Ah ich seh gerade einen blöden Fehler. Du musst das sockaddr_in struct im client zuerst auf 0 setzen weil du gar nicht alle Werte setzt. Am besten direkt beim definieren der Variable (das struct kann man in C++ weglassen):

    sockaddr_in from = {0};
    


  • Nachtrag: Im server machst du das ja auch mit bzero wie ich gerade sehe. Allerdings scheint bzero veraltet zu sein und man soll lieber memset nutzen oder noch besser direkt bei der Definition.



  • Hallo,

    super danke 👍 👍 👍

    Jetzt funktioniert es 🙂

    Was ändert sich jetzt, wenn ich zwei Clients habe, denen ich Daten schicken soll ?
    Ich brauch ja dann von beiden die Adressdaten, um was zurückzuschicken oder ?


Anmelden zum Antworten