Verständnis Socket SOCK_DGRAM
-
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ücksendenClient:
->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 sollINADDR_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 (dasstruct
kann man in C++ weglassen):sockaddr_in from = {0};
-
Nachtrag: Im server machst du das ja auch mit
bzero
wie ich gerade sehe. Allerdings scheintbzero
veraltet zu sein und man soll liebermemset
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 ?
-
Kommt drauf an was genau der Server machen soll. Wenn der Server immer nur auf Anfrage Daten sendet brauchst du die einzelnen Adressen nicht zu speichern. Du machst einfach ein einer Endlosschleife zuerst ein
recvfrom
und dannsendto
an die Absender Adresse.
-
Hallo,
deswegen das Problem mit externen Klassen membern.
Ich soll irgendwo anders im Code eine nachricht an zwei clients schicken.
dazu muss ich doch erst in der main die addressdaten bekommen und speichern, um sie später wieder zu haben oder gibt es eine elegantere lösung ?
kann sie nicht von funktion zu funktion weitergeben
-
Du kannst die
sockaddr_in
structs aber einfach speichern. Entweder in einem Array oderstd::vector
für variable Größe. Und natürlich kann man diesockaddr_in
auch an andere Funktionen übergeben.
-
mmh, okay ...
ich hab das schon probiert.
ich hatte eine Klasse, dessen Instanz über extern global war.
aber wenn ich da ein private element mit typ sockaddr_in anlegen wollte, kekam ich immer einen Fehler.den Vektor müsste ich ja auch global machen oder ?
sonst müsste ich den von funktion zu funktion über 5-10 funktionen hinweg immer mitschleifen
-
okay, ich hab es jetzt so und es funktioniert
Der vector, oder eher array (mag ich persönlich lieber) ist global
// C++ Standard Includes #include <iostream> #include <thread> // std::this_thread::sleep_for #include <chrono> // std::chrono::seconds #include <array> #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" using namespace std; array<sockaddr_in,2> ClientAddr; int main(int argc, char *argv[]) { int ServerSock, n; int length = 0; socklen_t clientlen; struct sockaddr_in server = {0}; struct sockaddr_in client = {0}; char buf[1024]; // 1. Socket create if ( (!(ServerSock = socket(AF_INET,SOCK_DGRAM,0)) > 0) ) { cout << "Creating Server Socket failed" << endl; exit(-1); } length = sizeof(struct sockaddr_in); 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 ) { cout << "bind Server Socket failed" << endl; exit(-1); } clientlen = sizeof(struct sockaddr_in); for ( int i=0; i<2; i++) { n = recvfrom(ServerSock,buf,1024,0,(struct sockaddr *)&client,&clientlen); cout << "Gesendete Nachricht: " << buf << endl; if (n == -1){ syslog( LOG_INFO, "recvfrom failed"); exit(-1); } ClientAddr[i] = client; } while(1){ for ( int i=0; i<2; i++) { int n = sendto(ServerSock , "Hello. I'm the server..." ,1024,0,(struct sockaddr *)&ClientAddr[i] ,clientlen); if (n<0) { cout << "Fehler beim Senden" << endl; } } std::this_thread::sleep_for (std::chrono::seconds(2)); } closelog(); return EXIT_SUCCESS; }