Socketprogrammierung
-
Ich habe ein kleines Prgramm nach einem tutorial verfasst, welches auf dem socketprinzip Daten zwischen Server und client verschickt.
läuft alles ganz gut mit einer Ausnahme, an der ich seit tagen schwitze. Nach dem 1. Schleifendurchlauf der do-while schleife vergleicht er nicht mehr die Städte sondern durchläuft diese Schleife immer wieder "blind". Außerdem häält der server nur bei jedem 2. mal bei der funktion read und wartet auf den client. hat jemand ne idee, wie ich das beheben kann?Server
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> //needed for socket #include <netinet/in.h> //for internet domain address #include <iostream> int StringCompare (const char *s1, const char *s2); void error(const char *msg) //function called, when error occour { perror(msg); exit(1); } using namespace std; int main(int argc, char *argv[]) { int sockfd, newsockfd; //file descriptors (store values returned by socket sytem call and accept system call int portno, ret; //portno = portnumber on which server accepts connections ; ret = stores value of StringCompare socklen_t clilen; char buffer[256]; //server reads characters from socket connection into the buffer char leipzig[256] = "leipzig"; struct sockaddr_in serv_addr, cli_addr; // sockaddr_in = struct containing internet adress. defined in netinet/in.h int n, nausg; // string switchv; if (argc < 2) { // displays an error, when user don't pass the port number fprintf(stderr,"ERROR, no port provided\n"); exit(1); } sockfd = socket(AF_INET, SOCK_STREAM, 0); //creates new socket (AF_INET = (unix command) internet domain; SOCK_STREAM = stream socket (characters are read in continuous stream; protocol (in case of stream socket we got a TCP protocol if (sockfd < 0) //check if we got a proper socket error("ERROR opening socket"); bzero((char *) &serv_addr, sizeof(serv_addr)); //sets all values in a buffer to zero (first argument is a pointer ti the buffer, the second is the buffersize. serv_addr is initialized to zeros portno = atoi(argv[1]); // function atoi converts the string of digits to an interger serv_addr.sin_family = AF_INET; //Adress family (internet domain) serv_addr.sin_addr.s_addr = INADDR_ANY; //INADDR_ANY = symbolic constant which gets the host ip adress serv_addr.sin_port = htons(portno); //htons convert portno (host byte order) to network byte order if (bind(sockfd, (struct sockaddr *) &serv_addr, //binds the socket to adress (1.Argument: socketfiledescriptor 2.Argument: address to which is bound 3.Argument: size of address to which is bound sizeof(serv_addr)) < 0) error("ERROR on binding"); listen(sockfd,5); //system call to listen on the socket connetion (1.Argument: socketfiledescriptor 2.Argumen: size of backlog queue clilen = sizeof(cli_addr); newsockfd = accept(sockfd,(struct sockaddr *) &cli_addr,&clilen); //accept block the process until a client connects, returns new filedescriptor for the communication (1Argument:socketfiledescriptor;2.Argument: reference pointer to the adress of the client 3.Argument: size of this structure) if (newsockfd < 0) error("ERROR on accept"); do { bzero(buffer,256); cout << "Server: Buffer nach bzero :" << buffer << endl; n = read(newsockfd,buffer,255); //read block until there is something to read in the socket. cout << "Server: Buffer :" << buffer << endl; if (n < 0) error("ERROR reading from socket"); // cout << "Here is the message: " << buffer << endl; ret = StringCompare(buffer,"Leipzig"); if (ret==true) { //fputs ("Leipzig: 22 Grad",buffer); n = write(newsockfd, "Leipzig 22 Grad", sizeof(buffer)); } ret = StringCompare(buffer,"Stuttgart"); if (ret==true) { n = write(newsockfd, "Stuttgart sonnig, 23 Grad", sizeof(buffer)); close(newsockfd); } ret = StringCompare(buffer,"Hamburg"); if (ret==true) { n = write(newsockfd, "Hamburg: windig, 18 Grad", sizeof(buffer)); } ret = StringCompare(buffer,"Erlangen"); if (ret==true) { n = write(newsockfd, "Erlangen sonnig, 23 Grad", sizeof(buffer)); } ret = StringCompare(buffer,"Konstanz"); if (ret==true) { n = write(newsockfd, "Konstanz neblig, 24 Grad", sizeof(buffer)); } if (n < 0) error("ERROR reading from socket"); else { n = write(newsockfd, "ungueltige Eingabe: ", 20 ); //nausg = write(newsockfd, buffer, sizeof(buffer)); } cout << "Server: Buffer am ende: " << buffer << endl; cout << "\n\n"; } while ( true); //nausg = write(newsockfd, "exit", sizeof(buffer)); //n = write(newsockfd,"ende vom spass",18); if (n < 0) error("ERROR writing to socket"); close(newsockfd); close(sockfd); return 0; } int StringCompare (const char *s1, const char *s2) //function to compare the character arrays { int ret; if (strcmp(s1,s2)) ret = 0; else ret = 1; return ret; }Client
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> //define a structure hostent #include <iostream> using namespace std; void error(const char *msg) { perror(msg); exit(0); } int main(int argc, char *argv[]) { int sockfd, portno, n; struct sockaddr_in serv_addr; struct hostent *server; //server is a pointer to a structure and will defines a host computer on the interet char buffer[256] ,exitv; if (argc < 3) { fprintf(stderr,"usage %s hostname port\n", argv[0]); // cout >> stderr exit(0); } portno = atoi(argv[2]); //server.cpp sockfd = socket(AF_INET, SOCK_STREAM, 0); //server.cpp if (sockfd < 0) error("ERROR opening socket"); server = gethostbyname(argv[1]); //takes hostname from argv and returns a pointer to a hostent containing inforamtion about that host if (server == NULL) { fprintf(stderr,"ERROR, no such host\n"); exit(0); } bzero((char *) &serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; bcopy((char *)server->h_addr, //copy byte by byte from source to destination because server->h_addr is a character String (arguments: source,destination,length) (char *)&serv_addr.sin_addr.s_addr, server->h_length); serv_addr.sin_port = htons(portno); //server.cpp if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) //connect establish a connection error("ERROR connecting"); do { cout << "Buffer am Beginn : " << buffer << endl; cout << "Stadt eingeben: "; bzero(buffer,256); cout << "\nBuffer nach bzero : " << buffer << endl; cin >> buffer; cout << "Buffer nach einlesen : " << buffer << endl; //fgets(buffer,255,stdin); fgets geht nich, weill Null-character \0 an arrayende gesetzt wird. //cout << "Buffer: |" << buffer <<"|" ; nur zum debuggen n = write(sockfd,buffer,sizeof(buffer)); if (n < 0) error("ERROR writing to socket"); bzero(buffer,256); n = read(sockfd,buffer,255); if (n < 0) error("ERROR reading from socket"); cout << "Buffer am Ende: " << buffer << endl; bzero(buffer,256); } while (true); close(sockfd); return 0; }wie ich das gerade verfolgt hab bricht der Server das ganze sogar mit der Meldung: ERROR reading from socket: Bad file descriptor
ab.
Brauche also wirklich dringend hilfe
-
1. Bitte richtig einrücken.
2. read() gibt es nicht auf Windows, recv() schon. Würde ich bevorzugen. (Gleiches Gilt bei write() und send())
3. Du solltest nicht nur auf < 0 sondern auch auf == 0 prüfen. Das passiert nämlich, wenn jemand die Verbindung ordentlich schließt.
PS: Bis auf cout ist das C und kein C++.
PPS: Nur weil dein Client alles auf einmal schickt, heißt das nicht, dass auch alles in einem Paket ankommt. Du solltest dir Trennzeichen überlegen um ein vollständiges Paket identifizieren zu können. z.B. '\n'.
-
bin auf unix unterwegs... deshalb weis ich das auch nich wirklich.
sind die funktionen für c++ und c (also accept/bind/listen) nicht gleich? Dem widme ich mich, wenn alles so einigermaßen läuft.
Das mit dem Trennzeichen meinst du aus kosmetischen gründen?! weil sich client und server ja immer nen character array von 255 zeichen hin und her schicken und nat. steht da am ende immer nen null-character. macht ja read() und write() von selbst?!
-
Die Sache ist einfach die, dass das was du absendest nicht auch so ankommen musst. Es kan zum Beispiel sein, dass du 256 Byte sendest und zunächst nur 20 Byte versendet werden. Anschließend dann 200 Byte und dann die restlichen Bytes.
Sprich: Die Segmentierung ist nicht festgelegt und du kannst nicht davon ausgehen, dass alles "in einem Rutsch" gesendet wird.
Dem kann man zum Beispiel entgegenwirken, indem man sagt, dass ein Leerzeichen eine Nachricht trennt.
Empfängst du dann z.B. Stuttgart\nBerlin\K
Dann weißt du, dass die Nachrichten Stuttgart und Berlin vollständig sind (Trennzeichen vorhanden). Bei der Nachricht K fehlt aber noch etwas, da das Trennzeichen noch nicht gelesen wurde. Kann also sein, dass beim nächsten Lesevorgang folgendes gelesen wird:
öln\nAmsterdam\nEine sehr einfache aber ziemlich ineffiziente Lösung wäre es, wenn du immer nur ein Zeichen einliest. Wenn dann dein Trennzeichen ankommt, dann verarbeitest du die Nachricht und liest danach weiter ein.
-
read und write machen da nix selbst ...
Null-Char als "trennzeichen" geht tedentiell, das wird dir aber, wenn er erst mal anfaengt deine Packete zu fragmentieren( d.h. er bestimmt selbst was wann geschickt wird) probleme mit den string funktionen machen ... sprich du kannst dann nimmer mit denen draufrumrutschen (die beenden alle ihre taetigkeiten, wenn die ein NULL zeichen bekommen). Sondern du musst selber die packete zusammenbauen mit binaeren funktionen.
Ausserdem sieht /n schoener aus, wenn deine communication mit nem terminal überwachst / emulierst
244 als standardgroesse geht technisch auch, aber macht eher sinn bei UDP (SOCK_DGRAM)
Der vorteil von TCP ist eigentlich, das alles in richtiger reihenfolge durchkommt. Der nachteil: es ist nicht bestimmt wann.
also wenn du 2 mal 512 byte schickst, kann am ande z.b. Packete als 300,300,300,124 ankommen. also du wurdest mit nem aufruf 300 bytes lesen, dann wieder ...besser ist fuer TCP ein nicht durch längen bestimmtes Flussprotokoll.
Und gut ist auch ein textBasierrendes (texte = ascii/utf8 = binaerspezifiziert, also kann nix drehen durch systeme, ausserdem kann man das besser loggen und emulieren )sind die funktionen für c++ und c (also accept/bind/listen) nicht gleich? Dem widme ich mich, wenn alles so einigermaßen läuft.
c ist als "Teil" in c++ integriert, mit paar kleinen besonderheiten.
Also kannst du von C++ immer auf c wechseln ....C++ zu C ist eher ne Style-Frage.
was du schreibst ist reiner C-Style.
Was cooky451 meint ist, das die verwendung von std::cout nicht zu deinem Stil passt
mit printf statt std::cout koennte dein code auch sogar nen reiner c-compiler fressen 
Ciao ...
-
Die richtige Konstante für die Funktion socket() heißt übrigens PF_INET, nicht AF_INET, auch wenn auf vielen Plattformen beide den selben Wert haben.
AF steht nämlich für address family, PF für protocol family.