"einfacher" Download einer HTML-Datei
This commit is contained in:
217
src/main.cpp
217
src/main.cpp
@@ -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(){
|
||||
}
|
||||
#endif
|
||||
|
||||
cout << "Bitte gebe einen Hostnamen ein: ";
|
||||
string hostname;
|
||||
cin >> hostname;
|
||||
RemoveHttp(url);
|
||||
|
||||
hostent *phe = gethostbyname(hostname.c_str());
|
||||
string hostname = RemoveHostname(url);
|
||||
|
||||
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(){
|
||||
|
||||
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(){
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user