problem mit http - recv
-
hi leute
ich hab ein problem mit dem recv von einem http server ...
ich schicke ein GET dann überprüfe ich mit recv ob was angekommen ist ... das ist der fall ... dann schicke ich ein zweites GET, und noch ein recv .... doch nun kommt nix mehr an ... wireshark bestätigt das .... bei follow tcp stream kommt immer nur get - die antwort - get und dann keine antwort ....
ich nutze c++ und linux, hab mich an die anleitung ( http://www.c-plusplus.net/forum/viewtopic-var-t-is-169861.html ) gehalten und das programm leicht abgeändert
#include <iostream> #include <fstream> #include <stdexcept> // runtime_error #include <sstream> #include <sys/socket.h> // socket(), connect() #include <arpa/inet.h> // sockaddr_in #include <netdb.h> // gethostbyname(), hostent #include <errno.h> // errno std::runtime_error CreateSocketError() { std::ostringstream temp; temp << "Socket-Fehler #" << errno << ": " ;//<< strerror(errno); return std::runtime_error(temp.str()); } void SendAll(int socket, const char* const buf, const int size) { int bytesSent = 0; // Anzahl Bytes die wir bereits vom Buffer gesendet haben do { int result = send(socket, buf + bytesSent, size - bytesSent, 0); if(result < 0) // Wenn send einen Wert < 0 zurück gibt deutet dies auf einen Fehler hin. { throw CreateSocketError(); } bytesSent += result; } while(bytesSent < size); } std::string globalBuffer; // Globale Variable, nicht nachmachen! // Liest eine Zeile des Sockets in einen stringstream void GetLine(int socket, std::stringstream& line) { char buf[1024]; int recvSize; std::string::size_type pos; while((pos = globalBuffer.find('\n')) == std::string::npos) { if((recvSize = recv(socket, buf, sizeof(buf), 0)) <= 0) { throw CreateSocketError(); } globalBuffer.append(buf, recvSize); } line << globalBuffer.substr(0, pos); globalBuffer.erase(0, pos + 1); } int Recv(int socket, char *buf, int len, unsigned int flags) // Eigene Recv-Funktion { if(!globalBuffer.empty()) { int copySize = len <= globalBuffer.size() ? len : globalBuffer.size(); globalBuffer.copy(reinterpret_cast<char*>(buf), copySize); globalBuffer.erase(0, copySize); return copySize; } else { return recv(socket, buf, len, flags); } } // Entfernt das http:// vor dem URL void RemoveHttp(std::string& URL) { size_t pos = URL.find("http://"); if(pos != std::string::npos) { URL.erase(0, 7); } } // Gibt die Dateiendung im URL zurück std::string GetFileEnding(std::string& URL) { using namespace std; size_t pos = URL.rfind("."); if(pos == string::npos) { return ""; } URL.erase(0, pos); string ending = "."; // Algorithmus um Sachen wie ?index=home nicht zuzulassen for(string::iterator it = URL.begin() + 1; it != URL.end(); ++it) { if(isalpha(*it)) { ending += *it; } else { break; } } return ending; } // Gibt den Hostnamen zurück und entfernt ihn aus der URL, sodass nur noch der Pfad übrigbleibt std::string RemoveHostname(std::string& URL) { size_t pos = URL.find("/"); if(pos == std::string::npos) { std::string temp = URL; URL = "/"; return temp; } std::string temp = URL.substr(0, pos); URL.erase(0, pos); return temp; } int main() { using namespace std; cout << "URL: "; string URL="www.google.de"; //cin >> URL; // User gibt URL der Datei ein, die herruntergeladen werden soll RemoveHttp(URL); string hostname = RemoveHostname(URL); hostent* phe = gethostbyname(hostname.c_str()); if(phe == NULL) { cout << "Host konnte nicht aufgeloest werden!" << endl; return 1; } if(phe->h_addrtype != AF_INET) { cout << "Ungueltiger Adresstyp!" << endl; return 1; } if(phe->h_length != 4) { cout << "Ungueltiger IP-Typ!" << endl; return 1; } int Socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if(Socket == -1) { cout << "Socket konnte nicht erstellt werden!" << endl; return 1; } sockaddr_in service; service.sin_family = AF_INET; service.sin_port = htons(80); // Das HTTP-Protokoll benutzt Port 80 char** p = phe->h_addr_list; // p mit erstem Listenelement initialisieren int result; // Ergebnis von connect do { if(*p == NULL) // Ende der Liste { cout << "Verbindung fehlgschlagen!" << endl; return 1; } service.sin_addr.s_addr = *reinterpret_cast<unsigned long*>(*p); ++p; result = connect(Socket, reinterpret_cast<sockaddr*>(&service), sizeof(service)); } while(result == -1); cout << "Verbindung erfolgreich!" << endl; string request = "GET "; request += URL; // z.B. /faq/index.html request += " HTTP/1.1\n"; request += "Host: " + hostname + "\nConnection: close\n\n"; cout << request << endl; try { cout << "1.Send\n"; SendAll(Socket, request.c_str(), request.size()); int code = 100; // 100 = Continue string Protokoll; stringstream firstLine; // Die erste Linie ist anders aufgebaut als der Rest while(code == 100) { GetLine(Socket, firstLine); firstLine >> Protokoll; firstLine >> code; if(code == 100) { GetLine(Socket, firstLine); // Leere Zeile nach Continue ignorieren } } cout << "Protokoll: " << Protokoll << endl; sleep(5); cout << "2.Send\n"; SendAll(Socket, request.c_str(), request.size()); code = 100; // 100 = Continue while(code == 100) { GetLine(Socket, firstLine); firstLine >> Protokoll; firstLine >> code; if(code == 100) { GetLine(Socket, firstLine); // Leere Zeile nach Continue ignorieren } } cout << "Protokoll: " << Protokoll << endl; if(code != 200) { firstLine.ignore(); // Leerzeichen nach dem Statuscode ignorieren string msg; getline(firstLine, msg); cout << "Error #" << code << " - " << msg << endl; return 0; } bool chunked = false; const int noSizeGiven = -1; int size = noSizeGiven; while(true) { stringstream sstream; GetLine(Socket, sstream); if(sstream.str() == "\r") // Header zu Ende? { break; } string left; // Das was links steht sstream >> left; sstream.ignore(); // ignoriert Leerzeichen if(left == "Content-Length:") { sstream >> size; } if(left == "Transfer-Encoding:") { string transferEncoding; sstream >> transferEncoding; if(transferEncoding == "chunked") { chunked = true; } } } string filename = "download" + GetFileEnding(URL); cout << "Filename: " << filename << endl; fstream fout(filename.c_str(), ios::binary | ios::out); if(!fout) { cout << "Could Not Create File!" << endl; return 1; } int recvSize = 0; // Empfangene Bytes insgesamt char buf[1024]; int bytesRecv = -1; // Empfangene Bytes des letzten recv if(size != noSizeGiven) // Wenn die Größe über Content-length gegeben wurde { cout << "0%"; while(recvSize < size) { if((bytesRecv = Recv(Socket, buf, sizeof(buf), 0)) <= 0) { throw CreateSocketError(); } recvSize += bytesRecv; fout.write(buf, bytesRecv); cout << "\r" << recvSize * 100 / size << "%" << flush; // Mit \r springen wir an den Anfang der Zeile } } else { if(!chunked) { cout << "Downloading... (Unknown Filesize)" << endl; while(bytesRecv != 0) // Wenn recv 0 zurück gibt, wurde die Verbindung beendet { if((bytesRecv = Recv(Socket, buf, sizeof(buf), 0)) < 0) { throw CreateSocketError(); } fout.write(buf, bytesRecv); } } else { cout << "Downloading... (Chunked)" << endl; while(true) { stringstream sstream; GetLine(Socket, sstream); int chunkSize = -1; sstream >> hex >> chunkSize; // Größe des nächsten Parts einlesen if(chunkSize <= 0) { break; } cout << "Downloading Part (" << chunkSize << " Bytes)... " << endl; recvSize = 0; // Vor jeder Schleife wieder auf 0 setzen while(recvSize < chunkSize) { int bytesToRecv = chunkSize - recvSize; if((bytesRecv = Recv(Socket, buf, bytesToRecv > sizeof(buf) ? sizeof(buf) : bytesToRecv, 0)) <= 0) { throw CreateSocketError(); } recvSize += bytesRecv; fout.write(buf, bytesRecv); cout << "\r" << recvSize * 100 / chunkSize << "%" << flush; } cout << endl; for(int i = 0; i < 2; ++i) { char temp; recv(Socket, &temp, 1, 0); } } } } cout << endl << "Finished!" << endl; } catch(exception& e) { cout << endl; cerr << e.what() << endl; } close(Socket); // Verbindung beenden }
sry ich hab leider kein spoiler tag entdeckt ... beachtet bitte in der main funktion das senden und empfangen
was kann das problem sein? liegt es am source code oder sende ich nur ein falsches GET request?
und sry wenn ich in eine falsche kategorie gepostet habe ... aber ich nehme an dass der fehler tatsächlich an dem GET request liegt ....
also vielen dank fürs helfen
hano
-
Du schickst ein "Connection: Close", womit der Server die Verbindung schließt. Also entweder neue Verbindung aufbauen, oder "Connection: keep-alive" angeben. Ach, und auch in Requests werden Zeilen mit "\r\n" statt nur "\n" angeschlossen. Siehe auch http://www.jmarshall.com/easy/http/
-
oh sehr gut danke
das hat geholfen ... jedenfalls an diesem beispiel ....nur ich hab versucht das zu abstrahieren mit klassen .... sende aber das selbe get request ... und trotzdem tritt das selbe problem auf ... obwohl ich ein keep alive sende ...
void inet::sendGet() { cout << "inet::sendGet\n"; string req; req+="GET "; req+=zurl->getPfad(); req+=" HTTP/1.1\r\nHost: "; req+=zurl->getHostname(); req+="\r\nConnection: keep-alive\r\n\r\n"; cout << req << endl; sendAll(&req); } void inet::sendAll(string *tosend) { cout << "inet::sendAll\n"; unsigned int bytesSent = 0; // Anzahl Bytes die wir bereits vom Buffer gesendet haben do { int res = send(Socket, tosend->c_str() + bytesSent, tosend->length() - bytesSent, 0); if (res < 0) // Wenn send einen Wert < 0 zurück gibt deutet dies auf einen Fehler hin. { cout << "Fehler in inet::sendAll!\n"; } bytesSent += res; } while (bytesSent < tosend->length()); } void inet::getAll(string *toget) { cout << "inet::getAll\n"; int ret=1; char buf[2048]; while (ret>0) { ret=recv(Socket,&buf,sizeof(buf),0); if (ret<=0) { break; } toget->append(buf,ret); } cout << toget->length() << " Bytes empfangen\n"; if (ret<0) { cout << "Fehler in inet::getAll!\n"; return; } cout << "Dateien erfolgreich empfangen.\n"; }
das problem sollte damit nicht mehr das gesendete paket sein, sondern eigentlich die abstrahierte recv-funktion ..... nur ich komm nicht auf den fehler ....
vielen vielen dank für eure hilfe
hano
-
Hm, kann es sein, dass dein Code aus der Empfangs-Schleife gar nicht mehr rauskommt? Das ist bei HTTP leider etwas schwierig, eigentlich müsstest du den Antwort-Header parsen um zu wissen, wieviel du insgesamt empfangen musst. Und nutzt du blockierende oder nicht-blockierende Sockets? Und wäre für dich vielleicht eine Option, eine (wenigstens etwas) abstrahierende Bibliothek zu nutzen, z.B. SFML?
edit: Ja, ich denke um's Parsen kommst du nicht drumherum. Ich bastele zur Zeit nebenbei auch mit HTTP rum, ich kann gleich mal skizzieren, wie ich das Parsen von der Struktur gelöst hab, wenn du magst
-
ok es scheint wohl so als müsste man dabei tatsächlich immer den header parsen ... das ist ein komischer fehler ... ich schaus mir morgen nochmal an, vielleicht ergibt sich ja mehr ...
hmm ich hab schon drüber nachgedacht eine fertige library zu nehmen (libcurl) aber ich wollte eigentlich gerade diese abstraktionsklasse selber schreiben ... sozusagen nur auf den systemfunktionen aufbauend ....
also nochmals vielen herzlichen dank
echt klasse hilfe
mfg hano
-
Ich bastele zur Zeit nebenbei auch mit HTTP rum, ich kann gleich mal skizzieren, wie ich das Parsen von der Struktur gelöst hab, wenn du magst
zeig mal bitte
-
Ohne den code jetzt untersucht zu haben, geht man grundsätzlich so vor:
- finde \r\n\r\n um das header-ende bestimmen zu können
- teile in header und data
- parse header um bestimmen was für Daten gesendet wurden: Content-Length, Chunked oder "HTTP 1.0 Standard"
- behandle die Daten dementsprechend in einer Schleife bis
entweder alle Bytes empfangen wurden (Content-Length oder Chunked)
oder 0 Bytes gesendet wurden (gilt für alle 3 Transfertypen, da so einige server mitunter auch fehlerhaft programmiert sind)