Socket geschlossen nach recv
-
Hallo Leute,
ich wollte mir in C++ eine HTTP-Request-Klasse schreiben, mit welcher ich mir eine Website von einem Server abholen kann.Ich poste es hier unter "C++", da ich finde, dass es nicht direkt was mit der WinAPI und / bzw. mit einer Konsole zu tun hat.
Falls das dennoch falsch ist, bitte ich darum, den Thread zu verschieben und nicht zu löschen.
Code-Ausschnitte:
int connect() { this->sock=0; #ifndef linux //Nur für Windoof WSADATA w; if(WSAStartup(MAKEWORD(2,2), &w) != 0) { //"Winsock 2 konnte nicht gestartet werden! return 1; } #endif this->sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(this->sock == -1) { //"Socket konnte nicht erstellt werden!" return 1; } this->service.sin_family = AF_INET; // AF_INET für IPv4, für IPv6 wäre es AF_INET6 this->service.sin_port = htons(this->port); this->service.sin_addr.s_addr = inet_addr(this->ip.c_str()); if(int con = connect(this->sock, reinterpret_cast<sockaddr*>(&this->service), sizeof(this->service))>=0) { (void*) new HTTP_Request(this->sock,this->ip,this->page); (void*) new HTTP_Request(this->sock,this->ip,this->page); //!!! HIER GEHT ES NICHT MEHR!!! return 0; } else return 1; }Hier wird für Windows, welche ich zuzeit nutze, WSA gestartet, der Socket erstellt und zum Server verbunden und danach mein HTTP-Request durchgeführt.
Code-Ausschnitt
HTTP_Request::HTTP_Request(int sock,string ip,string page) { //ctor string http_header= "GET "; http_header+=page; http_header+=" HTTP/1.1\r\nHost: "; http_header+=ip; http_header+="\r\n\r\n"; const char* request = http_header.c_str(); string response =""; int size= strlen(request); char tmp; int bytesSent = 0; // Anzahl Bytes die wir bereits vom Buffer gesendet haben do { bytesSent += send(sock, request + bytesSent, size - bytesSent, 0); // cout << bytesSent; } while(bytesSent < size); while (recv(sock, &tmp, 1, 0) > 0) { response+=tmp; } delete this; }Hier wird die GET-Anfrage an den Server geschickt und danach die Antwort wieder abgeholt.
Soweit so gut. Das funktioniert auch wunderbar, jedoch wird der zweite Request nicht durchgeführt. Zumindest bekomme ich keine Antwort.
Es funktionierte bei mir nur, wenn ich nach jedem Request den Socket geschlossen und danach wieder neu erstellt habe, jedoch leidet das total unter der Performance...
Meine Frage lautet:
1. Wird der Socket nach jedem send() & recv() geschlossen?
2. Weshalb kann ich den weiten Request nicht durchführen.
3. Wie kann ich mehrere Requests durchführen, ohne den Socket immer wieder neu erstellen zu müssen?Ich wäre sehr dankbar, wenn mir jemand weiterhelfen könnte.
Gruß
c++--
-
Warum machst du im Ctor ein delete this;? Das ist schon mal Käse.
-
314159265358979 schrieb:
Warum machst du im Ctor ein delete this;? Das ist schon mal Käse.
Ich wollte das Request-Object nach dem Request löschen lassen.
Aber das ist ja nicht das Problem, die Zeile rauszunehmen.Meine Probleme sind eher die Fragen, welche ich oben postete.
Gruß
-
Ich würde dir empfehlen, eine Bibliothek dafür zu verwenden. Da wäre z.B. curl oder für Sockets alleine boost::asio zu nennen. Die Frickelei mit der C-API ist halt ziemlich fehleranfällig.
-
c++-- schrieb:
1. Wird der Socket nach jedem send() & recv() geschlossen?
Nein.
c++-- schrieb:
2. Weshalb kann ich den weiten Request nicht durchführen.
Vermutlich trennt der Server die Verbindung. Das kannst du ja schon an der Bedingung der while-Schleife erkennen. recv() würde blockieren, wenn der Server die Verbindung nicht schließen würde.
c++-- schrieb:
3. Wie kann ich mehrere Requests durchführen, ohne den Socket immer wieder neu erstellen zu müssen?
Eventuell kannst du da am HTTP-Protokoll was drehen. connection: Keep-Alive oder so etwas in der Art. Aber Performanceeinbußen? Was willst du denn da programmieren?
Edit:
Ich sehe, du nutzt HTTP/1.1? Da sollten alle Verbindungen automatisch "keep-alive" sein. Doch deine Schleife wartet halt so lange, bis der Server die Verbindung schließt. Deshalb auch die Performanceprobleme bei neuen Verbindungen, du musst jedes mal auf ein ~15 Sekunden timeout warten. Nutze HTTP/1.0 oder überprüfe beim Empfangen ob du alles hast.
-
314159265358979 schrieb:
Ich würde dir empfehlen, eine Bibliothek dafür zu verwenden. Da wäre z.B. curl oder für Sockets alleine boost::asio zu nennen. Die Frickelei mit der C-API ist halt ziemlich fehleranfällig.
Hey,
sowas hatte ich mir auch schon überlegt, aber ich wollte mir eine eigene Klasse dazu schreiben.
Und jetzt einfach aufzuhören und eine fertige Bibliothek zu verwenden, bringt in meinen Augen wenig, da ich dann ja nichts dazu lerne und nicht weiß, wo/wie mein Fehler war.Gruß
-
cooky451 schrieb:
c++-- schrieb:
1. Wird der Socket nach jedem send() & recv() geschlossen?
Nein.
c++-- schrieb:
2. Weshalb kann ich den weiten Request nicht durchführen.
Vermutlich trennt der Server die Verbindung. Das kannst du ja schon an der Bedingung der while-Schleife erkennen. recv() würde blockieren, wenn der Server die Verbindung nicht schließen würde.
c++-- schrieb:
3. Wie kann ich mehrere Requests durchführen, ohne den Socket immer wieder neu erstellen zu müssen?
Eventuell kannst du da am HTTP-Protokoll was drehen. connection: Keep-Alive oder so etwas in der Art. Aber Performanceeinbußen? Was willst du denn da programmieren?
Danke für die Antworten!
Mir fällt da gerade ein, das im Antwort-Header sowas in der Art stand:
"Connection: close".Das würde mir 3tens beantworten. Das werde ich ausprobieren, Danke!
Wenn ich das Programm starte, dauert es erstmal so 3-5 Sekunden bevor der Request gesendet wird. Ich vermute dass WSA-Startup oder das connect() eine Weile braucht, weil dieser Nebeneffekt auch auftrat, als ich den Socket immer wieder neu erstellte.
Ich dachte noch nicht an ein konkretes Programm, sondern nur an eine Klasse, womit ich mir eine Website abholen kann, z.B. für einen HTTP-Chat.
Gruß
EDIT:
Connection
Der Connection-Header gibt an, ob die Verbindung nach der Transaktion aufrecht erhalten werden soll (keep-alive) oder nicht (close). Bei HTTP 1.0 werden Verbindungen standardmäßig geschlossen. Soll die Verbindung dennoch geöffnet bleiben, muss keep-alive als Wert angegeben werden. HTTP 1.1 verwendet jedoch persistente (aufrechterhaltende) Verbindungen. Damit die Verbindung hier dennoch geschlossen wird, muss close als Wert angegeben werden. Beispiel:
Connection: keep-alive
-
Der Teil, der wohl am längsten dauert, ist das resolven des Hosts. Ich würde das in eine eigene Funktion auslagern und im Konstruktor nur irgend ne IP-Klasse als Parameter akzeptieren.
-
314159265358979 schrieb:
Der Teil, der wohl am längsten dauert, ist das resolven des Hosts. Ich würde das in eine eigene Funktion auslagern und im Konstruktor nur irgend ne IP-Klasse als Parameter akzeptieren.
Ich übergebe in meinen Programm vorerst nur die IP-Adresse und nutze dann
inet_addr();Oder wie meinst du das jetzt?
GRuß
-
c++-- schrieb:
Oder wie meinst du das jetzt?
Er hat wohl an gethostbyname() und Konsorten gedacht, die eine DNS-Anfrage machen müssen.
Deinen Beitrag oben zu meinem Kommentar verstehe ich nicht ganz, hast du das Problem jetzt gelöst?
-
cooky451 schrieb:
c++-- schrieb:
Oder wie meinst du das jetzt?
Er hat wohl an gethostbyname() und Konsorten gedacht, die eine DNS-Anfrage machen müssen.
Deinen Beitrag oben zu meinem Kommentar verstehe ich nicht ganz, hast du das Problem jetzt gelöst?Nicht ganz

Also Antwort bekomme ich jetzt:
HTTP/1.1 200 OK Date: Thu, 08 Sep 2011 18:49:15 GMT Server: Apache/2.2.17 (Win32) PHP/5.3.5 X-Powered-By: PHP/5.3.5 Expires: Mon, 26 Jul 1997 05:00:00 GMT Last-Modified: Thu, 08 Sep 2011 18:49:15 GMT Cache-Control: no-store, no-cache, must-revalidate Cache-Control: post-check=0, pre-check=0 Pragma: no-cache Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Transfer-Encoding: chunked Content-Type: text/htmlJedoch wird das 2te HTTP-Request dann wieder nicht ausgeführt

Noch eine Idee?
GRuß
-
c++-- schrieb:
Noch eine Idee?
Nein, aber meine alten Ideen gelten weiterhin. Solange du eine Anfrage auf HTTP/1.1 machst, wird die Verbindung wohl am Leben gehalten. Da deine Schleife solange empfängt, bis die Verbindung geschlossen wird, musst du jedes Mal auf das Timeout warten.
Vermutlich ist die beste Lösung für dich einfach HTTP/1.0 zu nutzen. Da kannst du dann so wie jetzt empfangen.
-
cooky451 schrieb:
c++-- schrieb:
Noch eine Idee?
Nein, aber meine alten Ideen gelten weiterhin. Solange du eine Anfrage auf HTTP/1.1 machst, wird die Verbindung wohl am Leben gehalten. Da deine Schleife solange empfängt, bis die Verbindung geschlossen wird, musst du jedes Mal auf das Timeout warten.
Vermutlich ist die beste Lösung für dich einfach HTTP/1.0 zu nutzen. Da kannst du dann so wie jetzt empfangen.Ok. Ich habe jetzt die "Connection: close" gesetzt.
Damit ist nach einem Request schluss.Ich dachte mir jetzt, wenn die Verbindung geschlossen wird, dann müsste ich doch per "connect()" diese wieder aufbauen können und einen neuen Request absetzen, oder?
(void*) new HTTP_Request(this->sock,this->ip,this->page); connect(this->sock, reinterpret_cast<sockaddr*>(&this->service), sizeof(this->service)); (void*) new HTTP_Request(this->sock,this->ip,this->page);Jedoch funktioniert das genauso wenig?!
GRuß
-
Tja.. den Socket musst du wahrscheinlich schließen und dann einen neuen erstellen.
(Und nur so am Rande.. was ist das für eine komische Aktion mit dem "(void*) new HTTP_Request" und dem delete im Konstruktor? Bitte ändere das! :))
-
cooky451 schrieb:
Tja.. den Socket musst du wahrscheinlich schließen und dann einen neuen erstellen.
(Und nur so am Rande.. was ist das für eine komische Aktion mit dem "(void*) new HTTP_Request" und dem delete im Konstruktor? Bitte ändere das! :))
Ok.
Das habe ich bereits geändert.Gruß
-
So, es funktioniert jetzte!
Ich danke allen!
Gruß und schönes Wochenende noch.