[Socket] Zeichen werden verschluckt
-
Hallo!
Ich hab vor kurzem bzgl. Sockets eine Frage gehabt... und nun wieder.
Habe eben was probiert und es klappt. Und zwar habe ich eine HTTP-Anfrage zu machen. Klappt alles wunderbar, jedoch verschluckt der mir den letzten Tag und zwar </html>.Meine Funktion zum empfangen:
int handling(int sock) { char buffer[BUFFER_SIZE]; int bytes; while (true) { buffer[0] = '\0'; bytes = recv(sock, buffer, sizeof(buffer) - 1, 0); buffer[bytes] = '\0'; cout << buffer; } return 0; }Was passt hier nicht?
gesamter code:
#include <iostream> #include <fstream> #include <string> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <arpa/inet.h> #include <unistd.h> using namespace std; #define BUFFER_SIZE 1024 #define DEFAULT_ADDR "85.125.39.37" #define DEFAULT_PORT 80 int handling(int sock) { char buffer[BUFFER_SIZE]; int bytes; while (true) { buffer[0] = '\0'; bytes = recv(sock, buffer, sizeof(buffer) - 1, 0); buffer[bytes] = '\0'; cout << buffer; } return 0; } int main(int argc, char *argv[]) { int s; struct sockaddr_in srv; s = socket(AF_INET, SOCK_STREAM, 0); if (s == -1) { perror("socket failed()"); return 2; } srv.sin_addr.s_addr = inet_addr(DEFAULT_ADDR); srv.sin_port = htons(DEFAULT_PORT); srv.sin_family = AF_INET; if (connect(s, (const sockaddr*)&srv, sizeof(srv)) == -1) { perror("connect failed()"); return 3; } char buffer[] = "GET / HTTP/1.1\nHost: citrosaft\nConnection: close\n\n"; send(s, buffer, strlen(buffer), 0); if (handling(s) == -1) { fprintf(stderr, "%s: error in handling()\n", argv[0]); return 4; } close(s); return 0; }
-
Deine recv-Schleife sieht doch etwas sonderbar aus. Was passiert denn, wenn die Verbindung abgebaut ist? Dann ergibt recv -1, und du packst an buffer[-1] eine 0 und erwartest, dass da was sinnvolles passiert. Unwahrscheinlich. Letztendlich fehlt auch noch das flush beim cout, deshalb wirst du wohl keine Ausgabe bekommen.
-
hallo !
butterbrei schrieb:
Klappt alles wunderbar, jedoch verschluckt der mir den letzten Tag und zwar </html>
der server beendet die verbindung, nachdem alle daten gesendet worden sind und schickt zum schluss ne 0.
in deinem puffer steht noch der rest der antwort drin und du machst den
mitbuffer[0] = '\0';dicht.
wenn also </html> fehlt, dann sollte im puffer '\0'/html> drin stehen.
wo ist eigentlich die abbruchbedingung in deiner schleife ?

-
Hm... das klingt ja alles logisch.
Aber ich habe noch keinen Lösungsansatz.
Ich hab die Schleife einmal auf diese hier abgeändert (bloss eine Zeile ist neu und eine ist weniger bzw auskommentiert):while (true) { bytes = recv(sock, buffer, sizeof(buffer) - 1, 0); if (bytes == -1) break; buffer[bytes] = '\0'; cout << buffer; //buffer[0] = '\0'; }Ergebniss: Das selbe wie vorhin.
//edit:
Wenn ich es so mache:int handling(int sock) { char buffer[BUFFER_SIZE]; int bytes; string ttmp; while (true) { bytes = recv(sock, buffer, sizeof(buffer) - 1, 0); if (bytes <= 0) break; buffer[bytes] = '\0'; ttmp += buffer; //cout << buffer; //buffer[0] = '\0'; } /*tmp.open("tmp.txt", ios::out); if (tmp.is_open()) { tmp << buffer; }*/ cout << ttmp; return 0; }Dann funktioniert es. Aber warum? Und ist es gut, wenn man das so macht? Also das ganze in einem String aufblähen?
//edit2:
Jetzt versteh ich das erst! Aber ich dachte recv übergibt die bytes? Wenn recv nun aber noch etwas enthält, kann es doch nicht 0 sein. Warum wird dann </html> nicht angezeigt?//edit3 und der letzte für heute:
Wenn ich kein break mach, sondern nur continue, dann tritt das ganze auf.
gn8
-
Probier mal so:
while (true) { bytes = recv(sock, buffer, sizeof(buffer)-1, 0); if (bytes == 0) break; // Verbindung wurde regulär geschlossen else if (bytes < 0) { perror("recv"); exit(1); // irgendwas ist schiefgegangen. TODO: bessere Fehlerbehandlung } buffer[bytes] = 0; cout << buffer; } cout << flush;
-
butterbrei schrieb:
//edit2:
Jetzt versteh ich das erst! Aber ich dachte recv übergibt die bytes? Wenn recv nun aber noch etwas enthält, kann es doch nicht 0 sein. Warum wird dann </html> nicht angezeigt?sorry, da habe ich mist geschrieben.
ich habe deine erste schleife nochmal gecheckt, kompiliert.
die komplette seite kommt rüber. weiss nicht warum das bei dir nicht geklappt hat.butterbrei schrieb:
Dann funktioniert es. Aber warum? Und ist es gut, wenn man das so macht? Also das ganze in einem String aufblähen?
wenn du dir sicher bist, das dein arbeitsspeicher immer groß genug für die webseite( oder für was auch immer) ist.
aber wer guckt sich schon ne webseite über cout in der konsole an, ne.
für testzwecke mag das ja völlig ausreichen.
wo es mit sicherheit probleme geben wird: wenn du daten im binärformat empfängst, also bilder, mukke, videos und so.
denn die enthalten nullbytes. die anzeige wird dann zerhackt, bzw. wenn du den string speicherst, wird die datei zerstückelt und gekürzt.
darüber hinaus fügst du unnötig nullbytes ein, wo keine hingehören.wenn du allerdings nur textdateien empfangen willst, ist es egal.
ich habe bei meinem downloader den puffer bei jedem schleifendurchlauf in einer datei gespeichert, nachdem ich den
http-antwort-header rausgefiltert hatte. ( das wäre in deinem programm eventuell noch ein todo )... int bytes = 0; while( ( bytes = recv(sock, buffer, sizeof(buffer), 0) ) > 0 ) { fwrite(buffer, bytes, 1, fp); // hier vielleicht noch ne fehlerbehandlung rein. } if ( bytes ) // fehlerbehandlung ... ...edit: quellkot entfernt.

-
Okay!
Vielen Dank.
Mit dem hier:int handling(int sock) { char buffer[BUFFER_SIZE]; int bytes; string ttmp; ofstream tmp; FILE* fp; while (true) { bytes = recv(sock, buffer, sizeof(buffer) - 1, 0); if (bytes == 0) { continue; }else if (bytes < 0) { perror("recv"); exit(1); }else { buffer[bytes] = '\0'; fp = fopen("test.txt", "a"); fputs(buffer,fp); fclose(fp); } } return 0; }klappts.
Warum ich die das ganze wirklich in einer Endlosschleife laufen hab ist einfach: Ich möchte einfach immer lauschen, ob da nun was kommt oder nicht.
-
butterbrei schrieb:
Okay!
Vielen Dank.
Mit dem hier:if (bytes == 0) { continue;klappts.
Was soll das bringen? Wenn bytes == 0 ist, dann ist die Verbindung geschlossen. Und sie geht von selbst auch nicht wieder auf. Der Himmel weiß, was du unter "klappt" verstehst.
Warum ich die das ganze wirklich in einer Endlosschleife laufen hab ist einfach: Ich möchte einfach immer lauschen, ob da nun was kommt oder nicht.
Wie gesagt, wenn die Verbindung zu ist, ist sie zu. Da kannst du lange lauschen, AFAIK müsstest du von recv danach -1 bekommen.
-
Hmm... stimmt.
Eine andere Frage noch. Wenn ich kein 0-Byte am ende hinschreibe, dann wird ja irgendwie zum nächsten 0-Byte gelesen, also über die Grenzen hinaus oder so. Zumindest kamen bei mir, wenn ich das 0-Byte weglass, immer wieder komische Zeichen.
Also beispielsweise war das Ende</html>und hier noch ein paar wenige komische ZeichenWie kann ich das umgehen, damit ich auch binäre Daten ohne Probleme laden kann.
-
butterbrei schrieb:
Wie kann ich das umgehen, damit ich auch binäre Daten ohne Probleme laden kann.
- kein fputs fürs speichern benutzen, eher sowas hier:
fwrite(buffer, bytes, 1, fp);oder ein c++ äquivalent.
die variable bytes hat ja immer die übertragene anzahl der bytes, die im puffer sind, gespeichert.- keine strings anhängen in der form: ttmp += buffer;
in buffer kann in etwa so etwas drin stehen:
abc\0defbei einem fputs oder einem ttmp += buffer würdest du einen teil der daten verlieren, nämlich das "def"
-
Tatsächlich... Danke
achja man muss wenn dann schreiben
fwrite(buffer, 1, bytes, fp);
-
butterbrei schrieb:
Tatsächlich... Danke
achja man muss wenn dann schreibenfwrite(buffer, 1, bytes, fp);
achja ? muss ? nöö.

-
Echt? Warum?
-
Weil dann size = bytes ist, der count = 1 mal geschrieben wird, das ist das gleiche in grün.
Die Blockgröße ist bytes, die einmal geschrieben wird.
Wie würdest du denn die fwrite-Parameter beim Speichern von z.B. 300 Strukturblöcken schreiben ?struct myStruct { int i; }; struct myStruct many_structs[300]; fwrite( many_structs, ???, ???, fp );