Browse Source

"einfacher" Download einer HTML-Datei

lookup-tables
Stephan Richter 7 years ago
parent
commit
f4016ad106
  1. 217
      src/main.cpp

217
src/main.cpp

@ -1,16 +1,100 @@ @@ -1,16 +1,100 @@
#include <iostream>
#include <fstream>
#include <stdexcept> // runtime_error
#include <sstream>
#include <cstring>
#include <unistd.h>
#ifdef linux
#include <sys/socket.h> // socket(), connect()
#include <arpa/inet.h> // sockaddr_in
#include <netdb.h> // gethostbyname(), hostent
#include <arpa/inet.h> // inet_ntoa()
#include <errno.h> // errno
#else
#include <winsock2.h>
#endif
// erzeugt eine Exception, verwendet dabei die globale Variable errno
std::runtime_error CreateSocketError(){
std::ostringstream temp;
#ifdef linux
temp << "Socket-Fehler #" << errno << ": " << strerror(errno);
#else
int error = WSAGetLastError();
temp << "Socket-Fehler #" << error;
char* msg;
if(FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),reinterpret_cast<char*>(&msg), 0, NULL)){
try{
temp << ": " << msg;
LocalFree(msg);
}catch(...){
LocalFree(msg);
throw;
}
}
#endif
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) throw CreateSocketError();
bytesSent += result;
} while(bytesSent < size);
}
// Liest eine Zeile des Sockets in einen stringstream
void GetLine(int socket, std::stringstream &line){
for(char c; recv(socket, &c, 1, 0) > 0; line << c){
if(c == '\n') return;
}
throw CreateSocketError();
}
// 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;
cin >> url; // User gibt URL der Datei ein, die herruntergeladen werden soll
#ifndef linux
WSADATA w;
if(int result = WSAStartup(MAKEWORD(2,2), &w) != 0){
@ -19,33 +103,28 @@ int main(){ @@ -19,33 +103,28 @@ int main(){
}
#endif
cout << "Bitte gebe einen Hostnamen ein: ";
string hostname;
cin >> hostname;
RemoveHttp(url);
string hostname = RemoveHostname(url);
hostent *phe = gethostbyname(hostname.c_str());
hostent* phe = gethostbyname(hostname.c_str());
if(phe == NULL){
cout << "Host konnte nicht aufgeloest werden!" << endl;
cout << "Host konnte nicht aufgelöst werden!" << endl;
return 1;
}
cout << "\nHostname: " << phe->h_name << endl << "Aliases: ";
for(char** p = phe->h_aliases; *p != 0; ++p) cout << *p << " ";
cout << endl;
if(phe->h_addrtype != AF_INET){
cout << "Ungueltiger Adresstyp!" << endl;
cout << "Ungültiger Adresstyp!" << endl;
return 1;
}
if(phe->h_length != 4){
cout << "Ungueltiger IP-Typ!" << endl;
cout << "Ungültiger IP-Typ!" << endl;
return 1;
}
int Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
int Socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(Socket == -1){
cout << "Socket konnte nicht erstellt werden!" << endl;
return 1;
@ -57,7 +136,7 @@ int main(){ @@ -57,7 +136,7 @@ int main(){
char** p = phe->h_addr_list; // p mit erstem Listenelement initialisieren
int result; // Ergebnis von connect
do {
do{
if(*p == NULL) {
cout << "Verbindung fehlgschlagen!" << endl;
return 1;
@ -71,9 +150,113 @@ int main(){ @@ -71,9 +150,113 @@ int main(){
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";
try{
SendAll(Socket, request.c_str(), request.size());
int code;
string Protokoll;
stringstream firstLine; // Die erste Linie ist anders aufgebaut als der Rest
do{
GetLine(Socket, firstLine);
firstLine >> Protokoll;
firstLine >> code;
// 100 = Continue
if(code == 100) GetLine(Socket, firstLine); // Leere Zeile nach Continue ignorieren
} while(code == 100);
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") break;// Header zu ende?
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;
chunked = (transferEncoding == "chunked");
}
}
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;
}
#ifdef linux
close(Socket);
close(Socket); // Verbindung beenden
#else
closesocket(Socket);
closesocket(Socket); // Windows-Variante
#endif
}

Loading…
Cancel
Save