Hypercell ein ] Hypercell aus ] Zeige Navigation ] Verstecke Navigation ]
c++.de  
   
Forentreff 2012     
Bücher-Shop mit Amazon (Buchkategorien)C++ : Referenzen zu C++ : C++ Builder : Visual C++ : C# : Java : Spieleprogrammierung : Systemprogrammierung Linux : Software-Entwicklung : .NET : Compilertechnik : Algorithmen & Datenstrukturen : Objektorientierung : Entwurfsmuster : UML : eXtreme Programming : Scrum : Projektmanagement : Software-Testing : Datenbanken : Tom DeMarco : Dilbert : User Friendly
C/C++ Forum :: Die Artikel ::  Sockets und das HTTP-Protokoll     Zeige alle Beiträge auf einer Seite Auf Beitrag antworten
Autor Nachricht
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 20:10:46 09.01.2007   Titel:   Sockets und das HTTP-Protokoll            Zitieren

1 Vorwort

1.1 Einleitung

Willkommen zu meinem ersten Tutorial, in dem ich erklären möchte, wie man Sockets unter Linux und Windows benutzt. Am Ende werden wir ein Programm geschaffen haben, welches, auf Eingabe einer URL, über das HTTP-Protokoll die entsprechende Datei herunterlädt und auf der Festplatte speichert. Sie merken, es geht in diesem Tutorial nur um das Verbinden, also fangen Sie am Besten nicht an mit lesen, wenn Sie eine Einführung in die Programmierung von Netzwerkspielen erwarten.

1.2 Vorausetzungen

Unter Windows benutze ich als IDE Code::Blocks (http://www.codeblocks.org) und den GNU-GCC-Compiler in der Version 3.4.4.
Unter Linux verwende ich gedit (http://www.gnome.org/projects/gedit/) und den GNU-GCC-Compilier in der Version 4.1.2.

Sie sollten C++-Kenntnisse in Exceptions, Stringstreams und Filestreams haben, außerdem ist Erfahrung mit Zeigern empfehlenswert.

1.3 Linken der benötigten Libs

Wenn Sie unter Linux sind, brauchen Sie gar nichts tun. Falls Sie aber unter Windows arbeiten, müssen Sie noch gegen die benötigte Winsock-Library linken. Der Name der Lib lautet libws2_32.a. Falls diese bei Ihnen nicht vorhanden ist, suchen Sie einfach eine beliebige heraus, die nach Sockets klingt und probieren Sie sie einfach aus. Sollte das nicht helfen, fragen Sie am besten im Compiler- oder WinAPI-Forum. Hier 2 Screenshots für das Hinzufügen mit Code::Blocks:


Abb. 1.3.1 Auswählen der Build-Options


Abb. 1.3.2 Eingeben der Winsock-Lib.

Sollten Sie Nutzer der MS VisualC++ IDE sein, müssen Sie die WS2_32.lib Bibliothek dem Linker bekannt geben. Dazu gehen Sie unter Projekt->Eigenschaften->Linker und tragen, wie im Screenshot zu sehen, die Library unter "Zusätzliche Abhängigkeiten" ein:


Abb. 1.3.3 Zusätzliche Abhängigkeiten


2 Das erste Socket-Programm

Unser erstes Programm soll einfach nur über den Port 80, welcher der standardmäßige Port für das HTTP-Protokoll ist, eine Verbindung aufbauen.
Zuerst müssen Sie die nötigen Headerdateien inkludieren:

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#ifdef
linux
#include
<sys/socket.h> // socket(), connect()
#include
<arpa/inet.h> // sockaddr_in
#else
#include
<winsock2.h>
#endif

int
main()
{
    using namespace std;
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#ifdef
linux
#include
<sys/socket.h> // socket(), connect()
#include
<arpa/inet.h> // sockaddr_in
#else
#include
<winsock2.h>
#endif

int
main()
{
using namespace std;
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#ifdef
linux
#include
<sys/socket.h> // socket(), connect()
#include
<arpa/inet.h> // sockaddr_in
#else
#include
<winsock2.h>
#endif

int
main()
{
    using namespace std;


Die Konstante linux wird von meinem Compiler automatisch definiert, wenn ich unter Linux bin. Nähere Infos: http://predef.sourceforge.net/preos.html
Sie können auch einfach nur den für Ihr Betriebssystem benötigten Code nehmen und den Rest verwerfen.
Die iostream-Headerdatei ist klar; für Windows müssen wir nur die winsock2.h inkludieren, dort sind alle Funktionen für die zweite Winsock-Version definiert. Bei Linux sind die einzelnen Sachen in verschiedene Dateien aufgeteilt.

Folgender Code ist nur für Windows wichtig:

C/C++ Code:
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
#ifndef linux
    WSADATA w;
    if(int result = WSAStartup(MAKEWORD(2,2), &w) != 0)
    {
        cout << "Winsock 2 konnte nicht gestartet werden! Error #" << result << endl;
        return 1;
    }
#endif
C/C++ Code:
1
2
3
4
5
6
7
8
#ifndef linux
WSADATA w;
if(int result = WSAStartup(MAKEWORD(2,2), &w) != 0)
{
cout << "Winsock 2 konnte nicht gestartet werden! Error #" << result << endl;
return 1;
}
#endif
C/C++ Code:
1
2
3
4
5
6
7
8
#ifndef linux
    WSADATA w;
    if(int result = WSAStartup(MAKEWORD(2,2), &w) != 0)
    {
        cout << "Winsock 2 konnte nicht gestartet werden! Error #" << result << endl;
        return 1;
    }
#endif


Mit der Funktion WSAStartup wird Windows mitgeteilt, dass man gerne Zugriff auf die Winsock-Library haben will. Die Parameter sind eigentlich unwichtig, falls diese Sie dennoch interessieren, können Sie auf der MSDN-Seite nachschauen, was sie bedeuten. Das einzige was wir uns merken müssen, ist dass jedes Winsock-Programm mit dieser Funktion starten sollte, bevor es irgendwelche anderen Socket-Funktionen aufruft.

Nun geht es weiter in der main-Funktion:

C/C++ Code:
    int Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(Socket == -1)
    {
        cout << "Socket konnte nicht erstellt werden!" << endl;
        return 1;
    }
C/C++ Code:
int Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(Socket == -1)
{
cout << "Socket konnte nicht erstellt werden!" << endl;
return 1;
}
C/C++ Code:
    int Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(Socket == -1)
    {
        cout << "Socket konnte nicht erstellt werden!" << endl;
        return 1;
    }


Die socket()-Funktion erstellt ein neues Socket, einen „Netzwerkanschluss“, und gibt dessen ID zurück. Mit dieser ID können wir später auf dem Socket Daten senden bzw. empfangen. Ein negativer Wert stellt hierbei einen Fehler beim Erstellen dar. Weitere Infos zu der socket-Funktion gibt es hier.

Hinweis: Als Windows-User werden Sie wahrscheinlich auf den Datentyp SOCKET treffen. Dieser ist ein typedef auf unsigned int. Trotzdem können Sie ein Socket einfach als int behandeln, da es eine implizite Umwandlung zwischen diesen Typen gibt.

Nachdem wir ein Socket erstellt haben, müssen wir eine Verbindung aufbauen.
Jede Verbindung hat verschiedene Parameter, die in einer Struktur namens sockaddr gespeichert sind. Sie müssen sich das so vorstellen, dass die sockaddr eine abstrakte Basisklasse ist (die nie benutzt wird) und Strukturen wie sockaddr_in sind die abgeleiteten Klassen.
(Dass man nicht gleich ein Klassendesign gewählt hat, liegt daran, dass Sockets auch unter C funktionieren sollen)
Wir benötigen nur die sockaddr_in, sie ist für die normalen, vierstelligen IP-Adressen zuständig (z.B. 192.168.114.100). Falls Sie sich mit dem neuen IPv6 beschäftigen wollen, benötigen Sie dann eine andere sockaddr-Struktur. Eine gute Übersicht gibt's hier.
Der erste Parameter dieser Strukturen ist immer der Typ der sockaddr-Struktur:

C/C++ Code:
    sockaddr_in service; // Normale IPv4 Struktur
    service.sin_family = AF_INET; // AF_INET für IPv4, für IPv6 wäre es AF_INET6
C/C++ Code:
sockaddr_in service; // Normale IPv4 Struktur
service.sin_family = AF_INET; // AF_INET für IPv4, für IPv6 wäre es AF_INET6
C/C++ Code:
    sockaddr_in service; // Normale IPv4 Struktur
    service.sin_family = AF_INET; // AF_INET für IPv4, für IPv6 wäre es AF_INET6


Als nächstes müssen wir einen Port festlegen, auf dem wir connecten wollen, dies wäre beim HTTP-Protokoll der Port 80. Hierbei ist zu beachten, dass die Struktur den Port in umgekehrter Bytereihenfolge speichert. Also zuerst das eigentlich hintere Byte von short und dann das vordere. Klingt komisch, ist aber eigentlich gar nicht so schwer, denn um eine normale Zahl in die umgekehrte Reihenfolge zu bringen, gibt es schon die Funktion htons(). Für genauere Infos suchen Sie im Internet nach big-endian.

C/C++ Code:
   service.sin_port = htons(80); // Das HTTP-Protokoll benutzt Port 80
C/C++ Code:
service.sin_port = htons(80); // Das HTTP-Protokoll benutzt Port 80
C/C++ Code:
   service.sin_port = htons(80); // Das HTTP-Protokoll benutzt Port 80


Falls Sie noch Probleme haben, das mit der Bytereihenfolge zu verstehen, gibt es hier ein kleines Beispiel für eine mögliche Implementierung dieser Funktion:

C/C++ Code:
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
unsigned short my_htons(unsigned short h)
{
    char* p = reinterpret_cast<char*>(&h);
    char n[2];
    n[0] = p[1];
    n[1] = p[0];
    return *reinterpret_cast<unsigned short*>(n);
}
C/C++ Code:
1
2
3
4
5
6
7
8
unsigned short my_htons(unsigned short h)
{
char* p = reinterpret_cast<char*>(&h);
char n[2];
n[0] = p[1];
n[1] = p[0];
return *reinterpret_cast<unsigned short*>(n);
}
C/C++ Code:
1
2
3
4
5
6
7
8
unsigned short my_htons(unsigned short h)
{
    char* p = reinterpret_cast<char*>(&h);
    char n[2];
    n[0] = p[1];
    n[1] = p[0];
    return *reinterpret_cast<unsigned short*>(n);
}


Falls Sie später htons durch my_htons ersetzen, sollte es genauso funktionieren.

Jetzt kommen wir zum Interessanten: Der IP. Bei unserem ersten Programm soll der User erstmal nur eine IP eingeben, zu der dann verbunden wird.

C/C++ Code:
    string ip;
    cout << "IP: ";
    cin >> ip;
C/C++ Code:
string ip;
cout << "IP: ";
cin >> ip;
C/C++ Code:
    string ip;
    cout << "IP: ";
    cin >> ip;


Nun wird es wieder etwas schwieriger. Wie Sie wissen, wird die IP in der Form 123.44.32.99 dargestellt. Die einzelnen vier Werte gehen von 0 bis 255, und da muss jedem Programmierer sofort auffallen, dass dies ein unsigned char ist. Also haben wir 4 unsigned chars die durch einen Punkt getrennt sind. Dies ist aber nur eine für Menschen leserlich gemachte Form. In Wirklichkeit speichert der Computer natürlich keinen String sondern die 4 Bytes direkt. Also haben wir ein 4 Byte großes Array aus unsigned char. Klingt logisch; macht Sie das nicht misstrauisch? Richtig! Natürlich speichert man kein Array, sondern einen unsigned long. Wäre vielleicht ja noch ganz logisch, wenn man diesen anstelle einer Struktur benutzt, aber nein, den packen wir den nochmal in eine Struktur rein.

C/C++ Code:
struct in_addr
{
    unsigned long s_addr; // long ist 4 bytes also 4 chars groß
};
C/C++ Code:
struct in_addr
{
unsigned long s_addr; // long ist 4 bytes also 4 chars groß
};
C/C++ Code:
struct in_addr
{
    unsigned long s_addr; // long ist 4 bytes also 4 chars groß
};


Dass dies sinnlos ist, haben sich die Entwickler bei Microsoft wohl auch gedacht. Also haben sie die in_addr bei Winsock neu entworfen. In deren Struktur gibt es vier unsigned chars, zwei unsigned shorts und einen unsigned long. Damit das ganze kompatibel ist, hat man diese Variablen in eine union gepackt. Somit lassen sich immernoch Casts von unsigned long* zu einem in_addr* durchführen, dazu aber später mehr. Wichtig ist: Vergessen Sie die Microsoft-Version der in_addr und stellen Sie sich einfach vor, die in_addr-Struktur enthält nur einen unsigned long, der die IP-Adresse in binärer Form speichert.

Da wir ja wollen, dass der User nicht die IP-Adresse binär eingibt, sondern in der gewohnten Form, müssen wir sie umwandeln. Zum Glück gibt es dafür auch schon eine Funktion namens inet_addr. Diese gibt einen unsigned long zurück, den wir dann einfach an den s_addr-Member der in_addr-Struktur übergeben. Dieser heißt in der sockaddr_in-Struktur sin_addr (Siehe Übersicht der sockaddr_in-Struktur).

C/C++ Code:
   service.sin_addr.s_addr = inet_addr(ip.c_str());
C/C++ Code:
service.sin_addr.s_addr = inet_addr(ip.c_str());
C/C++ Code:
   service.sin_addr.s_addr = inet_addr(ip.c_str());


Nun haben wir alle Informationen für eine Verbindung an die vom User eingegebene IP gespeichert. Es ist an der Zeit nun wirklich zu verbinden. Dazu gibt es die Funktion connect. Diese erwartet als ersten Parameter ein Socket, als zweiten einen Zeiger auf unsere sockaddr-Struktur, den wir natürlich casten müssen, da wir ja in Wirklichkeit eine sockaddr_in Struktur haben. Und als letztes folgt die Länge unserer sockaddr-Struktur. Die Länge muss übergeben werden, weil... äh.. ist eigentlich eine gute Frage, denn theoretisch, kann die connect-Funktion ja durch die sin_family herausfinden wie lang die bestimmte sockaddr-Struktur ist. Aber hier hat man sich anscheinend dazu entschieden, die Länge zur Sicherheit auch noch zu übergeben, vielleicht weil sie nicht plattformunabhängig ist.

C/C++ Code:
   int result = connect(Socket, reinterpret_cast<sockaddr*>(&service), sizeof(service));
C/C++ Code:
int result = connect(Socket, reinterpret_cast<sockaddr*>(&service), sizeof(service));
C/C++ Code:
   int result = connect(Socket, reinterpret_cast<sockaddr*>(&service), sizeof(service));


Der Rückgabewert von connect ist bei einem Fehler -1. Dies überprüfen wir und geben bekannt, falls die Verbindung fehlgeschlagen ist:

C/C++ Code:
    if(result == -1)
    {
        cout << "Verbindung fehlgeschlagen!" << endl;
        return 1;
    }

    cout << "Verbindung erfolgreich!" << endl;
C/C++ Code:
if(result == -1)
{
cout << "Verbindung fehlgeschlagen!" << endl;
return 1;
}

cout << "Verbindung erfolgreich!" << endl;
C/C++ Code:
    if(result == -1)
    {
        cout << "Verbindung fehlgeschlagen!" << endl;
        return 1;
    }

    cout << "Verbindung erfolgreich!" << endl;


An dieser Stelle haben wir jetzt eine Verbindung mit dem Server aufgebaut und könnten theoretisch Daten austauschen, das verschieben wir aber mal lieber auf das nächste Kapitel und beenden jetzt schon die Verbindung:

C/C++ Code:
#ifdef linux
    close(Socket);
#else

    closesocket(Socket);
#endif

}
C/C++ Code:
#ifdef linux
close(Socket);
#else

closesocket(Socket);
#endif

}
C/C++ Code:
#ifdef linux
    close(Socket);
#else

    closesocket(Socket);
#endif

}


Diese Funktion schließt das Socket, dessen Identifizierungsnummer hier übergeben wird. Unter Linux heißt sie close() und unter Windows closesocket(). Falls wir hier nach nochmal connect() oder eine andere Funktion aufrufen, die ein Socket erwartet, werden wir eine Fehlermeldung erhalten, denn diese ID zeigt nicht auf ein gültiges Socket. Wir beachten, dass die closesocket-Funktion threadsicher ist und alle blockenden Socket-Funktionen, die dieses Socket benutzen, stoppt. Auch wird ein bestimmter Status gesendet, sodass das Gegenüber weiß, dass die Verbindung geschlossen wurde. Also schrecken Sie nicht davor zurück, close zu benutzen.

Unser Programm sollte nun so aussehen. Nun können wir es kompilieren und es sollte keine Fehlermeldung erscheinen. Testen Sie eine IP, hinter der Sie einen Webserver wissen. Falls Ihnen keine einfällt, können Sie z.B. mit dem Konsolen-Befehl

Code:
nslookup www.google.de
Code:
nslookup www.google.de
Code:
nslookup www.google.de


die IP-Adresse von Google erfahren. (Die Konsole starten Sie unter Windows mit [Windowstaste]+[R] und dann „cmd“ ausführen.)
Die Verbindung sollte erfolgreich zustande kommen. Probieren Sie auch einfach irgendetwas beliebiges aus, denn nun sollte es eine Fehlermeldung geben. Eine Konsolenausgabe könnte z.B. so aussehen (Linux):

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
jhasse@jhasse-desktop:~/C++/http$ g++ 01.cpp
jhasse@jhasse-desktop:~/C++/http$ nslookup www.google.de
Server:         217.237.149.161
Address:        217.237.149.161#53

Non-authoritative answer:
www.google.de   canonical name = www.google.com.
www.google.com  canonical name = www.l.google.com.
Name:   www.l.google.com
Address: 209.85.129.104
Name:   www.l.google.com
Address: 209.85.129.147
Name:   www.l.google.com
Address: 209.85.129.99

jhasse@jhasse-desktop:~/C++/http$ ./a.out
IP: 209.85.129.104
Verbindung erfolgreich!
jhasse@jhasse-desktop:~/C++/http$ ./a.out
IP: 209.85.129.147
Verbindung erfolgreich!
jhasse@jhasse-desktop:~/C++/http$ ./a.out
IP: 209.85.129.99
Verbindung erfolgreich!
jhasse@jhasse-desktop:~/C++/http$ ./a.out
IP: 123.123.123.123
Verbindung fehlgeschlagen!
jhasse@jhasse-desktop:~/C++/http$
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
jhasse@jhasse-desktop:~/C++/http$ g++ 01.cpp
jhasse@jhasse-desktop:~/C++/http$ nslookup www.google.de
Server: 217.237.149.161
Address: 217.237.149.161#53

Non-authoritative answer:
www.google.de canonical name = www.google.com.
www.google.com canonical name = www.l.google.com.
Name: www.l.google.com
Address: 209.85.129.104
Name: www.l.google.com
Address: 209.85.129.147
Name: www.l.google.com
Address: 209.85.129.99

jhasse@jhasse-desktop:~/C++/http$ ./a.out
IP: 209.85.129.104
Verbindung erfolgreich!
jhasse@jhasse-desktop:~/C++/http$ ./a.out
IP: 209.85.129.147
Verbindung erfolgreich!
jhasse@jhasse-desktop:~/C++/http$ ./a.out
IP: 209.85.129.99
Verbindung erfolgreich!
jhasse@jhasse-desktop:~/C++/http$ ./a.out
IP: 123.123.123.123
Verbindung fehlgeschlagen!
jhasse@jhasse-desktop:~/C++/http$
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
jhasse@jhasse-desktop:~/C++/http$ g++ 01.cpp
jhasse@jhasse-desktop:~/C++/http$ nslookup www.google.de
Server:         217.237.149.161
Address:        217.237.149.161#53

Non-authoritative answer:
www.google.de   canonical name = www.google.com.
www.google.com  canonical name = www.l.google.com.
Name:   www.l.google.com
Address: 209.85.129.104
Name:   www.l.google.com
Address: 209.85.129.147
Name:   www.l.google.com
Address: 209.85.129.99

jhasse@jhasse-desktop:~/C++/http$ ./a.out
IP: 209.85.129.104
Verbindung erfolgreich!
jhasse@jhasse-desktop:~/C++/http$ ./a.out
IP: 209.85.129.147
Verbindung erfolgreich!
jhasse@jhasse-desktop:~/C++/http$ ./a.out
IP: 209.85.129.99
Verbindung erfolgreich!
jhasse@jhasse-desktop:~/C++/http$ ./a.out
IP: 123.123.123.123
Verbindung fehlgeschlagen!
jhasse@jhasse-desktop:~/C++/http$


Bis hierhin sollten Sie alles verstanden haben. Falls es dennoch Probleme gibt, schauen Sie noch mal in die Referenzen und anderen Tutorials (siehe Links am Ende).


3 Unser eigenes nslookup

Jetzt ist es natürlich sehr nervig, dass man immer eine IP eingeben muss. Es wäre doch viel praktischer, wenn man wie beim Browser einfach einen Namen eingibt und das Programm diesen automatisch auflöst. Also fangen wir an: Programmieren wir uns unser eigenes nslookup.

Die Funktion, die uns interessiert heißt gethostbyname und gibt einen Zeiger auf eine hostent-Struktur zurück. Diese wollen wir uns mal genauer anschauen:

C/C++ Code:
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
struct hostent
{
    char* h_name; /* Offizieller Name des Host */
    char** h_aliases; /* Weitere Namen für diesen Host */
    int h_addrtype; /* Adresstyp, meistens AF_INET */
    int h_length; /* Länge einer IP-Adresse in Bytes, meistens 4 */
    char** h_addr_list; /* Die IP-Adressen */
};
C/C++ Code:
1
2
3
4
5
6
7
8
struct hostent
{
char* h_name; /* Offizieller Name des Host */
char** h_aliases; /* Weitere Namen für diesen Host */
int h_addrtype; /* Adresstyp, meistens AF_INET */
int h_length; /* Länge einer IP-Adresse in Bytes, meistens 4 */
char** h_addr_list; /* Die IP-Adressen */
};
C/C++ Code:
1
2
3
4
5
6
7
8
struct hostent
{
    char* h_name; /* Offizieller Name des Host */
    char** h_aliases; /* Weitere Namen für diesen Host */
    int h_addrtype; /* Adresstyp, meistens AF_INET */
    int h_length; /* Länge einer IP-Adresse in Bytes, meistens 4 */
    char** h_addr_list; /* Die IP-Adressen */
};


Der erste Parameter ist ein C-Array, der den Namen des Host speichert. Dieser ist für uns später unwichtig genauso wie die weiteren Namen des Hosts. Da diese mehrere sein können, haben wir eine Liste aus Zeiger, bzw aus C-Arrays. Das wird jetzt sehr kompliziert, deswegen ein kurzes Beispiel:

Code:
char* = C-String
char** = C-String*
Code:
char* = C-String
char** = C-String*
Code:
char* = C-String
char** = C-String*


Wir haben also einen Zeiger auf einen C-String. Aber wozu einen Zeiger? Klar, weil es sich um ein dynamisches Array handelt:

Code:
Dynamisches Array in C:     T* p = (T)malloc(size);
Dynamisches Array in C++:     std::vector<T> v(size);
Code:
Dynamisches Array in C: T* p = (T)malloc(size);
Dynamisches Array in C++: std::vector<T> v(size);
Code:
Dynamisches Array in C:     T* p = (T)malloc(size);
Dynamisches Array in C++:     std::vector<T> v(size);


Wenn also in C ein dynamisches Array ein Zeiger ist, der auf einen Speicherbereich zeigt, dann ist es in C++ ein Vector. Also würde h_aliases in C++ so aussehen:

C/C++ Code:
std::vector<std::string> h_aliases;
C/C++ Code:
std::vector<std::string> h_aliases;
C/C++ Code:
std::vector<std::string> h_aliases;


Schade, dass die hostent-Struktur in C geschrieben wurde, aber ich hoffe, Sie haben das Prinzip verstanden.

Der nächste Typ beschreibt den Adresstyp, bei uns also einfach nur AF_INET, IPv6-Adressen sind uns egal. Also ist auch der nächste Parameter nicht relevant und sollte eigentlich immer 4 betragen.

Wichtig ist nun die Liste der IP-Adressen. Diese sieht am Anfang genau so aus wie die Liste der Aliases, doch passen Sie auf: es ist nicht das gleiche. Hier haben wir keine Strings, denn die IP-Adressen werden, wie in der sockaddr, binär gespeichert.

Da ein Bild mehr als 1000 Worte sagt, ist hier mal ein Bild:



Dies ist der grundsätzliche Aufbau der hostent-Struktur. Kommen wir zurück zur gethostbyname-Funktion. Diese erwartet einen C-String, in dem der Hostname in der Form www.bla.de enthalten ist. Unser neues Programm fängt also so an:

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#ifdef
linux
#include
<netdb.h> // gethostbyname(), hostent
#include
<arpa/inet.h> // inet_ntoa()
#else
#include
<winsock2.h>
#endif

int
main()
{
    using namespace std;

#ifndef
linux
    WSADATA w;
    if(int result = WSAStartup(MAKEWORD(2,2), &w) != 0)
    {
        cout << "Winsock 2 konnte nicht gestartet werden! Error #" << result << endl;
        return 1;
    }
#endif


    cout << "Bitte gebe einen Hostnamen ein: ";
    string Hostname;
    cin >> Hostname;

    hostent* phe = gethostbyname(Hostname.c_str());
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#ifdef
linux
#include
<netdb.h> // gethostbyname(), hostent
#include
<arpa/inet.h> // inet_ntoa()
#else
#include
<winsock2.h>
#endif

int
main()
{
using namespace std;

#ifndef
linux
WSADATA w;
if(int result = WSAStartup(MAKEWORD(2,2), &w) != 0)
{
cout << "Winsock 2 konnte nicht gestartet werden! Error #" << result << endl;
return 1;
}
#endif


cout << "Bitte gebe einen Hostnamen ein: ";
string Hostname;
cin >> Hostname;

hostent* phe = gethostbyname(Hostname.c_str());
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#ifdef
linux
#include
<netdb.h> // gethostbyname(), hostent
#include
<arpa/inet.h> // inet_ntoa()
#else
#include
<winsock2.h>
#endif

int
main()
{
    using namespace std;

#ifndef
linux
    WSADATA w;
    if(int result = WSAStartup(MAKEWORD(2,2), &w) != 0)
    {
        cout << "Winsock 2 konnte nicht gestartet werden! Error #" << result << endl;
        return 1;
    }
#endif


    cout << "Bitte gebe einen Hostnamen ein: ";
    string Hostname;
    cin >> Hostname;

    hostent* phe = gethostbyname(Hostname.c_str());


Dass der Zeiger auf die hostent-Struktur wieder freigegeben wird, soll uns nicht kümmern, denn dies wird automatisch erledigt. Erstmal sollten wir checken, ob der Hostname überhaupt existiert:

C/C++ Code:
    if(phe == NULL)
    {
        cout << "Host konnte nicht aufgeloest werden!" << endl;
        return 1;
    }
C/C++ Code:
if(phe == NULL)
{
cout << "Host konnte nicht aufgeloest werden!" << endl;
return 1;
}
C/C++ Code:
    if(phe == NULL)
    {
        cout << "Host konnte nicht aufgeloest werden!" << endl;
        return 1;
    }


Nun geben wir den Namen sowie die Aliases aus:

C/C++ Code:
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
    cout << "\nHostname: " << phe->h_name << endl
         << "Aliases: ";

    for(char** p = phe->h_aliases; *p != 0; ++p)
    {
        cout << *p << " ";
    }
    cout << endl;
C/C++ Code:
1
2
3
4
5
6
7
8
cout << "\nHostname: " << phe->h_name << endl
<< "Aliases: ";

for(char** p = phe->h_aliases; *p != 0; ++p)
{
cout << *p << " ";
}
cout << endl;
C/C++ Code:
1
2
3
4
5
6
7
8
    cout << "\nHostname: " << phe->h_name << endl
         << "Aliases: ";

    for(char** p = phe->h_aliases; *p != 0; ++p)
    {
        cout << *p << " ";
    }
    cout << endl;


Die Funktion der for-Schleife ist nicht ganz einfach: p zeigt auf den ersten C-String und wird nach jedem Schleifendurchlauf um 1 erhöht, bis wir einen Nullzeiger haben. Wichtig: Keinen leeren String der ein '\0' enthält, sondern die Liste von Zeigern auf C-Strings enthält einen Nullzeiger, der nicht auf einen C-String zeigt.

Als nächstes wird einfach nur überprüft, ob es sich bei den IPs um IPv4-Adressen handelt. IPv6 lassen wir außer Acht.

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
1
2
3
4
5
6
7
8
9
10
11
    if(phe->h_addrtype != AF_INET)
    {
        cout << "Ungueltiger Adresstyp!" << endl;
        return 1;
    }

    if(phe->h_length != 4)
    {
        cout << "Ungueltiger IP-Typ!" << endl;
        return 1;
    }
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
if(phe->h_addrtype != AF_INET)
{
cout << "Ungueltiger Adresstyp!" << endl;
return 1;
}

if(phe->h_length != 4)
{
cout << "Ungueltiger IP-Typ!" << endl;
return 1;
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
    if(phe->h_addrtype != AF_INET)
    {
        cout << "Ungueltiger Adresstyp!" << endl;
        return 1;
    }

    if(phe->h_length != 4)
    {
        cout << "Ungueltiger IP-Typ!" << endl;
        return 1;
    }


Nun wollen wir diese IP-Adressen ausgeben, doch sie liegen in binärer Form vor. Also müssen wir sie in einen String umwandeln und hierzu gibt es die Funktion inet_ntoa(). Sie wandelt eine in_addr-Struktur in einen String um, in der Form „x.x.x.x“. Leider haben wir keine Zeiger auf in_addr-Strukturen sondern Zeiger auf chars. Da die in_addr-Struktur die Daten auch nur binär speichert, können wir den Zeiger einfach in einen in_addr-Zeiger casten:

C/C++ Code:
reinterpret_cast<in_addr*>(*phe->h_addr_list);
C/C++ Code:
reinterpret_cast<in_addr*>(*phe->h_addr_list);
C/C++ Code:
reinterpret_cast<in_addr*>(*phe->h_addr_list);


Dieser neue Zeiger muss nun noch dereferenziert werden, da inet_ntoa() eine Instanz und keinen Zeiger erwartet:

C/C++ Code:
   cout << inet_ntoa(*reinterpret_cast<in_addr*>(*phe->h_addr_list));
C/C++ Code:
cout << inet_ntoa(*reinterpret_cast<in_addr*>(*phe->h_addr_list));
C/C++ Code:
   cout << inet_ntoa(*reinterpret_cast<in_addr*>(*phe->h_addr_list));


Nun müssen wir noch, wie bei den Aliases, die Liste wirklich durchgehen und nicht einfach nur das erste Element nehmen, da ein Host ja auch mehrere IPs haben kann. Der endgültige Code sieht also so aus:

C/C++ Code:
    cout << "IP-Adressen: ";
    for(char** p = phe->h_addr_list; *p != 0; ++p)
    {
        cout << inet_ntoa(*reinterpret_cast<in_addr*>(*p)) << " ";
    }
    cout << endl;
}
C/C++ Code:
cout << "IP-Adressen: ";
for(char** p = phe->h_addr_list; *p != 0; ++p)
{
cout << inet_ntoa(*reinterpret_cast<in_addr*>(*p)) << " ";
}
cout << endl;
}
C/C++ Code:
    cout << "IP-Adressen: ";
    for(char** p = phe->h_addr_list; *p != 0; ++p)
    {
        cout << inet_ntoa(*reinterpret_cast<in_addr*>(*p)) << " ";
    }
    cout << endl;
}


Um die Funktion der inet_ntoa-Funktion etwas klarer zu machen, hier nochmal eine mögliche Implementierung:

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
1
2
3
4
5
6
7
8
9
10
11
#include <sstream>
std::string my_inet_ntoa(in_addr& ip)
{
    unsigned char* p = reinterpret_cast<unsigned char*>(&ip);
    std::stringstream sstream;
    for(int i = 0; i < 4 && (i == 0 || (sstream << ".")); ++i)
    {
        sstream << static_cast<int>(p[i]);
    }
    return sstream.str();
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
#include <sstream>
std::string my_inet_ntoa(in_addr& ip)
{
unsigned char* p = reinterpret_cast<unsigned char*>(&ip);
std::stringstream sstream;
for(int i = 0; i < 4 && (i == 0 || (sstream << ".")); ++i)
{
sstream << static_cast<int>(p[i]);
}
return sstream.str();
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
#include <sstream>
std::string my_inet_ntoa(in_addr& ip)
{
    unsigned char* p = reinterpret_cast<unsigned char*>(&ip);
    std::stringstream sstream;
    for(int i = 0; i < 4 && (i == 0 || (sstream << ".")); ++i)
    {
        sstream << static_cast<int>(p[i]);
    }
    return sstream.str();
}


Nun sind wir schon fertig. Das endgültiges Programm könnt ihr nun testen, mit einer Adresse wie www.google.de oder www.microsoft.com (oder www.kernel.org :P). Alles sollte klappen und als nächstes wollen wir diesen Code in das vorherige Programm einfügen.


4 Integration von nslookup

Nun wollen wir unser eigenes nslookup in das Programm aus Kapitel 2 einfügen. Danach sollte der Benutzer nur noch den Namen der Internetseite eingeben müssen und das Programm kümmert sich um die Verbindung. Dies klingt einfacher als es ist, da ein Host ja mehrere IP-Adressen haben kann und diese müssen alle ausprobiert werden, bevor gesagt wird: "Keine Verbindung möglich!". Denn das wäre ja gelogen ;).

Fangen wir zuerst an wie beim Programm vom vorherigen Kapitel:

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <iostream>
#ifdef
linux
#include
<netdb.h> // gethostbyname(), hostent
#include
<arpa/inet.h> // inet_ntoa()
#else
#include
<winsock2.h>
#endif

int
main()
{
    using namespace std;

#ifndef
linux
    WSADATA w;
    if(int result = WSAStartup(MAKEWORD(2,2), &w) != 0)
    {
        cout << "Winsock 2 konnte nicht gestartet werden! Error #" << result << endl;
        return 1;
    }
#endif


    cout << "Bitte gebe einen Hostnamen ein: ";
    string Hostname;
    cin >> Hostname;

    hostent* phe = gethostbyname(Hostname.c_str());

    if(phe == NULL)
    {
        cout << "Host konnte nicht aufgeloest 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;
        return 1;
    }

    if(phe->h_length != 4)
    {
        cout << "Ungueltiger IP-Typ!" << endl;
        return 1;
    }
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <iostream>
#ifdef
linux
#include
<netdb.h> // gethostbyname(), hostent
#include
<arpa/inet.h> // inet_ntoa()
#else
#include
<winsock2.h>
#endif

int
main()
{
using namespace std;

#ifndef
linux
WSADATA w;
if(int result = WSAStartup(MAKEWORD(2,2), &w) != 0)
{
cout << "Winsock 2 konnte nicht gestartet werden! Error #" << result << endl;
return 1;
}
#endif


cout << "Bitte gebe einen Hostnamen ein: ";
string Hostname;
cin >> Hostname;

hostent* phe = gethostbyname(Hostname.c_str());

if(phe == NULL)
{
cout << "Host konnte nicht aufgeloest 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;
return 1;
}

if(phe->h_length != 4)
{
cout << "Ungueltiger IP-Typ!" << endl;
return 1;
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <iostream>
#ifdef
linux
#include
<netdb.h> // gethostbyname(), hostent
#include
<arpa/inet.h> // inet_ntoa()
#else
#include
<winsock2.h>
#endif

int
main()
{
    using namespace std;

#ifndef
linux
    WSADATA w;
    if(int result = WSAStartup(MAKEWORD(2,2), &w) != 0)
    {
        cout << "Winsock 2 konnte nicht gestartet werden! Error #" << result << endl;
        return 1;
    }
#endif


    cout << "Bitte gebe einen Hostnamen ein: ";
    string Hostname;
    cin >> Hostname;

    hostent* phe = gethostbyname(Hostname.c_str());

    if(phe == NULL)
    {
        cout << "Host konnte nicht aufgeloest 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;
        return 1;
    }

    if(phe->h_length != 4)
    {
        cout << "Ungueltiger IP-Typ!" << endl;
        return 1;
    }


Nun müssen wir jede einzelne IP-Adresse testen, ob sie klappt. Dazu gehen wir die Liste vom Anfang durch und sobald wir eine Verbindung gefunden haben, fahren wir fort. Falls das Ende der Liste erreicht wurde und keine IP-Adresse klappte, brechen wir ab. Mit ein bisschen Logik kann man daraus eine Schleife erstellen, ich habe es so gemacht:

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    int Socket = socket(AF_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;
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int Socket = socket(AF_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;
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    int Socket = socket(AF_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;


Ich erstelle eine Variable result, die das Ergebnis von connect speichert, eine Variable p die als Iterator dient. Als erstes prüfe ich, ob ein Nullzeiger vorliegt, also ob die Liste zu Ende ist. Wenn nicht, erstelle ich meine binäre IP-Adresse und erhöhe danach schonmal den p-Zeiger (irgendwo muss ich's ja tun). Jetzt wird versucht zu verbinden. Falls result -1 ist, also fehlgeschlagen, dann wird das Ganze wiederholt, wenn nicht geht's nach der Schleife weiter.

Nun müssen wir nur noch die Verbindung beenden und unser Programm ist fertig.


5 Senden und Empfangen

5.1 Grundlegender Aufbau des HTTP-Protokolls

Nun wollen wir uns endlich mit dem Senden und Empfangen von Daten beschäftigen. Was bei uns eine Internetadresse ist, besteht aus folgenden Abschnitten:

Code:
http://www.kernel.org/faq/index.html
  1  |       2       |     3
Code:
http://www.kernel.org/faq/index.html
1 | 2 | 3
Code:
http://www.kernel.org/faq/index.html
  1  |       2       |     3


Zuerst kommt das Protokoll (1), danach der Host den wir auflösen (2) und als letztes die Datei bzw. der Pfad, den wir an den Webserver schicken müssen. Die Kommunikation zwischen Browser und Webserver geschieht hierbei über das HTTP-Protokoll. Es handelt sich also um eine Sprache, mit der der Dateiaustausch über das Internet geregelt werden kann. Ein anderes Protokoll wäre z.B. das FTP-Protokoll.
Nun müssen Sie wissen, wie das HTTP-Protokoll funktioniert: Ein Webserver horcht auf Port 80. Sobald sich ein Client verbindet, wird auf zu empfangende Daten gewartet. Der Client, meistens der Browser, in diesem Fall aber unser Programm, schickt nun eine Anfrage, welche Datei er haben möchte. Bei dieser Anfrage handelt sich um einfachen Text im ASCII-Format (Deswegen auch unter anderem die Probleme beim Realisieren von Domainnamen mit Umlauten). Eine Anfrage, die wir dem Server nach dem Verbindungsaufbau schicken, sieht so aus:

Code:
GET /faq/index.html HTTP/1.1
Host: www.kernel.org
(hier eine leere Zeile)
Code:
GET /faq/index.html HTTP/1.1
Host: www.kernel.org
(hier eine leere Zeile)
Code:
GET /faq/index.html HTTP/1.1
Host: www.kernel.org
(hier eine leere Zeile)


Zuerst kommt ein Befehl, in diesem Fall GET, gefolgt von einem Leerzeichen. Nun kommt die Datei, die wir anfordern, noch ein Leerzeichen und dann die Protokollversion (HTTP/1.0 wäre zum Beispiel die ältere Variante). Nun kommen wir in die nächste Zeile, dabei müssen wir aber eines beachten: Die Zeilenumbrüche sind hier im DOS-Format, das heißt wir haben zuerst ein \r-Zeichen (ASCII-Code 13) und ein \n-Zeichen (ASCII-Code 10). Wollen wir nun also einen String erstellen der unsere Anfrage enthält, würde es so aussehen:

C/C++ Code:
const string request = "GET /faq/index.html HTTP/1.1\r\nHost: www.kernel.org\r\n\r\n";
C/C++ Code:
const string request = "GET /faq/index.html HTTP/1.1\r\nHost: www.kernel.org\r\n\r\n";
C/C++ Code:
const string request = "GET /faq/index.html HTTP/1.1\r\nHost: www.kernel.org\r\n\r\n";


Am Ende haben wir ein \r\n\r\n, hierbei handelt es sich einfach um die leere Zeile, die am Ende gesendet werden muss, damit der Server erkennt, dass hier die Anfrage zu Ende ist.
Nach der Anfrage kommt natürlich die Antwort. Diese besitzt auch einen ganz bestimmten Aufbau, den wir uns aber erst im nächsten Kapitel anschauen wollen. Jetzt kommen wir zuerst zum allgemeinen Senden und Empfangen von Daten.

5.2 Die Funktionen send und recv

Das Senden bzw. Empfangen geschieht mit Hilfe folgender Funktionen:

C/C++ Code:
int send(int sockfd, const void *msg, int len, int flags);
int recv(int sockfd,       void *buf, int len, unsigned int flags);
C/C++ Code:
int send(int sockfd, const void *msg, int len, int flags);
int recv(int sockfd, void *buf, int len, unsigned int flags);
C/C++ Code:
int send(int sockfd, const void *msg, int len, int flags);
int recv(int sockfd,       void *buf, int len, unsigned int flags);


Der Aufbau beider Funktion ist ziemlich gleich. Der erste Parameter ist der Socket, auf dem gesendet werden soll, der zweite ein Zeiger auf einen Binärenspeicherbereich mit der Länge len. Der letzte Parameter flags ist für uns unwichtig und es wird einfach 0 übergeben.
Nicht zu vergessen ist der Rückgabewert: Hierbei handelt es sich um die Anzahl der übertragenen Bytes. Denn auch wenn man versucht, einen Buffer von 100 Byte zu übertragen, kann send damit nicht fertig werden und wird vorher schon aufhören und dann die geschaffte Anzahl zurückgeben, z.B. 34. Unsere Aufgabe ist es also den restlichen Buffer von 64 Bytes noch zu senden. Dazu schreiben wir eine Funktion, die uns diese Arbeit immer abnehmen soll:

C/C++ Code:
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
void SendAll(int socket, const char* const buf, const int size)
{
    int bytesSent = 0; // Anzahl Bytes die wir bereits vom Buffer gesendet haben
    do
    {
        bytesSent += send(socket, buf + bytesSent, size - bytesSent, 0);
    } while(bytesSent < size);
}
C/C++ Code:
1
2
3
4
5
6
7
8
void SendAll(int socket, const char* const buf, const int size)
{
int bytesSent = 0; // Anzahl Bytes die wir bereits vom Buffer gesendet haben
do
{
bytesSent += send(socket, buf + bytesSent, size - bytesSent, 0);
} while(bytesSent < size);
}
C/C++ Code:
1
2
3
4
5
6
7
8
void SendAll(int socket, const char* const buf, const int size)
{
    int bytesSent = 0; // Anzahl Bytes die wir bereits vom Buffer gesendet haben
    do
    {
        bytesSent += send(socket, buf + bytesSent, size - bytesSent, 0);
    } while(bytesSent < size);
}


Diese Funktion sendet den von uns übergebenen Buffer komplett. Doch was passiert nun, wenn während dem Senden ein Fehler auftritt? Vielleicht bricht die Verbindung ab oder geht verloren. Dies signalisiert uns send(), indem es uns einen Wert kleiner 0 zurückgibt.
Da dies unerwartet ist, nehmen wir hierfür Exceptions. Hierzu werden wir die std::runtime_error-Klasse verwenden. Jedes mal wenn wir sie werfen wollen, lassen wir sie von einer Funktion erstellen:

C/C++ Code:
std::runtime_error CreateSocketError()
{
C/C++ Code:
std::runtime_error CreateSocketError()
{
C/C++ Code:
std::runtime_error CreateSocketError()
{


Die Signatur der Funktion ist selbsterklärend, doch wenn wir zum Inhalt kommen, wird's kompliziert. Leider sind die Funktionen zum Erhalt von Fehlermeldungen unter Windows und Linux ziemlich verschieden. Alle Linux-Fans können jetzt aufatmen:

C/C++ Code:
    std::ostringstream temp;
#ifdef
linux
    temp << "Socket-Fehler #" << errno << ": " << strerror(errno);
C/C++ Code:
std::ostringstream temp;
#ifdef
linux
temp << "Socket-Fehler #" << errno << ": " << strerror(errno);
C/C++ Code:
    std::ostringstream temp;
#ifdef
linux
    temp << "Socket-Fehler #" << errno << ": " << strerror(errno);


Da der Konstruktor von std::runtime_error einen std::string erwartet, verwenden wir einen std::ostringstream zur einfachen Formatierung. Wir geben eine Fehlernummer aus, gefolgt von einem Text, der den Fehler beschreibt. Natürlich müssen wir uns dies nicht selbst ausdenken. Eine Fehlernummer wird in der globalen Variable errno gespeichert, mit der Funktion strerror(int) erzeugen wir einen Text den wir ausgeben können. Hierzu muss man nur noch die errno.h am Anfang inkludieren.

Unter Windows sieht das ganze so aus:

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#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
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#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
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#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


Hier gibt die Funktion WSAGetLastError() eine Fehlernummer zurück und FormatMessage erzeugt daraus einen Text. Die genaue Funktionsweise lässt sich in der MSDN nachlesen.

Am Ende unserer Funktion erstellen wir eine Instanz der std::runtime_error-Klasse und geben sie zurück:

C/C++ Code:
    return std::runtime_error(temp.str());
}
C/C++ Code:
return std::runtime_error(temp.str());
}
C/C++ Code:
    return std::runtime_error(temp.str());
}


Nun wollen wir von unserer neuen Funktion Gebrauch machen und fügen sie in die SendAll-Funktion ein:

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
1
2
3
4
5
6
7
8
9
10
11
12
13
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);
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
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);
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
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);
}


Nun entwickeln wir noch eine Funktion für das Empfangen von Daten. Da das HTTP-Protokoll ja zeilenweise sendet, soll unsere Funktion auch so aufgebaut sein:

C/C++ Code:
// Liest eine Zeile des Sockets in einen stringstream
void GetLine(int socket, std::stringstream& line)
{
C/C++ Code:
// Liest eine Zeile des Sockets in einen stringstream
void GetLine(int socket, std::stringstream& line)
{
C/C++ Code:
// Liest eine Zeile des Sockets in einen stringstream
void GetLine(int socket, std::stringstream& line)
{


Wir lesen byteweise von dem Socket bis wir auf einen Zeilenumbruch treffen.

C/C++ Code:
    for(char c; recv(socket, &c, 1, 0) > 0; line << c)
    {
        if(c == '\n')
        {
            return;
        }
    }
C/C++ Code:
for(char c; recv(socket, &c, 1, 0) > 0; line << c)
{
if(c == '\n')
{
return;
}
}
C/C++ Code:
    for(char c; recv(socket, &c, 1, 0) > 0; line << c)
    {
        if(c == '\n')
        {
            return;
        }
    }


Die Schleife wird nur verlassen, wenn recv einen Wert kleiner oder gleich 0 zurück gibt, somit werfen wir dahinter unsere Exception.

C/C++ Code:
    throw CreateSocketError();
}
C/C++ Code:
throw CreateSocketError();
}
C/C++ Code:
    throw CreateSocketError();
}


Falls die recv-Funktion einen Wert gleich 0 zurück gibt, deutet dies auf einen normalen Verbindungsabbruch hin. Dies ist der Fall, wenn die andere Seite close/closesocket aufruft.

5.3 Request

Nun wollen wir ein Programm erstellen, das all diese Sachen anwendet. Wir schicken einen Request an www.kernel.org und geben dann erstmal einfach die Ausgabe auf der Konsole aus. Unser Programm fängt so an:

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include <iostream>
#include
<fstream>
#include
<stdexcept> // runtime_error
#include
<sstream>
#ifdef
linux
#include
<sys/socket.h> // socket(), connect()
#include
<arpa/inet.h> // sockaddr_in
#include
<netdb.h> // gethostbyname(), hostent
#include
<errno.h> // errno
#else
#include
<winsock2.h>
#endif


// Hier die Funktionen CreateSocketError, SendLine und GetLine einfügen

int main()
{
    using namespace std;

#ifndef
linux
    WSADATA w;
    if(int result = WSAStartup(MAKEWORD(2,2), &w) != 0)
    {
        cout << "Winsock 2 konnte nicht gestartet werden! Error #" << result << endl;
        return 1;
    }
#endif


    hostent* phe = gethostbyname("www.kernel.org");

    if(phe == NULL)
    {
        cout << "Host konnte nicht aufgeloest 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;
        return 1;
    }

    if(phe->h_length != 4)
    {
        cout << "Ungueltiger IP-Typ!" << endl;
        return 1;
    }

    int Socket = socket(AF_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;
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include <iostream>
#include
<fstream>
#include
<stdexcept> // runtime_error
#include
<sstream>
#ifdef
linux
#include
<sys/socket.h> // socket(), connect()
#include
<arpa/inet.h> // sockaddr_in
#include
<netdb.h> // gethostbyname(), hostent
#include
<errno.h> // errno
#else
#include
<winsock2.h>
#endif


// Hier die Funktionen CreateSocketError, SendLine und GetLine einfügen

int main()
{
using namespace std;

#ifndef
linux
WSADATA w;
if(int result = WSAStartup(MAKEWORD(2,2), &w) != 0)
{
cout << "Winsock 2 konnte nicht gestartet werden! Error #" << result << endl;
return 1;
}
#endif


hostent* phe = gethostbyname("www.kernel.org");

if(phe == NULL)
{
cout << "Host konnte nicht aufgeloest 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;
return 1;
}

if(phe->h_length != 4)
{
cout << "Ungueltiger IP-Typ!" << endl;
return 1;
}

int Socket = socket(AF_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;
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include <iostream>
#include
<fstream>
#include
<stdexcept> // runtime_error
#include
<sstream>
#ifdef
linux
#include
<sys/socket.h> // socket(), connect()
#include
<arpa/inet.h> // sockaddr_in
#include
<netdb.h> // gethostbyname(), hostent
#include
<errno.h> // errno
#else
#include
<winsock2.h>
#endif


// Hier die Funktionen CreateSocketError, SendLine und GetLine einfügen

int main()
{
    using namespace std;

#ifndef
linux
    WSADATA w;
    if(int result = WSAStartup(MAKEWORD(2,2), &w) != 0)
    {
        cout << "Winsock 2 konnte nicht gestartet werden! Error #" << result << endl;
        return 1;
    }
#endif


    hostent* phe = gethostbyname("www.kernel.org");

    if(phe == NULL)
    {
        cout << "Host konnte nicht aufgeloest 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;
        return 1;
    }

    if(phe->h_length != 4)
    {
        cout << "Ungueltiger IP-Typ!" << endl;
        return 1;
    }

    int Socket = socket(AF_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;


Wie Sie sehen, nehmen wir als Hostnamen www.kernel.org, denn die Seite www.kernel.org/faq/index.html soll unsere Testseite werden. Nun wollen wir unsere Anfrage senden:

C/C++ Code:
    const string request = "GET /faq/index.html HTTP/1.1\r\nHost: www.kernel.org\r\nConnection: close\r\n\r\n";

    SendAll(Socket, request.c_str(), request.size());
C/C++ Code:
const string request = "GET /faq/index.html HTTP/1.1\r\nHost: www.kernel.org\r\nConnection: close\r\n\r\n";

SendAll(Socket, request.c_str(), request.size());
C/C++ Code:
    const string request = "GET /faq/index.html HTTP/1.1\r\nHost: www.kernel.org\r\nConnection: close\r\n\r\n";

    SendAll(Socket, request.c_str(), request.size());


Dies ist, bis auf das Hinzufügen der folgenden Zeile, die gleiche Anfrage:

Code:
Connection: close
Code:
Connection: close
Code:
Connection: close


Sie bewirkt, dass nachdem wir die Antwort erhalten haben, der Server die Verbindung schließt. Die Verbindung könnte z.B. aufrecht erhalten bleiben, wenn wir weitere Request hinterherschicken wollten, um z.B. Bilder zu laden.
Da das Behandeln der Response im nächsten Kapitel behandelt wird, wollen wir die Antwort vorläufig in eine Textdatei schreiben:

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    ofstream fout("output.txt");

    cout << "Empfange und schreibe Antwort in output.txt..." << endl;
    while(true)
    {
        stringstream line;
        try
        {
            GetLine(Socket, line);
        }
        catch(exception& e) // Ein Fehler oder Verbindungsabbruch
        {
            break; // Schleife verlassen
        }
        fout << line.str() << endl; // Zeile in die Datei schreiben.
    }
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ofstream fout("output.txt");

cout << "Empfange und schreibe Antwort in output.txt..." << endl;
while(true)
{
stringstream line;
try
{
GetLine(Socket, line);
}
catch(exception& e) // Ein Fehler oder Verbindungsabbruch
{
break; // Schleife verlassen
}
fout << line.str() << endl; // Zeile in die Datei schreiben.
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    ofstream fout("output.txt");

    cout << "Empfange und schreibe Antwort in output.txt..." << endl;
    while(true)
    {
        stringstream line;
        try
        {
            GetLine(Socket, line);
        }
        catch(exception& e) // Ein Fehler oder Verbindungsabbruch
        {
            break; // Schleife verlassen
        }
        fout << line.str() << endl; // Zeile in die Datei schreiben.
    }


Am Ende schließen wir wie gewohnt das Socket und unser Programm sieht so aus.

Wenn wir es ausführen, wird die Antwort die Response vom Server in die output.txt-Datei geschrieben. Diese sollte ungefähr so anfangen:

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
1
2
3
4
5
6
7
8
9
10
11
12
13
HTTP/1.1 200 OK
Date: Fri, 15 Dec 2006 14:51:19 GMT
Server: Apache/2.2.2 (Fedora)
Last-Modified: Wed, 08 Nov 2006 22:06:19 GMT
ETag: "b643c8-6118-421c3874aacc0"
Accept-Ranges: bytes
Content-Length: 24856
Connection: close
Content-Type: text/html
X-Pad: avoid browser bug

<?xml version="1.0" encoding="utf-8"?>
...
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
HTTP/1.1 200 OK
Date: Fri, 15 Dec 2006 14:51:19 GMT
Server: Apache/2.2.2 (Fedora)
Last-Modified: Wed, 08 Nov 2006 22:06:19 GMT
ETag: "b643c8-6118-421c3874aacc0"
Accept-Ranges: bytes
Content-Length: 24856
Connection: close
Content-Type: text/html
X-Pad: avoid browser bug

<?xml version="1.0" encoding="utf-8"?>
...
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
HTTP/1.1 200 OK
Date: Fri, 15 Dec 2006 14:51:19 GMT
Server: Apache/2.2.2 (Fedora)
Last-Modified: Wed, 08 Nov 2006 22:06:19 GMT
ETag: "b643c8-6118-421c3874aacc0"
Accept-Ranges: bytes
Content-Length: 24856
Connection: close
Content-Type: text/html
X-Pad: avoid browser bug

<?xml version="1.0" encoding="utf-8"?>
...


6 Response

6.1 Statuszeile

Die erste Zeile beschreibt einen Statuscode. Sie besteht aus der HTTP-Version des Servers, einer Status-Nr. und einem Text, der diese beschreibt. Eine Liste aller Codes gibt's unter anderem bei Wikipedia. Für uns wichtig ist zuerst einmal der Code 200: Er steht dafür, dass alles geklappt hat, unsere Anfrage in Ordnung war und die Datei existiert. Ein weiterer wichtiger Status-Code ist 100. Er sagt uns, dass der Server noch etwas Zeit braucht die Anfrage zu verarbeiten und uns gleich noch eine Antwort schicken wird. Das könnte z.B. so aussehen:

Code:
HTTP/1.1 100 Continue

HTTP/1.1 200 OK
Date: Fri, 15 Dec 2006 14:51:19 GMT
Server: Apache/2.2.2 (Fedora)
...
Code:
HTTP/1.1 100 Continue

HTTP/1.1 200 OK
Date: Fri, 15 Dec 2006 14:51:19 GMT
Server: Apache/2.2.2 (Fedora)
...
Code:
HTTP/1.1 100 Continue

HTTP/1.1 200 OK
Date: Fri, 15 Dec 2006 14:51:19 GMT
Server: Apache/2.2.2 (Fedora)
...


Nach der ersten Antwort die uns nur sagt, dass es gleich weiter geht, kommt eine freie Zeile und dann die richtige Antwort.
In unserem letzten Programm machen wir jetzt nach dem Verbinden so weiter:

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    const string request = "GET /faq/index.html HTTP/1.1\r\nHost: www.kernel.org\r\nConnection: close\r\n\r\n";
    try
    {
        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;
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const string request = "GET /faq/index.html HTTP/1.1\r\nHost: www.kernel.org\r\nConnection: close\r\n\r\n";
try
{
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;
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    const string request = "GET /faq/index.html HTTP/1.1\r\nHost: www.kernel.org\r\nConnection: close\r\n\r\n";
    try
    {
        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;


Mit der GetLine-Funktion (siehe oben) lesen wir die erste Zeile in einen Stringstream ein. Nun schreiben wir den Protokolltyp in einen String. Zur Erinnerung: Der >>-operator von streams liest bis zu einem Leerzeichen ein. Als nächstes lesen wir den Statuscode aus, wenn dieser 100 ist, müssen wir noch die leere Zeile zwischen der neuen Anfrage ignorieren und beginnen dann wieder von vorne.
Nun müssen wir nur noch eine Fehlermeldung ausgeben, wenn ein Statuscode anders als 200 oder 100 auftrat:

C/C++ Code:
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
        if(code != 200)
        {
            firstLine.ignore(); // Leerzeichen nach dem Statuscode ignorieren
            string msg;
            getline(firstLine, msg);
            cout << "Error #" << code << " - " << msg << endl;
            return 0;
        }
C/C++ Code:
1
2
3
4
5
6
7
8
if(code != 200)
{
firstLine.ignore(); // Leerzeichen nach dem Statuscode ignorieren
string msg;
getline(firstLine, msg);
cout << "Error #" << code << " - " << msg << endl;
return 0;
}
C/C++ Code:
1
2
3
4
5
6
7
8
        if(code != 200)
        {
            firstLine.ignore(); // Leerzeichen nach dem Statuscode ignorieren
            string msg;
            getline(firstLine, msg);
            cout << "Error #" << code << " - " << msg << endl;
            return 0;
        }


6.2 Transfer-Arten

Nun kommen wir zum eigentlichen Header. Er besteht aus vielen Argumenten, von denen einige aber nebensächlich sind. Wichtig für uns ist erstmal die allgemeine Übertragungsart:

Normales Übertragen, Dateigröße nicht mit übergeben

Dies ist die einfachste Art der Übertragung. Sobald der Header zu Ende ist, lesen wir mit recv solange, bis der Server die Verbindung schließt. Wir erinnern uns: recv gibt beim Schließen der Verbindung 0 zurück.

Normales Übertragen, Dateigröße übergeben

Nun machen wir alles genauso wie vorher, nur das wir nicht darauf warten müssen, dass recv 0 zurück gibt: Wir hören einfach auf, sobald wir die Datei vollständig erhalten haben. Vorteil: Wir können den Fortschritt in % anzeigen.

Chunked-Encoding

Chunked-Encoding ist die komplizierteste Art: Die Datei wird nicht als ganzes übertragen, sondern in verschiedenen Abschnitten. Dies ist z.B. notwendig, wenn der Server ein PHP-Script ausführt noch während die Seite gesendet wird.

Die für diese 3 Übertragunstypen wichtigen Argumente sind folgende:

Code:
Content-Length: 1234
Transfer-Encoding: chunked
Code:
Content-Length: 1234
Transfer-Encoding: chunked
Code:
Content-Length: 1234
Transfer-Encoding: chunked


Content-Length gibt uns die Größe der Datei in Bytes und das Argument Transfer-Encoding existiert nur dann, wenn auch das Chunked-Encoding angewendet wird.
Wir erstellen uns also eine bool-Variable, die speichert ob das "Tranfer-Encoding: chunked" angegeben wurde, sowie eine Variable, die die übergebene Dateigröße speichert und mit einer Konstanten belegt wird, falls keine Größe angegeben wurde:

C/C++ Code:
        bool chunked = false;
        const int noSizeGiven = -1;
        int size = noSizeGiven;
C/C++ Code:
bool chunked = false;
const int noSizeGiven = -1;
int size = noSizeGiven;
C/C++ Code:
        bool chunked = false;
        const int noSizeGiven = -1;
        int size = noSizeGiven;


Nun starten wir mir der zeilenweisen Auslesung des Headers:

C/C++ Code:
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
        while(true)
        {
            stringstream sstream;
            GetLine(Socket, sstream);
            if(sstream.str() == "\r") // Header zu Ende?
            {
                break;
            }
C/C++ Code:
1
2
3
4
5
6
7
8
while(true)
{
stringstream sstream;
GetLine(Socket, sstream);
if(sstream.str() == "\r") // Header zu Ende?
{
break;
}
C/C++ Code:
1
2
3
4
5
6
7
8
        while(true)
        {
            stringstream sstream;
            GetLine(Socket, sstream);
            if(sstream.str() == "\r") // Header zu Ende?
            {
                break;
            }


Zur Erinnerung: Der Header endet mit einer leeren Zeile, da als Zeilenumbruch allerdings nicht \n verwendet wird (sondern \r\n), handelt es sich um eine Zeile, die nur ein \r enthält:

Code:
HTTP/1.1 200 OK\r\nAsd: 123\r\n\r\n
Code:
HTTP/1.1 200 OK\r\nAsd: 123\r\n\r\n
Code:
HTTP/1.1 200 OK\r\nAsd: 123\r\n\r\n


Nun lesen wir in einen String ein, der der Name des Arguments ist. Danach ignorieren wir ein Zeichen (das Leerzeichen), um danach den Wert einzulesen.

C/C++ Code:
            string left; // Das was links steht
            sstream >> left;
            sstream.ignore(); // ignoriert Leerzeichen
C/C++ Code:
string left; // Das was links steht
sstream >> left;
sstream.ignore(); // ignoriert Leerzeichen
C/C++ Code:
            string left; // Das was links steht
            sstream >> left;
            sstream.ignore(); // ignoriert Leerzeichen


Wenn der Name "Content-Size" ist, lesen wir die Größe ein:

C/C++ Code:
            if(left == "Content-Length:")
            {
                sstream >> size;
            }
C/C++ Code:
if(left == "Content-Length:")
{
sstream >> size;
}
C/C++ Code:
            if(left == "Content-Length:")
            {
                sstream >> size;
            }


Falls wir auf eine Angabe über das Transfer-Encoding treffen, werten wir den Wert aus. Ist dieser "chunked", setzen wir die Bool-Variable:

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
            if(left == "Transfer-Encoding:")
            {
                string transferEncoding;
                sstream >> transferEncoding;
                if(transferEncoding == "chunked")
                {
                    chunked = true;
                }
            }
        }
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
if(left == "Transfer-Encoding:")
{
string transferEncoding;
sstream >> transferEncoding;
if(transferEncoding == "chunked")
{
chunked = true;
}
}
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
            if(left == "Transfer-Encoding:")
            {
                string transferEncoding;
                sstream >> transferEncoding;
                if(transferEncoding == "chunked")
                {
                    chunked = true;
                }
            }
        }


Nun sind wir schon fertig mit der Auswertung des Headers. :)

6.3 Die Übertragung

Nun beginnen wir mit der eigentlichen Übertragung der Datei. Dazu erstellen wir erstmal eine Output-Datei, in die wir binär schreiben werden:

C/C++ Code:
        fstream fout("faq.html", ios::binary | ios::out);
        if(!fout)
        {
            cout << "Could Not Create File!" << endl;
            return 1;
        }
C/C++ Code:
fstream fout("faq.html", ios::binary | ios::out);
if(!fout)
{
cout << "Could Not Create File!" << endl;
return 1;
}
C/C++ Code:
        fstream fout("faq.html", ios::binary | ios::out);
        if(!fout)
        {
            cout << "Could Not Create File!" << endl;
            return 1;
        }


Wir erstellen uns drei Variablen:

C/C++ Code:
        int recvSize = 0; // Empfangene Bytes insgesamt
        char buf[1024];
        int bytesRecv = -1; // Empfangene Bytes des letzten recv
C/C++ Code:
int recvSize = 0; // Empfangene Bytes insgesamt
char buf[1024];
int bytesRecv = -1; // Empfangene Bytes des letzten recv
C/C++ Code:
        int recvSize = 0; // Empfangene Bytes insgesamt
        char buf[1024];
        int bytesRecv = -1; // Empfangene Bytes des letzten recv


In der ersten Variable speichern wir, wieviel Bytes wir schon emfpangen haben (dies ist z.B. für eine Prozentanzeige notwendig). Außerdem erstellen wir ein Array aus 1024 bytes für die Pufferung der Daten.
Als letztes erstellen wir die temporäre Variable bytesRecv, die den letzten Rückgabe wert von recv speichert.

C/C++ Code:
        if(size != noSizeGiven) // Wenn die Größe über Content-length gegeben wurde
        {
            cout << "0%";
C/C++ Code:
if(size != noSizeGiven) // Wenn die Größe über Content-length gegeben wurde
{
cout << "0%";
C/C++ Code:
        if(size != noSizeGiven) // Wenn die Größe über Content-length gegeben wurde
        {
            cout << "0%";


Wenn eine Größe übergeben wurde, können wir eine Fortschrittsanzeige ausgeben.
Wir empfangen so lange, bis die gesamte Datei übertragen wurde:

C/C++ Code:
            while(recvSize < size)
            {
C/C++ Code:
while(recvSize < size)
{
C/C++ Code:
            while(recvSize < size)
            {


Nun schreiben wir die Daten, die wir auf unserem Socket empfangen, in den Buffer:

C/C++ Code:
                if((bytesRecv = recv(Socket, buf, sizeof(buf), 0)) <= 0)
                {
                    throw CreateSocketError();
                }
C/C++ Code:
if((bytesRecv = recv(Socket, buf, sizeof(buf), 0)) <= 0)
{
throw CreateSocketError();
}
C/C++ Code:
                if((bytesRecv = recv(Socket, buf, sizeof(buf), 0)) <= 0)
                {
                    throw CreateSocketError();
                }


Falls recv einen Wert kleiner oder gleich 0 zurück gibt, brechen wir ab und werfen eine Exception.
Nun addieren wir die aktuell empfange Anzahl an Bytes zu der Gesamtanzahl und schreiben den Buffer in die Datei:

C/C++ Code:
                recvSize += bytesRecv;
                fout.write(buf, bytesRecv);
C/C++ Code:
recvSize += bytesRecv;
fout.write(buf, bytesRecv);
C/C++ Code:
                recvSize += bytesRecv;
                fout.write(buf, bytesRecv);


Als letztes geben wir noch eine Prozentanzeige aus und sind auch schon fertig:

C/C++ Code:
                cout << "\r" << recvSize * 100 / size << "%" << flush; // Mit \r springen wir an den Anfang der Zeile
            }
        }
C/C++ Code:
cout << "\r" << recvSize * 100 / size << "%" << flush; // Mit \r springen wir an den Anfang der Zeile
}
}
C/C++ Code:
                cout << "\r" << recvSize * 100 / size << "%" << flush; // Mit \r springen wir an den Anfang der Zeile
            }
        }


Nun müssen wir noch die anderen zwei Transfer-Arten implementieren:

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
        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);
                }
            }
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
}
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
        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);
                }
            }


Dies ist die normale Übertragung ohne Angabe von Dateigröße. Der Unterschied besteht darin, dass wir nicht wissen, wann die Übertragung zu Ende ist, sondern einfach auf einen Verbindungsabbruch warten müssen.

6.4 Chunked Transfer-Encoding

Das letzte was uns nun noch fehlt, ist die Übertragung mit Chunks. Dazu müssen wir uns wieder den Aufbau angucken:

Code:
1
2
3
4
5
6
7
8
9
10
11
1
2
3
4
5
6
7
8
9
10
11
HTTP/1.1 200 OK
Transfer-Encoding: chunked

<Größe des Chunks in Hex>
<Daten des Chunks>
<Größe des zweiten Chunks in Hex>
<Daten des zweiten Chunks>
0
Weiteres Argument: Blabla
Noch ein Argument: 123
(hier eine leere Zeile)
Code:
1
2
3
4
5
6
7
8
9
10
11
HTTP/1.1 200 OK
Transfer-Encoding: chunked

<Größe des Chunks in Hex>
<Daten des Chunks>
<Größe des zweiten Chunks in Hex>
<Daten des zweiten Chunks>
0
Weiteres Argument: Blabla
Noch ein Argument: 123
(hier eine leere Zeile)
Code:
1
2
3
4
5
6
7
8
9
10
11
HTTP/1.1 200 OK
Transfer-Encoding: chunked

<Größe des Chunks in Hex>
<Daten des Chunks>
<Größe des zweiten Chunks in Hex>
<Daten des zweiten Chunks>
0
Weiteres Argument: Blabla
Noch ein Argument: 123
(hier eine leere Zeile)


Anstelle der Daten folgt bei dieser Übertragungs-Art eine Zeile in hexadezimalen Schreibweise, der wir die Größe des Chunks in Bytes entnehmen können. Daraufhin kommen die Daten, gefolgt von einem Zeilenumbruch. Nun beginnt entweder ein neuer Chunk, oder es wird die Größe 0 übergeben, was für das Ende der Chunks steht. Hiernach können noch weitere Argumente folgen, die für uns aber unwichtig sind. Hauptsache wir haben die Datei und dann schnell weg :P.

Machen wir also weiter im Code, wo wir vorhin aufgehört haben:

C/C++ Code:
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
            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
C/C++ Code:
1
2
3
4
5
6
7
8
9
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
C/C++ Code:
1
2
3
4
5
6
7
8
9
            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


Wir lesen die erste Zeile ein und schreiben die hexadezimale Zahl in size. Hierzu brauchen wir nur das "hex"-Flag setzen, damit der Stream auch das Hexadezimalsystem anwendet.
Wenn die Größe kleiner oder gleich 0 ist, verlassen wir unsere Schleife:

C/C++ Code:
                    if(chunkSize <= 0)
                    {
                        break;
                    }
C/C++ Code:
if(chunkSize <= 0)
{
break;
}
C/C++ Code:
                    if(chunkSize <= 0)
                    {
                        break;
                    }


Nun beginnen wir eine Schleife die den aktuellen Chunk runterlädt:

C/C++ Code:
                    cout << "Downloading Part (" << chunkSize << " Bytes)... " << endl;
                    recvSize = 0; // Vor jeder Schleife wieder auf 0 setzen
                    while(recvSize < chunkSize)
                    {
C/C++ Code:
cout << "Downloading Part (" << chunkSize << " Bytes)... " << endl;
recvSize = 0; // Vor jeder Schleife wieder auf 0 setzen
while(recvSize < chunkSize)
{
C/C++ Code:
                    cout << "Downloading Part (" << chunkSize << " Bytes)... " << endl;
                    recvSize = 0; // Vor jeder Schleife wieder auf 0 setzen
                    while(recvSize < chunkSize)
                    {


Nun erstellen wir uns eine Variable, die nur speichert, wie viel Bytes wir diesen Schleifendurchgang noch empfangen müssen:

C/C++ Code:
                        int bytesToRecv = chunkSize - recvSize;
C/C++ Code:
int bytesToRecv = chunkSize - recvSize;
C/C++ Code:
                        int bytesToRecv = chunkSize - recvSize;


Dies ist nämlich notwendig, da wir ja am Ende unseres Chunks, nicht mehr empfangen dürfen als notwendig, sonst könnten wir z.B. versehentlich schon die Größe des nächsten Chunks in unsere Datei schreiben und das wäre fatal. Deswegen übergeben wir an recv als Größe entweder sizeof(buf) oder aber bytesToRecv, wenn wir nur noch ein kleines Stück empfangen müssen:

C/C++ Code:
                        if((bytesRecv = recv(Socket, buf, bytesToRecv > sizeof(buf) ? sizeof(buf) : bytesToRecv, 0)) <= 0)
                        {
                            throw CreateSocketError();
                        }
C/C++ Code:
if((bytesRecv = recv(Socket, buf, bytesToRecv > sizeof(buf) ? sizeof(buf) : bytesToRecv, 0)) <= 0)
{
throw CreateSocketError();
}
C/C++ Code:
                        if((bytesRecv = recv(Socket, buf, bytesToRecv > sizeof(buf) ? sizeof(buf) : bytesToRecv, 0)) <= 0)
                        {
                            throw CreateSocketError();
                        }


Nun addieren wir wie gewohnt die empfangenen Bytes und geben eine Prozentanzeige aus:

C/C++ Code:
                        recvSize += bytesRecv;
                        fout.write(buf, bytesRecv);
                        cout << "\r" << recvSize * 100 / chunkSize << "%" << flush;
                    }
                    cout << endl;
C/C++ Code:
recvSize += bytesRecv;
fout.write(buf, bytesRecv);
cout << "\r" << recvSize * 100 / chunkSize << "%" << flush;
}
cout << endl;
C/C++ Code:
                        recvSize += bytesRecv;
                        fout.write(buf, bytesRecv);
                        cout << "\r" << recvSize * 100 / chunkSize << "%" << flush;
                    }
                    cout << endl;


Nun haben wir alles empfangen, doch halt: Haben wir nicht noch etwas vergessen? Richtig: Nach den Daten erfolgt ein Zeilenumbruch. Dieser besteht aus einem \r und einem \n, also 2 Bytes. Es ist also am einfachesten wenn wir eben 2 Bytes vom Socket ignorieren:

C/C++ Code:
                    for(int i = 0; i < 2; ++i)
                    {
                        char temp;
                        recv(Socket, &temp, 1, 0);
                    }
C/C++ Code:
for(int i = 0; i < 2; ++i)
{
char temp;
recv(Socket, &temp, 1, 0);
}
C/C++ Code:
                    for(int i = 0; i < 2; ++i)
                    {
                        char temp;
                        recv(Socket, &temp, 1, 0);
                    }


Nun geben wir noch ein "Finished!" aus und unser Programm ist fertig:

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
                }
            }
        }
        cout << endl << "Finished!" << endl;
    }
    catch(exception& e)
    {
        cout << endl;
        cerr << e.what() << endl;
    }
#ifdef
linux
    close(Socket); // Verbindung beenden
#else

    closesocket(Socket); // Windows-Variante
#endif

}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
}
}
}
cout << endl << "Finished!" << endl;
}
catch(exception& e)
{
cout << endl;
cerr << e.what() << endl;
}
#ifdef
linux
close(Socket); // Verbindung beenden
#else

closesocket(Socket); // Windows-Variante
#endif

}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
                }
            }
        }
        cout << endl << "Finished!" << endl;
    }
    catch(exception& e)
    {
        cout << endl;
        cerr << e.what() << endl;
    }
#ifdef
linux
    close(Socket); // Verbindung beenden
#else

    closesocket(Socket); // Windows-Variante
#endif

}


Das ganze Programm sollte uns jetzt die faq.html-Datei herrunterladen und auf der Festplatte speichern. Nun sind wir schon fast fertig.


7 Der letzte Feinschliff

Als letztes wollen wir unser Programm um ein paar Funktionen ergänzen, die eine Eingabe einer URL ermöglichen. Schließlich will man nicht immer nur eine Datei herunterladen. Dazu fügen wir am Anfang eine Eingabe hinzu:

C/C++ Code:
int main()
{
    using namespace std;

    cout << "URL: ";
    string URL;
    cin >> URL; // User gibt URL der Datei ein, die heruntergeladen werden soll
C/C++ Code:
int main()
{
using namespace std;

cout << "URL: ";
string URL;
cin >> URL; // User gibt URL der Datei ein, die heruntergeladen werden soll
C/C++ Code:
int main()
{
    using namespace std;

    cout << "URL: ";
    string URL;
    cin >> URL; // User gibt URL der Datei ein, die heruntergeladen werden soll


Nun müssen wir ein vor der URL evtl. existierendes http:// entfernen:

C/C++ Code:
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
// Entfernt das http:// vor der URL
void RemoveHttp(std::string& URL)
{
    size_t pos = URL.find("http://");
    if(pos != std::string::npos)
    {
        URL.erase(0, 7);
    }
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
// Entfernt das http:// vor der URL
void RemoveHttp(std::string& URL)
{
size_t pos = URL.find("http://");
if(pos != std::string::npos)
{
URL.erase(0, 7);
}
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
// Entfernt das http:// vor der URL
void RemoveHttp(std::string& URL)
{
    size_t pos = URL.find("http://");
    if(pos != std::string::npos)
    {
        URL.erase(0, 7);
    }
}


Diese Funktion rufen wir nun nach dem Start von WinSock auf:

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
#ifndef linux
    WSADATA w;
    if(int result = WSAStartup(MAKEWORD(2,2), &w) != 0)
    {
        cout << "Winsock 2 konnte nicht gestartet werden! Error #" << result << endl;
        return 1;
    }
#endif


    RemoveHttp(URL);
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
#ifndef linux
WSADATA w;
if(int result = WSAStartup(MAKEWORD(2,2), &w) != 0)
{
cout << "Winsock 2 konnte nicht gestartet werden! Error #" << result << endl;
return 1;
}
#endif


RemoveHttp(URL);
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
#ifndef linux
    WSADATA w;
    if(int result = WSAStartup(MAKEWORD(2,2), &w) != 0)
    {
        cout << "Winsock 2 konnte nicht gestartet werden! Error #" << result << endl;
        return 1;
    }
#endif


    RemoveHttp(URL);


Jetzt extrahieren wir den Hostnamen aus der URL und speichern ihn in einem neuen String ab:

C/C++ Code:
    string hostname = RemoveHostname(URL);
C/C++ Code:
string hostname = RemoveHostname(URL);
C/C++ Code:
    string hostname = RemoveHostname(URL);


Die RemoveHostname-Funktion sieht so aus:

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 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;
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 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;
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 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;
}


Nun lösen wir den Hostnamen in dem neuen String auf:

C/C++ Code:
    hostent* phe = gethostbyname(hostname.c_str());
C/C++ Code:
hostent* phe = gethostbyname(hostname.c_str());
C/C++ Code:
    hostent* phe = gethostbyname(hostname.c_str());


Es folgt der Aufbau der Verbindung:

C/C++ Code:
    if(phe == NULL)
    // ...
    cout << "Verbindung erfolgreich!" << endl;
C/C++ Code:
if(phe == NULL)
// ...
cout << "Verbindung erfolgreich!" << endl;
C/C++ Code:
    if(phe == NULL)
    // ...
    cout << "Verbindung erfolgreich!" << endl;


Als nächstes müssen wir eine HTTP-Request erstellen:

C/C++ Code:
    string request = "GET ";
    request += URL;    // z.B. /faq/index.html
    request += " HTTP/1.1\n";
    request += "Host: " + hostname + "\nConnection: close\n\n";
C/C++ Code:
string request = "GET ";
request += URL; // z.B. /faq/index.html
request += " HTTP/1.1\n";
request += "Host: " + hostname + "\nConnection: close\n\n";
C/C++ Code:
    string request = "GET ";
    request += URL;    // z.B. /faq/index.html
    request += " HTTP/1.1\n";
    request += "Host: " + hostname + "\nConnection: close\n\n";


Diesen übergeben wir jetzt an unsere SendAll-Funktion und danach folgt der Code aus 05.cpp bis zur Öffnung des Filestreams:

C/C++ Code:
    try
    {
        SendAll(Socket, request.c_str(), request.size()); // Anfrage an Server senden
        int code = 100; // 100 = Continue
        // ...

        }
C/C++ Code:
try
{
SendAll(Socket, request.c_str(), request.size()); // Anfrage an Server senden
int code = 100; // 100 = Continue
// ...

}
C/C++ Code:
    try
    {
        SendAll(Socket, request.c_str(), request.size()); // Anfrage an Server senden
        int code = 100; // 100 = Continue
        // ...

        }


Nun muss die Datei erstellt und der Inhalt eingelesen werden. Das größte Problem ist hierbei der Dateiname: Bei vielen Fällen lässt er sich extrahieren aber manchmal ist er auch unbekannt (z.B. www.google.de). Die sauberste Methode wäre es, das Response-Argument "Content-Type:" auszuwerten, dies wollen wir uns aber mal sparen und nennen unsere Datei einfach download und hängen eine Endung an, falls diese in der URL enhalten ist:

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Gibt die Dateiendung in der 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;
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Gibt die Dateiendung in der 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;
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Gibt die Dateiendung in der 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;
}


C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
        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

        // ...


    }
    catch(exception& e)
    {
        cout << endl;
        cerr << e.what() << endl;
    }
#ifdef
linux
    close(Socket); // Verbindung beenden
#else

    closesocket(Socket); // Windows-Variante
#endif

}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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

// ...


}
catch(exception& e)
{
cout << endl;
cerr << e.what() << endl;
}
#ifdef
linux
close(Socket); // Verbindung beenden
#else

closesocket(Socket); // Windows-Variante
#endif

}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
        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

        // ...


    }
    catch(exception& e)
    {
        cout << endl;
        cerr << e.what() << endl;
    }
#ifdef
linux
    close(Socket); // Verbindung beenden
#else

    closesocket(Socket); // Windows-Variante
#endif

}


Fertig! Das komplette Programm können Sie sich hier herunterladen.


8 Nachwort

Falls Sie eine URL finden sollten, die dieses Programm nicht herunterladen kann, können Sie auf mein Tutorial antworten und mir den Link mitteilen, damit ich es mir ansehen kann.
Bei allgemeinen Fragen zum Thema Sockets können Sie in der WinAPI- oder der Linux/Unix-Abteilung nachfragen.

In meinem Code rufe ich recv auf, um nur ein einziges Byte zu empfangen. Dies sollte man in der Praxis möglichst unterlassen und immer eine angemesse Buffergröße wählen, z.B. 4 KB. Hierdurch kann recv uns einzelne TCP-Packete im Ganzen zurück geben. Um trotzdem zeilenweise einzulesen muss man einfach die recv-Funktion in seiner eigenen Funktion kapseln. Dies sollte man sowieso tun, da recv und send sehr low-level sind. Ein auf diese Weise verändertes Programm sähe so aus.
Desweiteren wurde die gethostbyname-Funktion von der flexibleren getaddrinfo-Funktion ersetzt. Eine Erklärung sowie Beispiele finden Sie hier.

Ich bedanke mich bei jcmds, flammenvogel, scrub, GPC, Artchi, predator und allen anderen aus der Redaktion, die mir geholfen haben. Das war's von mir, vielen Dank fürs Lesen!

9 Linkliste

Socket-Referenz
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/wsastartup_2.asp

Socket-Tutorial
http://beej.us/guide/bgnet/

HTTP-Tutorial
http://www.jmarshall.com/easy/http/

RFC für HTTP/1.1
http://tools.ietf.org/html/rfc2616


Zuletzt bearbeitet von joomoo am 23:32:28 28.10.2008, insgesamt 26-mal bearbeitet
ten
Mitglied

Benutzerprofil
Anmeldungsdatum: 17.12.2006
Beiträge: 1277
Beitrag ten Mitglied 11:06:34 10.01.2007   Titel:   Re: Sockets und das HTTP-Protokoll            Zitieren

joomoo schrieb:

C/C++ Code:
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
unsigned short my_htons(unsigned short h)
{
    char* p = reinterpret_cast<char*>(&h);
    char n[2];
    n[0] = p[1];
    n[1] = p[0];
    return *reinterpret_cast<unsigned short*>(n);
}
C/C++ Code:
1
2
3
4
5
6
7
8
unsigned short my_htons(unsigned short h)
{
char* p = reinterpret_cast<char*>(&h);
char n[2];
n[0] = p[1];
n[1] = p[0];
return *reinterpret_cast<unsigned short*>(n);
}
C/C++ Code:
1
2
3
4
5
6
7
8
unsigned short my_htons(unsigned short h)
{
    char* p = reinterpret_cast<char*>(&h);
    char n[2];
    n[0] = p[1];
    n[1] = p[0];
    return *reinterpret_cast<unsigned short*>(n);
}


so'n umständlichen endiantauscher hab' ich noch nie gesehen :eek:
mach so:
C# Code:
unsigned short my_htons (unsigned short h)
{
   return (h>>8) | (h<<8);
}
C# Code:
unsigned short my_htons (unsigned short h)
{
return (h>>8) | (h<<8);
}
C# Code:
unsigned short my_htons (unsigned short h)
{
   return (h>>8) | (h<<8);
}

oder so:
Code:
#ifdef BIG_ENDIAN
#define MY_HTONS(h) (h)
#else
#define MY_HTONS(h) ((h>>8)|(h<<8))
#endif
Code:
#ifdef BIG_ENDIAN
#define MY_HTONS(h) (h)
#else
#define MY_HTONS(h) ((h>>8)|(h<<8))
#endif
Code:
#ifdef BIG_ENDIAN
#define MY_HTONS(h) (h)
#else
#define MY_HTONS(h) ((h>>8)|(h<<8))
#endif

:xmas2:

_________________
god bless the circle jerks, god bless rodney on the roq, god bless skateboards.
addr
Unregistrierter




Beitrag addr Unregistrierter 11:54:14 10.01.2007   Titel:              Zitieren

gethostbyname sollte man nicht mehr benutzen, sondern man sollte getaddrinfo verwenden.
addr
Unregistrierter




Beitrag addr Unregistrierter 11:55:49 10.01.2007   Titel:              Zitieren

http://www.c-plusplus.de/magazin/bilder/sockets_und_das_http-protokoll/07.cpp

Der Link funktioniert nicht.
addr
Unregistrierter




Beitrag addr Unregistrierter 12:05:16 10.01.2007   Titel:              Zitieren

Zitat:
Dies signalisiert uns send(), indem es uns einen Wert gleich 0, für eine normale Beendigung der Verbindung


Das ist IMHO falsch. Wenn die Verbindung venünftig beendet wird kriegt man das nur über recv mit.


Zu WSAStartup: Wenn das fehlschlägt darf man WSAGetLastError nicht benutzen, sondern man muss den Rückgabewert als Fehlercode nehmen.
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 17:28:29 10.01.2007   Titel:              Zitieren

Hallo addr,

Vielen Dank für die Hinweise, die Datei hab ich gerade eben hochgeladen.
Die anderen Sachen schau ich mir auch eben an und antworte gleich nochmal (bin gerade nach Hause gekommen).

mfg.
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 17:34:45 10.01.2007   Titel:   Re: Sockets und das HTTP-Protokoll            Zitieren

ten schrieb:
joomoo schrieb:

C/C++ Code:
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
unsigned short my_htons(unsigned short h)
{
    char* p = reinterpret_cast<char*>(&h);
    char n[2];
    n[0] = p[1];
    n[1] = p[0];
    return *reinterpret_cast<unsigned short*>(n);
}
C/C++ Code:
1
2
3
4
5
6
7
8
unsigned short my_htons(unsigned short h)
{
char* p = reinterpret_cast<char*>(&h);
char n[2];
n[0] = p[1];
n[1] = p[0];
return *reinterpret_cast<unsigned short*>(n);
}
C/C++ Code:
1
2
3
4
5
6
7
8
unsigned short my_htons(unsigned short h)
{
    char* p = reinterpret_cast<char*>(&h);
    char n[2];
    n[0] = p[1];
    n[1] = p[0];
    return *reinterpret_cast<unsigned short*>(n);
}


so'n umständlichen endiantauscher hab' ich noch nie gesehen :eek:
mach so:
C# Code:
unsigned short my_htons (unsigned short h)
{
   return (h>>8) | (h<<8);
}
C# Code:
unsigned short my_htons (unsigned short h)
{
return (h>>8) | (h<<8);
}
C# Code:
unsigned short my_htons (unsigned short h)
{
   return (h>>8) | (h<<8);
}


Klar ist der Endiantauscher umständlich, doch ich hab ihn ja auch nicht zum Einsetzen geschrieben sondern um die Funktionsweise zu demonstrieren. Deswegen wollte ich lieber auf Bitoperatoren verzichten.

mfg.

edit: Dies war ja gar nicht addr, sondern ten. Auch an dich ein großes Dankeschön!


Zuletzt bearbeitet von joomoo am 19:20:24 10.01.2007, insgesamt 1-mal bearbeitet
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 19:19:14 10.01.2007   Titel:              Zitieren

Zitat:
gethostbyname sollte man nicht mehr benutzen, sondern man sollte getaddrinfo verwenden.


Oh, das wusste ich gar nicht. Ich hatte mich nach Beej's Network Guide gerichtet. Ändern kann ich es leider nicht mehr aber ich habe eine Bemerkung am Ende ergänzt.

Zitat:
Das ist IMHO falsch. Wenn die Verbindung venünftig beendet wird kriegt man das nur über recv mit.

Tatsächlich. Vielen Dank, ich hab es jetzt im Artikel korrigiert.

Zitat:
Zu WSAStartup: Wenn das fehlschlägt darf man WSAGetLastError nicht benutzen, sondern man muss den Rückgabewert als Fehlercode nehmen.

Danke, ich hab's jetzt geändert und gebe den Rückgabewert aus.

mfg.
Tim
Mitglied

Benutzerprofil
Anmeldungsdatum: 30.11.2004
Beiträge: 6859
Beitrag Tim Mitglied 11:14:12 11.01.2007   Titel:              Zitieren

Das eingescannte Bild ist einfach nur cool :cool: :live:

_________________
Vorsicht, dieser Benutzer ist manisch-depressiv oder schizoid!
Professionell diagnostiziert durch Internet-Hobby-Psychologen
//
Unregistrierter




Beitrag // Unregistrierter 11:31:38 11.01.2007   Titel:              Zitieren

Das eingescannte Bild wirkt einfach nur billig.
Artchi
Autor

Benutzerprofil
Anmeldungsdatum: 16.03.2002
Beiträge: 8576
Beitrag Artchi Autor 11:54:44 11.01.2007   Titel:              Zitieren

Die Kompressionsrate des JPEGs ist nur zu hoch. Man kann deshalb fast nichts erkennen. Ansonst hat das Bild irgendwie einen gewissen Charme. :D

_________________
Bring back the Windows Start Menu Petition | GoPetition
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 17:54:19 11.01.2007   Titel:              Zitieren

Ja das Bid ist ein bisschen billig, das hab ich in einer langweiligen Geschichtsstunde gemalt :D

Falls jemand Zeit und Lust hat kann er ja versuchen es durch eine schöne Grafik zu ersetzen, ich kenn mich leider mit sowas nicht aus.

mfg.
Cryponaut
Unregistrierter




Beitrag Cryponaut Unregistrierter 21:00:58 12.01.2007   Titel:              Zitieren

Ich hab den Text zwischen den Zeilen auf dem Bild verstanden.
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 23:06:41 12.01.2007   Titel:              Zitieren

scrub war so nett und hatt das Bild nachgebaut. Das neue Bild könnt ihr schon im Artikel bewundern, das alte Bild gibt's noch hier:
http://www.c-plusplus.de/magazin/bilder/sockets_und_das_http-protokoll/hostent.jpg

@scrub
Vielen Dank für das Bild!! Ich hab noch eine Kleinigkeit geändert und die Auflösung etwas runter gesetzt damit auch 1024-User in den Genuss kommen.

mfg.
Fye
Mitglied

Benutzerprofil
Anmeldungsdatum: 10.12.2006
Beiträge: 88
Beitrag Fye Mitglied 01:21:01 14.01.2007   Titel:              Zitieren

joomoo schrieb:
C/C++ Code:
#ifdef linux
#include
<sys/socket.h> // socket(), connect()
#include
<arpa/inet.h> // sockaddr_in
#else
#include
<winsock2.h>
#endif
C/C++ Code:
#ifdef linux
#include
<sys/socket.h> // socket(), connect()
#include
<arpa/inet.h> // sockaddr_in
#else
#include
<winsock2.h>
#endif
C/C++ Code:
#ifdef linux
#include
<sys/socket.h> // socket(), connect()
#include
<arpa/inet.h> // sockaddr_in
#else
#include
<winsock2.h>
#endif


Das ist ziemlich unportabel. Besser ist:

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* all UNIX-like OSs (Linux, *BSD, MacOSX, Solaris, ...) */
#if
defined(unix) || defined(__unix) || defined(__unix__)
#include
<sys/socket.h>
#include
<arpa/inet.h>
/* MS Windows */
#elif
defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__)
#include
<winsock2.h>
/* IBM OS/2 */
#elif
defined(OS2) || defined(_OS2) || defined(__OS2__) || defined(__TOS_OS2__)
#include
<sys/socket.h>
#include
<arpa/nameser.h>
#else
#error
unsupported or unknown operating system.
#endif
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* all UNIX-like OSs (Linux, *BSD, MacOSX, Solaris, ...) */
#if
defined(unix) || defined(__unix) || defined(__unix__)
#include
<sys/socket.h>
#include
<arpa/inet.h>
/* MS Windows */
#elif
defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__)
#include
<winsock2.h>
/* IBM OS/2 */
#elif
defined(OS2) || defined(_OS2) || defined(__OS2__) || defined(__TOS_OS2__)
#include
<sys/socket.h>
#include
<arpa/nameser.h>
#else
#error
unsupported or unknown operating system.
#endif
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* all UNIX-like OSs (Linux, *BSD, MacOSX, Solaris, ...) */
#if
defined(unix) || defined(__unix) || defined(__unix__)
#include
<sys/socket.h>
#include
<arpa/inet.h>
/* MS Windows */
#elif
defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__)
#include
<winsock2.h>
/* IBM OS/2 */
#elif
defined(OS2) || defined(_OS2) || defined(__OS2__) || defined(__TOS_OS2__)
#include
<sys/socket.h>
#include
<arpa/nameser.h>
#else
#error
unsupported or unknown operating system.
#endif
ten
Mitglied

Benutzerprofil
Anmeldungsdatum: 17.12.2006
Beiträge: 1277
Beitrag ten Mitglied 01:26:57 14.01.2007   Titel:              Zitieren

Fye schrieb:

Besser ist...

das ist ja schon mal ein anfang. kannst es ja noch ergänzen: http://predef.sourceforge.net/preos.html

_________________
god bless the circle jerks, god bless rodney on the roq, god bless skateboards.
Fye
Mitglied

Benutzerprofil
Anmeldungsdatum: 10.12.2006
Beiträge: 88
Beitrag Fye Mitglied 01:36:12 14.01.2007   Titel:              Zitieren

Ich hab nicht gesagt, dass das vollständig ist. Ich mein halt, sich auf Windows und Linux zu beschränken und die armen BSD'ler und MacOSX'ler außen vor zu lassen ist nicht so toll. ;)

BTW: Die Seite kenne ich. Was meinste, wo ich die Makros alle herhabe? ;)


Zuletzt bearbeitet von Fye am 01:39:47 14.01.2007, insgesamt 1-mal bearbeitet
ten
Mitglied

Benutzerprofil
Anmeldungsdatum: 17.12.2006
Beiträge: 1277
Beitrag ten Mitglied 01:43:59 14.01.2007   Titel:              Zitieren

Fye schrieb:

Ich hab nicht gesagt, dass das vollständig ist. Ich mein halt, sich auf Windows und Linux zu beschränken und die armen BSD'ler und MacOSX'ler außen vor zu lassen ist nicht so toll. ;)

aber os-halbe wird berücksichtigt. ein system dass seit 10 jahren kein schwein mehr verwendet ;)

Fye schrieb:

BTW: Die Seite kenne ich. Was meinste, wo ich die Makros alle herhabe? ;)

selbst gebastelt? ich dachte das hast du aus irgendso'nem open source projekt rauskopiert...
:)

_________________
god bless the circle jerks, god bless rodney on the roq, god bless skateboards.
*******
Unregistrierter




Beitrag ******* Unregistrierter 18:18:42 14.01.2007   Titel:              Zitieren

Müsste es nicht HTT-Protokoll und FT-Protokoll heißen?
ck
Unregistrierter




Beitrag ck Unregistrierter 03:06:13 25.03.2007   Titel:              Zitieren

Zitat:
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
void SendAll(int socket, const char* const buf, const size_t size)
{
    size_t bytesSent = 0; // Anzahl Bytes die wir bereits vom Buffer gesendet haben
    do
    {
        bytesSent += send(socket, buf + bytesSent, size, 0);
        if(bytesSent < 0) // Wenn send einen Wert < 0 zurück gibt deutet dies auf einen Fehler hin.
        {
            throw CreateSocketError();
        }
    } while(bytesSent < size);
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
void SendAll(int socket, const char* const buf, const size_t size)
{
size_t bytesSent = 0; // Anzahl Bytes die wir bereits vom Buffer gesendet haben
do
{
bytesSent += send(socket, buf + bytesSent, size, 0);
if(bytesSent < 0) // Wenn send einen Wert < 0 zurück gibt deutet dies auf einen Fehler hin.
{
throw CreateSocketError();
}
} while(bytesSent < size);
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
void SendAll(int socket, const char* const buf, const size_t size)
{
    size_t bytesSent = 0; // Anzahl Bytes die wir bereits vom Buffer gesendet haben
    do
    {
        bytesSent += send(socket, buf + bytesSent, size, 0);
        if(bytesSent < 0) // Wenn send einen Wert < 0 zurück gibt deutet dies auf einen Fehler hin.
        {
            throw CreateSocketError();
        }
    } while(bytesSent < size);
}


Ich habe den Artikel nur kurz überflogen, aber das sieht falsch aus - muss der dritte Parameter nicht size - bytesSent sein? Sonst liest send() unter Umständen über den Puffer hinaus. Außerdem kann es nach dem ersten Schleifendurchlauf passieren, dass bytesSent schon einen positiven Wert hat, send() etwas negatives zurückgibt und bytesSent danach aber immer noch positiv ist.
Parallan
Unregistrierter




Beitrag Parallan Unregistrierter 20:01:28 26.03.2007   Titel:              Zitieren

ck schrieb:
Zitat:
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
void SendAll(int socket, const char* const buf, const size_t size)
{
    size_t bytesSent = 0; // Anzahl Bytes die wir bereits vom Buffer gesendet haben
    do
    {
        bytesSent += send(socket, buf + bytesSent, size, 0);
        if(bytesSent < 0) // Wenn send einen Wert < 0 zurück gibt deutet dies auf einen Fehler hin.
        {
            throw CreateSocketError();
        }
    } while(bytesSent < size);
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
void SendAll(int socket, const char* const buf, const size_t size)
{
size_t bytesSent = 0; // Anzahl Bytes die wir bereits vom Buffer gesendet haben
do
{
bytesSent += send(socket, buf + bytesSent, size, 0);
if(bytesSent < 0) // Wenn send einen Wert < 0 zurück gibt deutet dies auf einen Fehler hin.
{
throw CreateSocketError();
}
} while(bytesSent < size);
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
void SendAll(int socket, const char* const buf, const size_t size)
{
    size_t bytesSent = 0; // Anzahl Bytes die wir bereits vom Buffer gesendet haben
    do
    {
        bytesSent += send(socket, buf + bytesSent, size, 0);
        if(bytesSent < 0) // Wenn send einen Wert < 0 zurück gibt deutet dies auf einen Fehler hin.
        {
            throw CreateSocketError();
        }
    } while(bytesSent < size);
}


Ich habe den Artikel nur kurz überflogen, aber das sieht falsch aus - muss der dritte Parameter nicht size - bytesSent sein? Sonst liest send() unter Umständen über den Puffer hinaus. Außerdem kann es nach dem ersten Schleifendurchlauf passieren, dass bytesSent schon einen positiven Wert hat, send() etwas negatives zurückgibt und bytesSent danach aber immer noch positiv ist.


glaube ich nicht, es muss wenn überahaut so heissen:
C/C++ Code:
        bytesSent += send(socket, buf + bytesSent, size - bytesSent, 0);
C/C++ Code:
bytesSent += send(socket, buf + bytesSent, size - bytesSent, 0);
C/C++ Code:
        bytesSent += send(socket, buf + bytesSent, size - bytesSent, 0);
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 18:24:24 28.03.2007   Titel:              Zitieren

Hallo ck und Parallan,

Vielen Dank! Ihr habt natürlich Recht, ich wunder' mich warum ich so einen extremen Fehler machen konnte...
Hier mal die überarbeite Version:

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
1
2
3
4
5
6
7
8
9
10
11
12
13
void SendAll(int socket, const char* const buf, const size_t size)
{
    size_t bytesSent = 0; // Anzahl Bytes die wir bereits vom Buffer gesendet haben
    do
    {
        size_t 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);
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
void SendAll(int socket, const char* const buf, const size_t size)
{
size_t bytesSent = 0; // Anzahl Bytes die wir bereits vom Buffer gesendet haben
do
{
size_t 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);
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
void SendAll(int socket, const char* const buf, const size_t size)
{
    size_t bytesSent = 0; // Anzahl Bytes die wir bereits vom Buffer gesendet haben
    do
    {
        size_t 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);
}

Kann ich das so übernehmen?

mfg.
ck
Unregistrierter




Beitrag ck Unregistrierter 21:38:27 29.03.2007   Titel:              Zitieren

Zumindest unter Linux gibt send nicht size_t sondern ssize_t zurück (und die MSDN sagt int, für Windows). size_t ist meines Wissens jedenfalls immer unsigned, deshalb ist die if(result < 0)-Abfrage nie falsch. Ich denke es reicht aus, für result einfach int zu nehmen. Sonst sieht es IMHO gut aus :)
vista
Mitglied

Benutzerprofil
Anmeldungsdatum: 13.03.2007
Beiträge: 714
Beitrag vista Mitglied 22:06:27 29.03.2007   Titel:              Zitieren

ck schrieb:

size_t ist meines Wissens jedenfalls immer unsigned, deshalb ist die if(result < 0)-Abfrage nie falsch.

nie 'wahr' ;)
aber hast recht, es muss ein signed-typ sein damit das funzt.
übrigens: dieses 'throw' argument sollte besser 'SendError' oder so heissen ;)

_________________
http://www.youtube.com/watch?v=FSW3xl3F0fc
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 20:08:44 01.04.2007   Titel:              Zitieren

Vielen Dank! Ihr habt Recht, ich weiß auch nicht wie ich auf size_t gekommen bin. Ich hab's jetzt schon im Artikel korrigiert, auf den FTP-Server komm ich gerade nicht drauf...

Die CreateSocketError funktion hab ich so genannt, da sie ja noch nicht wirft sondern erstmal den SocketError erstellt.

mfg.
Ben04
Autor

Benutzerprofil
Anmeldungsdatum: 10.10.2004
Beiträge: 1635
Beitrag Ben04 Autor 21:18:43 01.04.2007   Titel:              Zitieren

Zitat:
C/C++ Code:
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
#else
    int
error = WSAGetLastError();
    char* msg;
    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                  NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                  reinterpret_cast<char*>(&msg), 0, NULL);
    temp << "Socket-Fehler #" << error << ": " << msg;
#endif
C/C++ Code:
1
2
3
4
5
6
7
8
#else
int
error = WSAGetLastError();
char* msg;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<char*>(&msg), 0, NULL);
temp << "Socket-Fehler #" << error << ": " << msg;
#endif
C/C++ Code:
1
2
3
4
5
6
7
8
#else
    int
error = WSAGetLastError();
    char* msg;
    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                  NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                  reinterpret_cast<char*>(&msg), 0, NULL);
    temp << "Socket-Fehler #" << error << ": " << msg;
#endif

Fehlt da nicht ein LocalFree? Und Leider kann auch FormatMessage fehl schlagen in welchem Fall das Program wahrscheinlich abstürzen würde. Dies passiert wenn kein Text gefunden wird was bei lokalisierten System leider hin und wieder passiert.
Peter01
Mitglied

Benutzerprofil
Anmeldungsdatum: 26.08.2005
Beiträge: 36
Beitrag Peter01 Mitglied 18:16:52 02.04.2007   Titel:   Re: Sockets und das HTTP-Protokoll            Zitieren

@joomoo

Also weil's hier noch niemand getan hat: Vielen Dank für die tolle Anleitung. Solche Sachen geben immer einen Haufen Arbeit und das muss man auch mal loben :live:

Gerade bei der Verarbeitung von 'chunked' Mode hilft mir dein Text jetzt weiter.

Gruss, Peter
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 11:48:24 04.04.2007   Titel:              Zitieren

@Ben04

Danke das hab ich vergessen! Ich kenn mich leider nicht so aus mit den Windows-Funktionen und hab's jetzt so abgeändert:

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
    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))
    {
        temp << ": " << msg;
        LocalFree(msg);
    }
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
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))
{
temp << ": " << msg;
LocalFree(msg);
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
    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))
    {
        temp << ": " << msg;
        LocalFree(msg);
    }


Ist das so in Ordnung?

@Peter01

Danke! Freut mich, dass es jemandem weiterhilft.

mfg.
Ben04
Autor

Benutzerprofil
Anmeldungsdatum: 10.10.2004
Beiträge: 1635
Beitrag Ben04 Autor 12:26:06 08.04.2007   Titel:              Zitieren

Ja das sollte so funktionieren, solange temp nicht auf "Ausnahme werfen" eingestellt ist.

Ein
C/C++ Code:
try{
  temp << ": " << msg;
  LocalFree(msg);
}catch(...){
  LocalFree(msg);
  throw;
}
C/C++ Code:
try{
temp << ": " << msg;
LocalFree(msg);
}catch(...){
LocalFree(msg);
throw;
}
C/C++ Code:
try{
  temp << ": " << msg;
  LocalFree(msg);
}catch(...){
  LocalFree(msg);
  throw;
}

wäre sicher.
________
Unregistrierter




Beitrag ________ Unregistrierter 13:33:59 08.04.2007   Titel:              Zitieren

Oder mit so einer Hilfsklasse:

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class local_free_on_block_exit
  : private noncopyable
{
public:
  // Constructor blocks all signals for the calling thread.
  explicit local_free_on_block_exit(void* p)
    : p_(p)
  {
  }

  // Destructor restores the previous signal mask.
  ~local_free_on_block_exit()
  {
    ::LocalFree(p_);
  }

private:
  void* p_;
};
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class local_free_on_block_exit
: private noncopyable
{
public:
// Constructor blocks all signals for the calling thread.
explicit local_free_on_block_exit(void* p)
: p_(p)
{
}

// Destructor restores the previous signal mask.
~local_free_on_block_exit()
{
::LocalFree(p_);
}

private:
void* p_;
};
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class local_free_on_block_exit
  : private noncopyable
{
public:
  // Constructor blocks all signals for the calling thread.
  explicit local_free_on_block_exit(void* p)
    : p_(p)
  {
  }

  // Destructor restores the previous signal mask.
  ~local_free_on_block_exit()
  {
    ::LocalFree(p_);
  }

private:
  void* p_;
};
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 20:11:28 08.04.2007   Titel:              Zitieren

temp würde doch nur werfen, wenn msg ungültig ist, und das passiert doch nur, wenn FormatMessage false zurück gibt, oder?

mfg.
CodeFinder
Mitglied

Benutzerprofil
Anmeldungsdatum: 06.06.2006
Beiträge: 3093
Beitrag CodeFinder Mitglied 00:11:10 11.04.2007   Titel:              Zitieren

Erstmal vorweg: Klasse Artikel! :arrow: :live: . Vor allem da er einen perfekten Einstieg in Bezug auf die Unterschiede bzw. Differenzierung zwischen den verschiedenen OS's liefert und dabei das Hauptthema (das HTTP-Protokoll) immer im Auge behält. Echt gelungen!

joomoo schrieb:
Ich kenn mich leider nicht so aus mit den Windows-Funktionen und hab's jetzt so abgeändert:

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
    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))
    {
        temp << ": " << msg;
        LocalFree(msg);
    }
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
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))
{
temp << ": " << msg;
LocalFree(msg);
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
    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))
    {
        temp << ": " << msg;
        LocalFree(msg);
    }

Jupp, so ist okay. Allerdings ist der Cast IMHO überflüssig (Und da wo man Casts -gerade reinterpret_cast's- vermeiden kann, sollte man das auch tun ;) ).


joomoo schrieb:
temp würde doch nur werfen, wenn msg ungültig ist, und das passiert doch nur, wenn FormatMessage false zurück gibt, oder?

Genau, deshalb ist Dein Code vollkommen okay ;) .
Siehe:
MSDN zu FormatMessage schrieb:
Return Value

If the function succeeds, the return value is the number of TCHARs stored in the output buffer, excluding the terminating null character.

If the function fails, the return value is zero. To get extended error information, call GetLastError.

Quelle :arrow: FormatMessage (MSDN).

PS: Ich würde allerdings den Link: http://predef.sourceforge.net/preos.html#sec22 noch in den Artikel mit aufnehmen, der ist echt gut ;) .

_________________
MfG CodeFinder

Einfach nur heiß :cool: : Riff Raff ... If You Want Blood (You've Got It)! - That's Rock 'N Roll!.
Ben04
Autor

Benutzerprofil
Anmeldungsdatum: 10.10.2004
Beiträge: 1635
Beitrag Ben04 Autor 13:49:51 13.04.2007   Titel:              Zitieren

joomoo schrieb:
temp würde doch nur werfen, wenn msg ungültig ist, und das passiert doch nur, wenn FormatMessage false zurück gibt, oder?

mfg.

Oder der Stream aus irgendeinem Grund zusammenbricht. Das kann an sich immer passieren. Jede Schreiboperation auf einem String kann den badbit setzen.

Zitat:
Allerdings ist der Cast IMHO überflüssig (Und da wo man Casts -gerade reinterpret_cast's- vermeiden kann, sollte man das auch tun ;) ).

Nö definitive nicht. Von char** kommt man nun einmal nur mit einem Cast auf char*.
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 18:22:11 22.04.2007   Titel:              Zitieren

@CodeFinder
Vielen Dank fürs Lob!
Den Link hab ich gleich mal aufgenommen.

Zitat:
Oder der Stream aus irgendeinem Grund zusammenbricht. Das kann an sich immer passieren. Jede Schreiboperation auf einem String kann den badbit setzen.

Okay, dann übernehm ich deinen Code noch.

Hab den neuen Code jetzt hinzugefügt und werde alle Quelldateien aktualisieren (geht momentan irgendwie noch nicht...).

mfg.
SammyRukka
Mitglied

Benutzerprofil
Anmeldungsdatum: 10.05.2007
Beiträge: 188
Beitrag SammyRukka Mitglied 15:22:06 17.06.2007   Titel:              Zitieren

So, jetzt auch von mir Lob:
Ich habe zwar nicht sooo viel verstanden, aber immerhin habe ich endlich Code, mit denen ich meine Internet-Seite halbwegs ordentlich auslesen kann.
Halbwegs deshalb, weil ich leider noch ein paar Probleme habe:
Umlaute und einige andere Zeichen stimmen einfach nicht. Ich tippe da auf ein Codepage-Problem, bzw. Single- und Multibyte Character Sets.....

Wenn ich bsw. einen EBay-Artikel damit auslese, dann werden einige Zeichen, die eigentlich Leerzeichen sein sollten (oder vom Browser zumindest als solche ausgegeben werden) als 'á' dargestellt (und Umlaute halt ganz anders).

Ich habe also eine Vermutlung, aber keine Ahnung, wie ich das Problem lösen könnte:
- Muss ich eine Codepage-Komvertierung machen?
- Ist vielleicht das Byteweise auslesen per recv das Problem?
Vielleicht hat jmd. Hilfe, ich komme da mit meinem Halbwissen nicht wirklich weiter. Bin schon froh, wenn meine Poniter tun, was sie sollen ;)
pale dog
Mitglied

Benutzerprofil
Anmeldungsdatum: 27.04.2007
Beiträge: 1258
Beitrag pale dog Mitglied 15:32:43 17.06.2007   Titel:              Zitieren

SammyRukka schrieb:

So, jetzt auch von mir Lob:
Ich habe zwar nicht sooo viel verstanden, aber immerhin habe ich endlich Code, mit denen ich meine Internet-Seite halbwegs ordentlich auslesen kann.
Halbwegs deshalb, weil ich leider noch ein paar Probleme habe:
Umlaute und einige andere Zeichen stimmen einfach nicht. Ich tippe da auf ein Codepage-Problem, bzw. Single- und Multibyte Character Sets.....

richtig. ziemlich am anfang der seite findet sich in etwa sowas:
Code:
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
Code:
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
Code:
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />

diese 'charset' angabe musst du auswerten und dann den seiteninhalt dementsprechend umwandeln...
:)

_________________
http://users.aol.com/s6sj7gt/mike666.htm
SammyRukka
Mitglied

Benutzerprofil
Anmeldungsdatum: 10.05.2007
Beiträge: 188
Beitrag SammyRukka Mitglied 15:38:29 17.06.2007   Titel:              Zitieren

Hmm, schön, zu hören, dass ich gut vermutet habe - aber deshalb komme ich trotzdem nicht weiter, wenn ich wüßte, wie ich die Pages konvertiere, dann hätte ich das schon getestet. Außderdem bin ich mir halt nicht sicher, ob das mit dem Code überhaupt geht - Stichworte: recv, Single- / Multibyte.
:confused: :confused: :confused:
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 15:43:53 17.06.2007   Titel:              Zitieren

@SammyRukka
Im Nachwort findest du ein Beispiel, welches recv nicht immer nur mit einem Byte aufruft.

mfg.
pale dog
Mitglied

Benutzerprofil
Anmeldungsdatum: 27.04.2007
Beiträge: 1258
Beitrag pale dog Mitglied 15:45:28 17.06.2007   Titel:              Zitieren

SammyRukka schrieb:

Außderdem bin ich mir halt nicht sicher, ob das mit dem Code überhaupt geht - Stichworte: recv, Single- / Multibyte.

doch, geht.
erst alles als bytes in einen buffer einlesen, dann den 'charset' auswerten und bei bedarf eben konvertieren.
--> http://www.w3.org/TR/html4/charset.html
:)

_________________
http://users.aol.com/s6sj7gt/mike666.htm


Zuletzt bearbeitet von pale dog am 15:46:06 17.06.2007, insgesamt 1-mal bearbeitet
SammyRukka
Mitglied

Benutzerprofil
Anmeldungsdatum: 10.05.2007
Beiträge: 188
Beitrag SammyRukka Mitglied 16:16:45 17.06.2007   Titel:              Zitieren

Was würd' ich bloß ohne Euch machen?!?! :p - vermutlich Java :D
Ich würd mich ja gern mit '....ich kann kein Englisch, hat nicht einer Code für C&P....' herausreden, aber das traue ich mich nicht. Ich werde das mal lesen und ggf. wieder posten, wär ein wunder, wenn ich's allein und voir allem auf Anhieb hinbekommen würde :)
pale dog
Mitglied

Benutzerprofil
Anmeldungsdatum: 27.04.2007
Beiträge: 1258
Beitrag pale dog Mitglied 16:25:38 17.06.2007   Titel:              Zitieren

SammyRukka schrieb:
Was würd' ich bloß ohne Euch machen?!?! :p - vermutlich Java :D

mal ganz nebenbei: in Java hättest du mit sowas nicht das geringste problem :live:

_________________
http://users.aol.com/s6sj7gt/mike666.htm
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 16:29:07 17.06.2007   Titel:              Zitieren

SammyRukka schrieb:
Wenn ich bsw. einen EBay-Artikel damit auslese, dann werden einige Zeichen, die eigentlich Leerzeichen sein sollten (oder vom Browser zumindest als solche ausgegeben werden) als 'á' dargestellt (und Umlaute halt ganz anders).

Wo dargestellt, in der Windows-Konsole?
SammyRukka
Mitglied

Benutzerprofil
Anmeldungsdatum: 10.05.2007
Beiträge: 188
Beitrag SammyRukka Mitglied 18:14:16 17.06.2007   Titel:              Zitieren

pale dog schrieb:

mal ganz nebenbei: in Java hättest du mit sowas nicht das geringste problem :live:

In Java hätte ich mit so vielem kein Problem und wäre vor allem vieeeel schneller!


joomoo schrieb:
Wo dargestellt, in der Windows-Konsole?

Ja, noch ja. Im meine aber, dass ich auch beim Schrieben in eine Datei das Problem hatte. Ausserdem kann ich mit den geparsten Werten deshalb auch nicht rechnen, ist also kein reines Anzeigeproblem...
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 18:46:19 17.06.2007   Titel:              Zitieren

SammyRukka schrieb:
joomoo schrieb:
Wo dargestellt, in der Windows-Konsole?

Ja, noch ja. Im meine aber, dass ich auch beim Schrieben in eine Datei das Problem hatte. Ausserdem kann ich mit den geparsten Werten deshalb auch nicht rechnen, ist also kein reines Anzeigeproblem...

Wenn es auch in Dateien auftritt, dann ist ist es ein anderes Problem. Um welche Seite/URL handelt es sich denn?
SammyRukka
Mitglied

Benutzerprofil
Anmeldungsdatum: 10.05.2007
Beiträge: 188
Beitrag SammyRukka Mitglied 18:52:16 17.06.2007   Titel:              Zitieren

Nimm einfach eine Ebay-Seite. vor und nach dem Betrag des Gebots sollten Leerzeichen sein.
Warum ist es ein anderes Problem? Wenn ich schon falsch zum Verarbeiten einlese, dann muss das Schreiben doch auch falsch laufen!?
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 20:41:26 17.06.2007   Titel:              Zitieren

SammyRukka schrieb:
Nimm einfach eine Ebay-Seite. vor und nach dem Betrag des Gebots sollten Leerzeichen sein.

Bei mir klappt das, vielleicht liegt's an deinem Editor?
SammyRukka
Mitglied

Benutzerprofil
Anmeldungsdatum: 10.05.2007
Beiträge: 188
Beitrag SammyRukka Mitglied 21:41:46 17.06.2007   Titel:              Zitieren

Hmmm. Blöd, wenn man so etwas über wochen verteilt. Die temporär heruntergeladene Datei wird im Editor korrekt angezeigt (notepad).
Dann klappt wohl das Einlesen der Datei nicht:

Code:
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
fin.open( dateiname.c_str(), ios_base::in );
if( fin ) {
    char c;
    while( !fin.eof() ) {
        fin.get( c );
        htmlContent += c;
    }
fin.close();
Code:
1
2
3
4
5
6
7
8
fin.open( dateiname.c_str(), ios_base::in );
if( fin ) {
char c;
while( !fin.eof() ) {
fin.get( c );
htmlContent += c;
}
fin.close();
Code:
1
2
3
4
5
6
7
8
fin.open( dateiname.c_str(), ios_base::in );
if( fin ) {
    char c;
    while( !fin.eof() ) {
        fin.get( c );
        htmlContent += c;
    }
fin.close();


Denkfehler? Also Pros, zeigt was ihr drauf habt :p und sry, dass ich den Tutorial-Code in Verdacht hatte :o)

---EDIT---

Ich komme da nicht weiter:
Annahme: Wenn meine HTML 'iso-8859-1' ist (und das ist sie!), dann benutzt sie doch auch nur 7/8 Bit für ein Zeichen und die ersten x (weiss nicht genau) sind mit dem Windows-1252 gleich.
Frage: Warum kann dann aus einem Simplen Space ein 'á' werden.
Und dann noch eine Frage:
Wenn ich den Tutorial-Code benutze, so wie er ist, und eine Datei herunterlade und speichere, dann wird das Encoding doch gar nicht ausgewertet, unter Umständen stimmt stimmt 'iso-8859-1' also gar nicht mehr, wenn ich bsw. beim Herunterladen mit einer anderen Codepage arbeite und bewusst keine Konvertierung vornehme. Oder was?! :confused:

Ich komme da nicht weiter.
Ausserdem frage ich mich, ob ich damit nicht einen eigenen Thread aufmachen sollte. :)

--EDIT2--
Ich habe mal einen neuen Thread gestartet:
http://www.c-plusplus.de/forum/viewtopic-var-p-is-1308613.html#1308613!


Zuletzt bearbeitet von SammyRukka am 20:11:21 18.06.2007, insgesamt 3-mal bearbeitet
nixchecker
Unregistrierter




Beitrag nixchecker Unregistrierter 23:16:59 12.10.2007   Titel:              Zitieren

Hallo Leute,

ich habe mir das fertige Programm runtergeladen und das funktioniert auch wunderbar.

Allerdings habe ich ein Problem wenn ich die Seite anstatt in einer Datei zu speichern in einem std::string speichern möchte um dann damit weiter zu arbeiten.

Wenn ich überall da wo in die Datei geschrieben wird die Variable "buf" einem String zuweise bekomme ich einen zerstückelten Inhalt.

Also da wiederholt sich der Inhalt manchmal und es ist nicht alles an der Position wo es sein sollte.

Kann mir vielleicht jemand einen Tipp geben wie ich am besten das Programm abändern kann damit der Inhalt nicht in eine Datei sondern in einem string gespeichert wird der dann am Schluß den kompletten Inhalt der Webseite hat.

Ich wäre für jeden Tipp sehr dankbar.

Aber sonst ist es ein perfektes Tutorial. Vielen Dank.


nixchecker
SammyRukka
Mitglied

Benutzerprofil
Anmeldungsdatum: 10.05.2007
Beiträge: 188
Beitrag SammyRukka Mitglied 23:54:56 12.10.2007   Titel:              Zitieren

Du solltest einen StringBuffer benutzen und ganz am Ende erst einen String draus machen. Ich glaube, dass ich das so gamacht habe (Sourcen sind tief in einer VM vergraben).
nixchecker
Unregistrierter




Beitrag nixchecker Unregistrierter 00:12:00 13.10.2007   Titel:              Zitieren

Ok das heißt also das ich an den stellen wo mit fout.write(...) in die Datei geschrieben wird stattdessen in einem StringBuffer schreiben und dann am schluß aus dem StringBuffer einen String daraus machen?

nixchecker
SammyRukka
Mitglied

Benutzerprofil
Anmeldungsdatum: 10.05.2007
Beiträge: 188
Beitrag SammyRukka Mitglied 00:55:24 13.10.2007   Titel:              Zitieren

Ja, das meinte ich, aber.....

nee, das nehme ich mal zurück.
Habe Codeschnipsel gefunden. Ich mache das so:

std::string meinString = "";
meinString append(buf, bytesRecv);

Genaugenommen arbeite ich auf der Referenz eines übergebenen Strings.

Damit sollte das klappen.

Ansonsten sollte Dir klar sein, dass ein cout in der Console unter Umständen für einige Zeichen Schotter ausgibt, da bin ich auch mal drauf reingefallen....
nixchecker
Unregistrierter




Beitrag nixchecker Unregistrierter 01:00:35 13.10.2007   Titel:              Zitieren

Hi,

ja ist mir bekannt mit dem cout.

Aber so scheint es zu funktionieren. An "append" hab ich gar nicht gedacht.

Vielen Dank für die Hilfe.

nixchecker
SammyRukka
Mitglied

Benutzerprofil
Anmeldungsdatum: 10.05.2007
Beiträge: 188
Beitrag SammyRukka Mitglied 09:45:43 13.10.2007   Titel:              Zitieren

nixchecker schrieb:
Vielen Dank für die Hilfe.


Schön, dass auch ich mal helfen konnte ;)
TheRave
Unregistrierter




Beitrag TheRave Unregistrierter 16:06:11 17.02.2008   Titel:              Zitieren

Danke fürs Tutorial. Ich hab mir den Code runtergeladen mit der gekapselten recv Funktion. Damit hab ich mir einen kleinen Webseitendownloader geschrieben.

Diese Funktion hier hat mich eine Weile beschäftigt:

Code:
//Chunk complete
for(int i = 0; i < 2; ++i)
{
 char temp;
 Recv(Socket, &temp, 1, 0);
}
Code:
//Chunk complete
for(int i = 0; i < 2; ++i)
{
char temp;
Recv(Socket, &temp, 1, 0);
}
Code:
//Chunk complete
for(int i = 0; i < 2; ++i)
{
 char temp;
 Recv(Socket, &temp, 1, 0);
}


Die soll doch dafür sorgen daß die letzten zwei Bytes ignoriert werden, nur warum landen die im GlobalBuffer? Diese letzten zwei Bytes sollte man vielleicht noch ordnungsgemäß entsorgen.


Das Problem war, daß nach dem laden des letzten Chunks der Website die Schleife ja mit der If Bedingung chunkSize <= 0 mit einem Break abgebrochen wird. Dann stand aber nochwas in dem GlobalBuffer - den man ja nicht nachmachen soll. Nämlich ein Zeilenumbruch - die nicht entsorgten zwei Bytes. Als folge davon konnte der Header der folgenden URL nicht mehr ausgewertet werden und der Download lief eine gewisse Zeit Amok.

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
cout << "Downloading... (Chunked)" << endl;
while(true)
   {
    stringstream sstream;
    GetLine(Socket, sstream);
    int chunkSize = -1;
                   
    // Größe des nächsten Parts einlesen
    sstream >> hex >> chunkSize;
                   
    if(chunkSize <= 0)
      {
       break;
      }
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
cout << "Downloading... (Chunked)" << endl;
while(true)
{
stringstream sstream;
GetLine(Socket, sstream);
int chunkSize = -1;

// Größe des nächsten Parts einlesen
sstream >> hex >> chunkSize;

if(chunkSize <= 0)
{
break;
}
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
cout << "Downloading... (Chunked)" << endl;
while(true)
   {
    stringstream sstream;
    GetLine(Socket, sstream);
    int chunkSize = -1;
                   
    // Größe des nächsten Parts einlesen
    sstream >> hex >> chunkSize;
                   
    if(chunkSize <= 0)
      {
       break;
      }


Das hat mich eine ganze Weile beschäftigt.
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 16:15:27 17.02.2008   Titel:              Zitieren

TheRave schrieb:
Danke fürs Tutorial. Ich hab mir den Code runtergeladen mit der gekapselten recv Funktion. Damit hab ich mir einen kleinen Webseitendownloader geschrieben.

Diese Funktion hier hat mich eine Weile beschäftigt:

Code:
//Chunk complete
for(int i = 0; i < 2; ++i)
{
 char temp;
 Recv(Socket, &temp, 1, 0);
}
Code:
//Chunk complete
for(int i = 0; i < 2; ++i)
{
char temp;
Recv(Socket, &temp, 1, 0);
}
Code:
//Chunk complete
for(int i = 0; i < 2; ++i)
{
 char temp;
 Recv(Socket, &temp, 1, 0);
}


Die soll doch dafür sorgen daß die letzten zwei Bytes ignoriert werden, nur warum landen die im GlobalBuffer? Diese letzten zwei Bytes sollte man vielleicht noch ordnungsgemäß entsorgen.


Das Problem war, daß nach dem laden des letzten Chunks der Website die Schleife ja mit der If Bedingung chunkSize <= 0 mit einem Break abgebrochen wird. Dann stand aber nochwas in dem GlobalBuffer - den man ja nicht nachmachen soll. Nämlich ein Zeilenumbruch - die nicht entsorgten zwei Bytes. Als folge davon konnte der Header der folgenden URL nicht mehr ausgewertet werden und der Download lief eine gewisse Zeit Amok.

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
cout << "Downloading... (Chunked)" << endl;
while(true)
   {
    stringstream sstream;
    GetLine(Socket, sstream);
    int chunkSize = -1;
                   
    // Größe des nächsten Parts einlesen
    sstream >> hex >> chunkSize;
                   
    if(chunkSize <= 0)
      {
       break;
      }
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
cout << "Downloading... (Chunked)" << endl;
while(true)
{
stringstream sstream;
GetLine(Socket, sstream);
int chunkSize = -1;

// Größe des nächsten Parts einlesen
sstream >> hex >> chunkSize;

if(chunkSize <= 0)
{
break;
}
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
cout << "Downloading... (Chunked)" << endl;
while(true)
   {
    stringstream sstream;
    GetLine(Socket, sstream);
    int chunkSize = -1;
                   
    // Größe des nächsten Parts einlesen
    sstream >> hex >> chunkSize;
                   
    if(chunkSize <= 0)
      {
       break;
      }


Das hat mich eine ganze Weile beschäftigt.

Tatsächlich. Das tut mir leid, vielen Dank dass du mir das sagst. Ich hatte einfach FInd & Replace gemacht um alle recv Funktion zu ersetzen.

Werde das gleich mal korrigieren und hochladen.
TheRave
Unregistrierter




Beitrag TheRave Unregistrierter 16:23:28 17.02.2008   Titel:              Zitieren

Ist ja kein Problem. Der Fall tritt ja auch nur ein, wenn man mehrere URLs hintereinander lädt.
FeGm
Mitglied

Benutzerprofil
Anmeldungsdatum: 17.02.2008
Beiträge: 10
Beitrag FeGm Mitglied 00:30:05 22.02.2008   Titel:              Zitieren

Wie schafft man es, das ganze so um zu schreiben, das man damit mehrer Seiten hintereinadner herutnerladen kann?

Ich habs schon mehrfach probiert, zB. das ganze in eine Funktion ausgelagert und diese mehrfach aufgerufen oder Schleifen an verschiedene Stelle gesetzt, doch leider kahm bei jeder 2. Verbindung der folgende Fehler:
http://img212.imageshack.us/img212/3702/errorerfolgreichsm9.jpg

Ich hab den Quellcode hier nicht gepostet, da der Fehler bei vielen Verschieden versuchen kahm und ich dachte ich hab vlt. nur irgendwas dummes übersehen...

Das simpelste war das komplette (Bis auf eingabe der URL) in eine Funktion zu packen und diese zweimal aufzurufen.

Jemand eine Idee was ich falsch mache? :(

//edit: Windows Vista, Code::Blocks und MinGW


Zuletzt bearbeitet von FeGm am 01:38:55 22.02.2008, insgesamt 2-mal bearbeitet
ReduX
Mitglied

Benutzerprofil
Anmeldungsdatum: 07.06.2007
Beiträge: 193
Beitrag ReduX Mitglied 22:44:18 22.02.2008   Titel:              Zitieren

Hi,
Ich habe noch ne kleine Frage zu den HTTP Sachen.
Wie kann ich nen request hinschicken mit nem username und nem passwort?
Also z.B. solche urls http://username:passwort@domain.com ?
Hoffe das die Frage nochnicht gestellt wurde (habe nicht alle Seiten durchgeblättert).

Schönes Wochenende!
MFG ReduX
............
Unregistrierter




Beitrag ............ Unregistrierter 01:43:55 23.02.2008   Titel:              Zitieren

schau mal hier: http://html-world.de/program/http_6.php#authorization
Was für ein eingescanntes
Unregistrierter




Beitrag Was für ein eingescanntes Unregistrierter 22:16:36 23.02.2008   Titel:              Zitieren

Tim schrieb:
Das eingescannte Bild ist einfach nur cool :cool: :live:
Was für ein eingescanntes Bild?
Mr. N
Mitglied

Benutzerprofil
Anmeldungsdatum: 28.12.2001
Beiträge: 4331
Beitrag Mr. N Mitglied 19:01:31 25.02.2008   Titel:              Zitieren

Was für ein eingescanntes schrieb:
Tim schrieb:
Das eingescannte Bild ist einfach nur cool :cool: :live:
Was für ein eingescanntes Bild?


Das, das ersetzt wurde.
monoid
Mitglied

Benutzerprofil
Anmeldungsdatum: 26.03.2008
Beiträge: 1
Beitrag monoid Mitglied 19:49:32 26.03.2008   Titel:   Problem mit IIS Response            Zitieren

Hallo miteinander, :D

ich hab mich jetzt in dieses Tutorial reingearbeitet. Leider liefert mein IIS folgenden Response zurück:

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
HTTP/1.1 100 Continue

Server: Microsoft-IIS/5.1

Date: Wed, 26 Mar 2008 17:29:40 GMT



HTTP/1.1 200 OK

Server: Microsoft-IIS/5.1

Date: Wed, 26 Mar 2008 17:29:40 GMT

Connection: close

X-Powered-By: ServletExec/5.0p06, Servlet/2.4, JSP/2.0

Content-Type: text/html; charset=UTF-8

Cache-Control: no-cache

Set-Cookie: JSESSIONID=2ZeunHRcvNeHrcmBfYa9cw1yilk; path=/arsys
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
HTTP/1.1 100 Continue

Server: Microsoft-IIS/5.1

Date: Wed, 26 Mar 2008 17:29:40 GMT



HTTP/1.1 200 OK

Server: Microsoft-IIS/5.1

Date: Wed, 26 Mar 2008 17:29:40 GMT

Connection: close

X-Powered-By: ServletExec/5.0p06, Servlet/2.4, JSP/2.0

Content-Type: text/html; charset=UTF-8

Cache-Control: no-cache

Set-Cookie: JSESSIONID=2ZeunHRcvNeHrcmBfYa9cw1yilk; path=/arsys
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
HTTP/1.1 100 Continue

Server: Microsoft-IIS/5.1

Date: Wed, 26 Mar 2008 17:29:40 GMT



HTTP/1.1 200 OK

Server: Microsoft-IIS/5.1

Date: Wed, 26 Mar 2008 17:29:40 GMT

Connection: close

X-Powered-By: ServletExec/5.0p06, Servlet/2.4, JSP/2.0

Content-Type: text/html; charset=UTF-8

Cache-Control: no-cache

Set-Cookie: JSESSIONID=2ZeunHRcvNeHrcmBfYa9cw1yilk; path=/arsys


Aufgrund dessen läuft die GetLine Funktion in eine Endlosschleife -> Absturz. Probiere jetzt schon seit Tagen herum. Ich versuche dieses Tutorial auf alle Webserver (Apache,Tomcat,IIS) zum laufen zu bekommen.

Danke für eure Hilfe! :o)
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 22:18:14 26.03.2008   Titel:   Re: Problem mit IIS Response            Zitieren

Hallo :D

monoid schrieb:
Hallo miteinander, :D

ich hab mich jetzt in dieses Tutorial reingearbeitet. Leider liefert mein IIS folgenden Response zurück:

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
HTTP/1.1 100 Continue

Server: Microsoft-IIS/5.1

Date: Wed, 26 Mar 2008 17:29:40 GMT



HTTP/1.1 200 OK

Server: Microsoft-IIS/5.1

Date: Wed, 26 Mar 2008 17:29:40 GMT

Connection: close

X-Powered-By: ServletExec/5.0p06, Servlet/2.4, JSP/2.0

Content-Type: text/html; charset=UTF-8

Cache-Control: no-cache

Set-Cookie: JSESSIONID=2ZeunHRcvNeHrcmBfYa9cw1yilk; path=/arsys
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
HTTP/1.1 100 Continue

Server: Microsoft-IIS/5.1

Date: Wed, 26 Mar 2008 17:29:40 GMT



HTTP/1.1 200 OK

Server: Microsoft-IIS/5.1

Date: Wed, 26 Mar 2008 17:29:40 GMT

Connection: close

X-Powered-By: ServletExec/5.0p06, Servlet/2.4, JSP/2.0

Content-Type: text/html; charset=UTF-8

Cache-Control: no-cache

Set-Cookie: JSESSIONID=2ZeunHRcvNeHrcmBfYa9cw1yilk; path=/arsys
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
HTTP/1.1 100 Continue

Server: Microsoft-IIS/5.1

Date: Wed, 26 Mar 2008 17:29:40 GMT



HTTP/1.1 200 OK

Server: Microsoft-IIS/5.1

Date: Wed, 26 Mar 2008 17:29:40 GMT

Connection: close

X-Powered-By: ServletExec/5.0p06, Servlet/2.4, JSP/2.0

Content-Type: text/html; charset=UTF-8

Cache-Control: no-cache

Set-Cookie: JSESSIONID=2ZeunHRcvNeHrcmBfYa9cw1yilk; path=/arsys


Aufgrund dessen läuft die GetLine Funktion in eine Endlosschleife -> Absturz.

Aufgrund von was?
Könntest du mir die URL (d)eines IIS-Servers geben, dann kann ich das selber ausprobieren.
SammyRukka
Mitglied

Benutzerprofil
Anmeldungsdatum: 10.05.2007
Beiträge: 188
Beitrag SammyRukka Mitglied 08:59:09 21.04.2008   Titel:              Zitieren

So! Hallo mal wieder!

Nach Monaten Abwesenheit bin ich auch mal wieder da :-) und natürlich brauche ich mal wieder Eure Hilfe.

Wie im Thread schon mal erwähnt, habe ich einen Teil dieses Samples erfolgreich in meinem Programm verbaut, das schöne ist halt, dass das alles ohne weitere Libs, .NET oder sonst was läuft.
Jetzt habe ich einen Umbau vor und müßte dazu einen Post-Request erzeugen. Da ich hier nur Get-Beispiele finde und aus der W3-Protokoll-Doku nicht gänzlich schlau werde, frage ich hier mal nach.
Vielleicht hat ja jmd. die notwendigen zusätzlichen Zeilen, die Grundform sollte ja die selbe sein.

THX!
http-freak
Unregistrierter




Beitrag http-freak Unregistrierter 22:03:11 21.04.2008   Titel:              Zitieren

http://jmarshall.com/easy/http/
:)
SammyRukka
Mitglied

Benutzerprofil
Anmeldungsdatum: 10.05.2007
Beiträge: 188
Beitrag SammyRukka Mitglied 22:13:18 21.04.2008   Titel:              Zitieren

SammyRukka schrieb:
...und aus der W3-Protokoll-Doku nicht gänzlich schlau werde,...


...und Du meinst, mit dem Link wird's besser... :p hast Du ein Vertrauen!

Aber OK, ich werd's mal durchforsten und hoffen, dass ich daraus sinnvollen Code gießen kann. thx so far
protocol-freak
Unregistrierter




Beitrag protocol-freak Unregistrierter 22:34:36 21.04.2008   Titel:              Zitieren

SammyRukka schrieb:

...und Du meinst, mit dem Link wird's besser... :p hast Du ein Vertrauen!

die seite gibts schon ziemlich lange. ich hab' damals 'nen kleinen webserver gecodet und hatte 0 ahnung von http. ich hab' fast alles, was man dafür wissen muss von dieser seite.
btw, hier ist ein einfaches beispiel u.a. mit POST request: http://dev.makingthings.com/browser/firmware/trunk/controller/makingthings/webclient.c
:)
SammyRukka
Mitglied

Benutzerprofil
Anmeldungsdatum: 10.05.2007
Beiträge: 188
Beitrag SammyRukka Mitglied 07:42:41 22.04.2008   Titel:              Zitieren

Na das ist doch mal etwas Greifbares. Danke! Ich schau mal.
Linkgeber
Unregistrierter




Beitrag Linkgeber Unregistrierter 11:23:23 22.04.2008   Titel:              Zitieren

http://html-world.de/program/http_ov.php
KOR
Mitglied

Benutzerprofil
Anmeldungsdatum: 18.03.2008
Beiträge: 37
Beitrag KOR Mitglied 14:12:35 22.08.2008   Titel:              Zitieren

Hi bei mir wird das Programm immer mit "Verbindung fehlgeschlagen!" beendet. Kann es daran iegen das indem Netzwerk wo ich bin alle browser nur mit einem Proxy laufen?
Wie kann man das beheben?
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 15:24:20 22.08.2008   Titel:              Zitieren

Ja das könnte daran liegen. Ich aber leider keine Ahnung, wie man das beheben kann. Am besten eröffne mal einen Thread im Linux- oder WinAPI-Forum.
VerzweifelterMensch
Unregistrierter




Beitrag VerzweifelterMensch Unregistrierter 20:48:01 08.11.2008   Titel:              Zitieren

Ich habe gerade ein Programm geschrieben, das eine Webseite herunterlädt. Wenn ich HTTP/1.0 nutze, dann klappt alles, aber nutze ich HTTP/1.1, dannn werden am Ende und Anfang noch einige Dinge mitgesendet, die da nicht hingehören, denke ich. Ich bin übrigens nicht nach Content-Length gegangen, sondern habe gewartet bis der Server trennt(Connection: Close)...

Hier die Ausgabe:
Zitat:

'HTTP/1.1 200 OK
'
'Date: Sat, 08 Nov 2008 18:33:37 GMT
'
'Server: Apache/2.2.3 (Debian) PHP/4.4.4-8+etch6
'
'X-Powered-By: PHP/4.4.4-8+etch6
'
'Set-Cookie: POSTNUKESID=40a9782024010d53f011b6e64fc72f17; expires=Wed, 09 Nov 2033 00:33:37 GMT; path=/cms
'
'Expires: Thu, 19 Nov 1981 08:52:00 GMT
'
'Cache-Control: cache
'
'Pragma: no-cache
'
'Connection: close
'
'Transfer-Encoding: chunked
'
'Content-Type: text/html
'
'
'
'47b2
'
'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">'
'<html>'
'<head>'
'<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">'
'<title>c++.de :: Irgendwer hat immer eine Antwort</title>'
'<meta name="KEYWORDS" content="C++, Software, C, C#, STL, OOP, OOA, Objektorientierte Programmierung, Softwareentwicklung, Tutorials, Compiler, Bücher, ebooks, IDE, Programmierung, Proggen, c++.de, Decompiler, Visual, MFC, VCL, main, if, while, else, for, Objekt, .NET, net, gtkmm, qt, wxWidgets, GUI, Dialog, Fenster, Anwendung">'
'<meta name="DESCRIPTION" content="Irgendwer hat immer eine Antwort">'
'<meta name="ROBOTS" content="INDEX,FOLLOW">'
'<meta name="resource-type" content="document">'
[usw......]
'pageTracker._trackPageview();'
'</script></body>'
'</html>
'
'0
'
'
'



Warum? Die ' sind nur zum besseren Debugen und im Original nicht vorhanden. Habt ihr das Problem bei http://www.c-plusplus.de auch oder ist mein Programm fehlerhaft? Aber warum geht es dann perfekt(!) mit HTTP/1.0 aber mit HTTP/1.1 nicht?

Danke für die Hilfe,
VerzweifelterMensch
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 21:22:08 08.11.2008   Titel:              Zitieren

Für solche Fragen eröffne bitte einen Thread im Linux- oder WinAPI-Forum.

Schau dir Kapitel 6.4 Chunked Transfer-Encoding nochmal an.


Zuletzt bearbeitet von joomoo am 21:24:10 08.11.2008, insgesamt 2-mal bearbeitet
R3dNeXX
Mitglied

Benutzerprofil
Anmeldungsdatum: 06.10.2008
Beiträge: 467
Beitrag R3dNeXX Mitglied 11:43:13 12.11.2008   Titel:              Zitieren

hi,

bin im Kapitel 6.4 und hab da alles fertig !
Aber irgendwie und aus irgendeinem grund funktioniert der code bei mir nicht !

Hab alles genauso, aber trotzdem geht es bis zur prozentanzeige, da steht dann eben 0% und dann kommt "Socket-Fehler #0: D"
hab meinem code mit dem code von hier verglichen, finde aber nix und wenn ich den code, den ich hier downgeloadet hab, ausprobier, dann funktioniert er, bloß mein selbst geschriebener nicht !

Verstehe ich nicht, weiß jemand rat, gibts noch irgendwas zu beachten ??

Hier mein Code:
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
#include <iostream>
#include
<fstream>
#include
<stdexcept>    // runtime_error
#include
<sstream>
#include
<string>
#include
<WinSock2.h>
using namespace std;
// Infos: http://www.c-plusplus.de/forum/viewtopic-var-t-is-169861.html

// wenn während dem Senden ein Fehler auftritt

runtime_error CreateSocketError()
{
    ostringstream temp;
    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<LPWSTR>(&msg), 0, NULL))
    {
        try
        {
            temp << ": " << msg;
            LocalFree(msg);
        }
        catch (...)
        {
            LocalFree(msg);
            throw;
        }
    }
    return runtime_error(temp.str());
}

// um restlichen Buffer noch zu senden, falls send eher aufhört zu senden
void SendAll(int socket, const char* const buf, const int size)
{
    int bytesSent = 0;    // Anzahl Bytes, die bereits vom Buffer gesendet wurden
    do
    {
        int result = send(socket, buf + bytesSent, size - bytesSent, 0);
        if (result < 0)
        {
            throw CreateSocketError();
        }
        bytesSent += result;
    } while (bytesSent < size);
}

// liest eine Zeile des Socket in Stringstream (HTTP-Protokoll sendet zeilenweise)
void GetLine(int socket, stringstream &line)
{
    // byteweise von Socket lesen, bis Zeilenumbruch auftritt
    for (char c; recv(socket, &c, 1, 0) > 0; line << c)
    {
        if (c == '\n')
        {
            return;
        }
    }
    throw CreateSocketError();    // wenn recv < 0 || = 0 ist,
}


int main()
{
    WSADATA w;
    // man will Zugriff auf Winsock-Lib haben
    if (int result = WSAStartup(MAKEWORD(2,2), &w) != 0)
    {
        cout << "Winsock 2 konnte nicht gestartet werden! Error: " << result << endl;
        return 1;
    }
    hostent *phe = gethostbyname("www.kernel.org");
    if (phe == NULL)
    {
        cout << "Host konnte nicht aufgeloest werden!\n";
        return 1;
    }
    if (phe->h_addrtype != AF_INET)
    {
        cout << "ungueltiger Adress-Typ!\n";
        return 1;
    }
    if (phe->h_length != 4)
    {
        cout << "Ungueltiger IP-Typ!\n";
        return 1;
    }


    SOCKET Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (Socket == -1)
    {
        cout << "Socket konnte nicht erstellt werden!\n";
        return 1;
    }

    sockaddr_in service;
    service.sin_family = AF_INET;    // für IPv4
    service.sin_port = htons(80);    // Port 80 nutzt HTTP

    // Iterator

    char **p = phe->h_addr_list;    // p mit 1. Listenelement initialisieren
    int result;                        // Ergebnis von connect
    do
    {
        // Liste zu Ende ?
        if (*p == NULL)
        {
            cout << "Verbindung fehlgeschlagen!\n";
            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!\n";
    const string request = "GET /faq/index.html HTTP/1.1\r\nHost: www.kernel.org\r\nConnection: close\r\n\r\n";
   
    try
    {
        SendAll((int)Socket, request.c_str(), (int)request.size());
        int code = 100;        // 100 = continue
        string protokoll;
        stringstream firstLine;
        while (code == 100)
        {
            GetLine((int)Socket, firstLine);
            firstLine >> protokoll;
            firstLine >> code;
            if (code == 100)
            {
                GetLine((int)Socket, firstLine);    // leere Zeile nach continue ignorieren
            }
        }
        cout << "Protokoll: " << protokoll << endl;
        if (code != 200)
        {
            firstLine.ignore();
            string msg;
            getline(firstLine, msg);
            cout << "Error #" << code << " - " << msg << endl;
            return 0;
        }
   
        bool chunked = false;        // wurde Transfer-Encoding: chunked angegeben ?
        const int noSizeGiven = -1;
        int size = noSizeGiven;        // speichert übergebene Dateigröße

        while (true)
        {
            stringstream sstream;
            GetLine((int)Socket, sstream);
            if (sstream.str() == "\r")        // Header zu Ende
            {
                break;
            }
            string left;
            sstream >> left;
            sstream.ignore();        // Leerzeichen ignorieren
       
            if (left == "Content-Length:")
            {
                sstream >> size;
            }
            if (left == "Transfer-Encoding:")
            {
                string transferEncoding;
                sstream >> transferEncoding;
                if (transferEncoding == "chunked")
                {
                    chunked = true;
                }
            }
        }
       
        fstream out("faq.html", ios::binary|ios::out);
        if (!out)
        {
            cout << "Could not create File!" << endl;
            return 1;
        }

        int recvSize = 0;        // empfangene Bytes insgesamt
        char buffer[1024];
        int bytesRecv = -1;        // empfangene Bytes des letzten recv

        if (size != noSizeGiven)    // wenn Größe über Content-Length gegeben wurde
        {
            cout << "0%";
            while (recvSize < size)
            {
                if (bytesRecv = recv(Socket, buffer, sizeof(buffer), 0) <= 0)
                {
                    throw CreateSocketError();
                }
                // empfange Anzahl an Bytes zu Gesamtanzahl addieren und Buffer in Datei schreiben
                recvSize += bytesRecv;
                out.write(buffer, bytesRecv);
                cout << "\r" << recvSize * 100 / size << "%" << flush;    // mit \r an Anfang der Zeile springen
            }
        }
        else
        {
            if (!chunked)
            {
                cout << "Downloading... (Unknown FileSize)" << endl;
                while (bytesRecv != 0)
                {
                    if (bytesRecv = recv(Socket, buffer, sizeof(buffer), 0) < 0)
                    {
                        throw CreateSocketError();
                    }
                    out.write(buffer, bytesRecv);
                }
            }
            else
            {
                cout << "Donwloading... (Chunked)" << endl;
                while (true)
                {
                    stringstream sstream;
                    GetLine((int)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;
                        // an recv als Größe entweder sizeof(buf) oder aber bytesToRecv übergeben, wenn nur noch ein kleines Stück empfangen werden muss
                        if (bytesRecv = recv(Socket, buffer, bytesToRecv > sizeof(buffer) ? sizeof(buffer) : bytesToRecv, 0) <= 0)
                        {
                            throw CreateSocketError();
                        }
                        recvSize += bytesRecv;
                        out.write(buffer, 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;
    }
   
    closesocket(Socket);

    cin.get();
    return 0;
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
#include <iostream>
#include
<fstream>
#include
<stdexcept> // runtime_error
#include
<sstream>
#include
<string>
#include
<WinSock2.h>
using namespace std;
// Infos: http://www.c-plusplus.de/forum/viewtopic-var-t-is-169861.html

// wenn während dem Senden ein Fehler auftritt

runtime_error CreateSocketError()
{
ostringstream temp;
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<LPWSTR>(&msg), 0, NULL))
{
try
{
temp << ": " << msg;
LocalFree(msg);
}
catch (...)
{
LocalFree(msg);
throw;
}
}
return runtime_error(temp.str());
}

// um restlichen Buffer noch zu senden, falls send eher aufhört zu senden
void SendAll(int socket, const char* const buf, const int size)
{
int bytesSent = 0; // Anzahl Bytes, die bereits vom Buffer gesendet wurden
do
{
int result = send(socket, buf + bytesSent, size - bytesSent, 0);
if (result < 0)
{
throw CreateSocketError();
}
bytesSent += result;
} while (bytesSent < size);
}

// liest eine Zeile des Socket in Stringstream (HTTP-Protokoll sendet zeilenweise)
void GetLine(int socket, stringstream &line)
{
// byteweise von Socket lesen, bis Zeilenumbruch auftritt
for (char c; recv(socket, &c, 1, 0) > 0; line << c)
{
if (c == '\n')
{
return;
}
}
throw CreateSocketError(); // wenn recv < 0 || = 0 ist,
}


int main()
{
WSADATA w;
// man will Zugriff auf Winsock-Lib haben
if (int result = WSAStartup(MAKEWORD(2,2), &w) != 0)
{
cout << "Winsock 2 konnte nicht gestartet werden! Error: " << result << endl;
return 1;
}
hostent *phe = gethostbyname("www.kernel.org");
if (phe == NULL)
{
cout << "Host konnte nicht aufgeloest werden!\n";
return 1;
}
if (phe->h_addrtype != AF_INET)
{
cout << "ungueltiger Adress-Typ!\n";
return 1;
}
if (phe->h_length != 4)
{
cout << "Ungueltiger IP-Typ!\n";
return 1;
}


SOCKET Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (Socket == -1)
{
cout << "Socket konnte nicht erstellt werden!\n";
return 1;
}

sockaddr_in service;
service.sin_family = AF_INET; // für IPv4
service.sin_port = htons(80); // Port 80 nutzt HTTP

// Iterator

char **p = phe->h_addr_list; // p mit 1. Listenelement initialisieren
int result; // Ergebnis von connect
do
{
// Liste zu Ende ?
if (*p == NULL)
{
cout << "Verbindung fehlgeschlagen!\n";
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!\n";
const string request = "GET /faq/index.html HTTP/1.1\r\nHost: www.kernel.org\r\nConnection: close\r\n\r\n";

try
{
SendAll((int)Socket, request.c_str(), (int)request.size());
int code = 100; // 100 = continue
string protokoll;
stringstream firstLine;
while (code == 100)
{
GetLine((int)Socket, firstLine);
firstLine >> protokoll;
firstLine >> code;
if (code == 100)
{
GetLine((int)Socket, firstLine); // leere Zeile nach continue ignorieren
}
}
cout << "Protokoll: " << protokoll << endl;
if (code != 200)
{
firstLine.ignore();
string msg;
getline(firstLine, msg);
cout << "Error #" << code << " - " << msg << endl;
return 0;
}

bool chunked = false; // wurde Transfer-Encoding: chunked angegeben ?
const int noSizeGiven = -1;
int size = noSizeGiven; // speichert übergebene Dateigröße

while (true)
{
stringstream sstream;
GetLine((int)Socket, sstream);
if (sstream.str() == "\r") // Header zu Ende
{
break;
}
string left;
sstream >> left;
sstream.ignore(); // Leerzeichen ignorieren

if (left == "Content-Length:")
{
sstream >> size;
}
if (left == "Transfer-Encoding:")
{
string transferEncoding;
sstream >> transferEncoding;
if (transferEncoding == "chunked")
{
chunked = true;
}
}
}

fstream out("faq.html", ios::binary|ios::out);
if (!out)
{
cout << "Could not create File!" << endl;
return 1;
}

int recvSize = 0; // empfangene Bytes insgesamt
char buffer[1024];
int bytesRecv = -1; // empfangene Bytes des letzten recv

if (size != noSizeGiven) // wenn Größe über Content-Length gegeben wurde
{
cout << "0%";
while (recvSize < size)
{
if (bytesRecv = recv(Socket, buffer, sizeof(buffer), 0) <= 0)
{
throw CreateSocketError();
}
// empfange Anzahl an Bytes zu Gesamtanzahl addieren und Buffer in Datei schreiben
recvSize += bytesRecv;
out.write(buffer, bytesRecv);
cout << "\r" << recvSize * 100 / size << "%" << flush; // mit \r an Anfang der Zeile springen
}
}
else
{
if (!chunked)
{
cout << "Downloading... (Unknown FileSize)" << endl;
while (bytesRecv != 0)
{
if (bytesRecv = recv(Socket, buffer, sizeof(buffer), 0) < 0)
{
throw CreateSocketError();
}
out.write(buffer, bytesRecv);
}
}
else
{
cout << "Donwloading... (Chunked)" << endl;
while (true)
{
stringstream sstream;
GetLine((int)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;
// an recv als Größe entweder sizeof(buf) oder aber bytesToRecv übergeben, wenn nur noch ein kleines Stück empfangen werden muss
if (bytesRecv = recv(Socket, buffer, bytesToRecv > sizeof(buffer) ? sizeof(buffer) : bytesToRecv, 0) <= 0)
{
throw CreateSocketError();
}
recvSize += bytesRecv;
out.write(buffer, 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;
}

closesocket(Socket);

cin.get();
return 0;
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
#include <iostream>
#include
<fstream>
#include
<stdexcept>    // runtime_error
#include
<sstream>
#include
<string>
#include
<WinSock2.h>
using namespace std;
// Infos: http://www.c-plusplus.de/forum/viewtopic-var-t-is-169861.html

// wenn während dem Senden ein Fehler auftritt

runtime_error CreateSocketError()
{
    ostringstream temp;
    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<LPWSTR>(&msg), 0, NULL))
    {
        try
        {
            temp << ": " << msg;
            LocalFree(msg);
        }
        catch (...)
        {
            LocalFree(msg);
            throw;
        }
    }
    return runtime_error(temp.str());
}

// um restlichen Buffer noch zu senden, falls send eher aufhört zu senden
void SendAll(int socket, const char* const buf, const int size)
{
    int bytesSent = 0;    // Anzahl Bytes, die bereits vom Buffer gesendet wurden
    do
    {
        int result = send(socket, buf + bytesSent, size - bytesSent, 0);
        if (result < 0)
        {
            throw CreateSocketError();
        }
        bytesSent += result;
    } while (bytesSent < size);
}

// liest eine Zeile des Socket in Stringstream (HTTP-Protokoll sendet zeilenweise)
void GetLine(int socket, stringstream &line)
{
    // byteweise von Socket lesen, bis Zeilenumbruch auftritt
    for (char c; recv(socket, &c, 1, 0) > 0; line << c)
    {
        if (c == '\n')
        {
            return;
        }
    }
    throw CreateSocketError();    // wenn recv < 0 || = 0 ist,
}


int main()
{
    WSADATA w;
    // man will Zugriff auf Winsock-Lib haben
    if (int result = WSAStartup(MAKEWORD(2,2), &w) != 0)
    {
        cout << "Winsock 2 konnte nicht gestartet werden! Error: " << result << endl;
        return 1;
    }
    hostent *phe = gethostbyname("www.kernel.org");
    if (phe == NULL)
    {
        cout << "Host konnte nicht aufgeloest werden!\n";
        return 1;
    }
    if (phe->h_addrtype != AF_INET)
    {
        cout << "ungueltiger Adress-Typ!\n";
        return 1;
    }
    if (phe->h_length != 4)
    {
        cout << "Ungueltiger IP-Typ!\n";
        return 1;
    }


    SOCKET Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (Socket == -1)
    {
        cout << "Socket konnte nicht erstellt werden!\n";
        return 1;
    }

    sockaddr_in service;
    service.sin_family = AF_INET;    // für IPv4
    service.sin_port = htons(80);    // Port 80 nutzt HTTP

    // Iterator

    char **p = phe->h_addr_list;    // p mit 1. Listenelement initialisieren
    int result;                        // Ergebnis von connect
    do
    {
        // Liste zu Ende ?
        if (*p == NULL)
        {
            cout << "Verbindung fehlgeschlagen!\n";
            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!\n";
    const string request = "GET /faq/index.html HTTP/1.1\r\nHost: www.kernel.org\r\nConnection: close\r\n\r\n";
   
    try
    {
        SendAll((int)Socket, request.c_str(), (int)request.size());
        int code = 100;        // 100 = continue
        string protokoll;
        stringstream firstLine;
        while (code == 100)
        {
            GetLine((int)Socket, firstLine);
            firstLine >> protokoll;
            firstLine >> code;
            if (code == 100)
            {
                GetLine((int)Socket, firstLine);    // leere Zeile nach continue ignorieren
            }
        }
        cout << "Protokoll: " << protokoll << endl;
        if (code != 200)
        {
            firstLine.ignore();
            string msg;
            getline(firstLine, msg);
            cout << "Error #" << code << " - " << msg << endl;
            return 0;
        }
   
        bool chunked = false;        // wurde Transfer-Encoding: chunked angegeben ?
        const int noSizeGiven = -1;
        int size = noSizeGiven;        // speichert übergebene Dateigröße

        while (true)
        {
            stringstream sstream;
            GetLine((int)Socket, sstream);
            if (sstream.str() == "\r")        // Header zu Ende
            {
                break;
            }
            string left;
            sstream >> left;
            sstream.ignore();        // Leerzeichen ignorieren
       
            if (left == "Content-Length:")
            {
                sstream >> size;
            }
            if (left == "Transfer-Encoding:")
            {
                string transferEncoding;
                sstream >> transferEncoding;
                if (transferEncoding == "chunked")
                {
                    chunked = true;
                }
            }
        }
       
        fstream out("faq.html", ios::binary|ios::out);
        if (!out)
        {
            cout << "Could not create File!" << endl;
            return 1;
        }

        int recvSize = 0;        // empfangene Bytes insgesamt
        char buffer[1024];
        int bytesRecv = -1;        // empfangene Bytes des letzten recv

        if (size != noSizeGiven)    // wenn Größe über Content-Length gegeben wurde
        {
            cout << "0%";
            while (recvSize < size)
            {
                if (bytesRecv = recv(Socket, buffer, sizeof(buffer), 0) <= 0)
                {
                    throw CreateSocketError();
                }
                // empfange Anzahl an Bytes zu Gesamtanzahl addieren und Buffer in Datei schreiben
                recvSize += bytesRecv;
                out.write(buffer, bytesRecv);
                cout << "\r" << recvSize * 100 / size << "%" << flush;    // mit \r an Anfang der Zeile springen
            }
        }
        else
        {
            if (!chunked)
            {
                cout << "Downloading... (Unknown FileSize)" << endl;
                while (bytesRecv != 0)
                {
                    if (bytesRecv = recv(Socket, buffer, sizeof(buffer), 0) < 0)
                    {
                        throw CreateSocketError();
                    }
                    out.write(buffer, bytesRecv);
                }
            }
            else
            {
                cout << "Donwloading... (Chunked)" << endl;
                while (true)
                {
                    stringstream sstream;
                    GetLine((int)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;
                        // an recv als Größe entweder sizeof(buf) oder aber bytesToRecv übergeben, wenn nur noch ein kleines Stück empfangen werden muss
                        if (bytesRecv = recv(Socket, buffer, bytesToRecv > sizeof(buffer) ? sizeof(buffer) : bytesToRecv, 0) <= 0)
                        {
                            throw CreateSocketError();
                        }
                        recvSize += bytesRecv;
                        out.write(buffer, 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;
    }
   
    closesocket(Socket);

    cin.get();
    return 0;
}


eben derselbe


-----EDIT-----

gelöst, muss jeweils so heißen:

C/C++ Code:
if ((bytesRecv = recv(Socket, buffer, sizeof(buffer), 0)) <= 0)
C/C++ Code:
if ((bytesRecv = recv(Socket, buffer, sizeof(buffer), 0)) <= 0)
C/C++ Code:
if ((bytesRecv = recv(Socket, buffer, sizeof(buffer), 0)) <= 0)

Klammern übersehen ;)

thx
mfg


Zuletzt bearbeitet von R3dNeXX am 15:30:53 12.11.2008, insgesamt 3-mal bearbeitet
Takti
Mitglied

Benutzerprofil
Anmeldungsdatum: 24.11.2008
Beiträge: 5
Beitrag Takti Mitglied 20:24:14 25.11.2008   Titel:              Zitieren

Hallo zusammen
ist es hiermit auch möglich normale datein wie z.b. Zip Dateien zu downloaden.
Und wenn nicht könnte mir dann evtl. jemanden sagen wie ich da vorgehen muss.

Hab mir so ein ähnliches programm geschrieben aber kann irgendwie keine zip,exe usw. Dateien downloaden bekomm dann immer nen Fehler vom Server das die Datei nicht gefunden wurde.
joomoo
Autor

Benutzerprofil
Anmeldungsdatum: 31.07.2004
Beiträge: 1520
Beitrag joomoo Autor 20:28:13 25.11.2008   Titel:              Zitieren

Takti schrieb:
Hallo zusammen
ist es hiermit auch möglich normale datein wie z.b. Zip Dateien zu downloaden.
Und wenn nicht könnte mir dann evtl. jemanden sagen wie ich da vorgehen muss.

Ja, ist damit möglich. Wobei ich eher eine Library wie libcurl (http://curl.haxx.se/ ) nehmen würde.
big_h
Unregistrierter




Beitrag big_h Unregistrierter 02:04:48 11.01.2009   Titel:              Zitieren

Hallo
Ich hab ma ne Frage. Hatt jmd vllt ein Tutorial das genau das Gegenteil macht sprich einen Server??
JunkDealer
Unregistrierter




Beitrag JunkDealer Unregistrierter 10:34:03 09.07.2009   Titel:              Zitieren

wie müsste man den code jetzt erweitern damit das auch mit proxies funktioniert?
jan1985
Unregistrierter




Beitrag jan1985 Unregistrierter 13:30:30 06.10.2009   Titel:              Zitieren

Hallo, ich benutze Microsoft Visual Studio und habe Standard C/C++ Kenntnisse, jedoch komme einfach nicht an diesen Linker Fehlermeldungen vorbei:

Fehler 2 error LNK2019: Verweis auf nicht aufgelöstes externes Symbol "__imp__connect@12" in Funktion "_main". main.obj
Fehler 3 error LNK2019: Verweis auf nicht aufgelöstes externes Symbol "__imp__inet_addr@4" in Funktion "_main". main.obj
Fehler 4 error LNK2019: Verweis auf nicht aufgelöstes externes Symbol "__imp__htons@4" in Funktion "_main". main.obj
Fehler 5 error LNK2019: Verweis auf nicht aufgelöstes externes Symbol "__imp__socket@12" in Funktion "_main". main.obj
Fehler 6 error LNK2019: Verweis auf nicht aufgelöstes externes Symbol "__imp__WSAStartup@8" in Funktion "_main". main.obj
Fehler 7 fatal error LNK1120: 6 nicht aufgelöste externe Verweise. C:\Users\Jan\msvc\Sockets\Debug\Sockets.exe

Mein Code sieht so aus:
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
#include
<winsock2.h>
using namespace std;

#define
IP "79.233.22.28"
#define
PORT 80

int main() {
    WSADATA wsa;
    int s;
    sockaddr_in service;
    int result;

    if(result = WSAStartup(MAKEWORD(2,2), &wsa) != 0) {
        cout << "WinSock Library couldn't be loaded!" << endl;
        cout << "Error: " << result << endl;
        return 1;
    }

    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if(s == -1) {
        cout << "Socket couldn't be set up!" << endl;
        return 1;
    }

    service.sin_family = AF_INET;
    service.sin_port = htons(PORT);
    service.sin_addr.s_addr = inet_addr(IP);
    result = connect(s, reinterpret_cast<sockaddr*>(&service), sizeof(service));

    if(result == -1) {
        cout << "Connection couldn't be established!" << endl;
        return 1;
    }
    cout << "Connection established!" << endl;
    closesocket(s);
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
#include
<winsock2.h>
using namespace std;

#define
IP "79.233.22.28"
#define
PORT 80

int main() {
WSADATA wsa;
int s;
sockaddr_in service;
int result;

if(result = WSAStartup(MAKEWORD(2,2), &wsa) != 0) {
cout << "WinSock Library couldn't be loaded!" << endl;
cout << "Error: " << result << endl;
return 1;
}

s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if(s == -1) {
cout << "Socket couldn't be set up!" << endl;
return 1;
}

service.sin_family = AF_INET;
service.sin_port = htons(PORT);
service.sin_addr.s_addr = inet_addr(IP);
result = connect(s, reinterpret_cast<sockaddr*>(&service), sizeof(service));

if(result == -1) {
cout << "Connection couldn't be established!" << endl;
return 1;
}
cout << "Connection established!" << endl;
closesocket(s);
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
#include
<winsock2.h>
using namespace std;

#define
IP "79.233.22.28"
#define
PORT 80

int main() {
    WSADATA wsa;
    int s;
    sockaddr_in service;
    int result;

    if(result = WSAStartup(MAKEWORD(2,2), &wsa) != 0) {
        cout << "WinSock Library couldn't be loaded!" << endl;
        cout << "Error: " << result << endl;
        return 1;
    }

    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if(s == -1) {
        cout << "Socket couldn't be set up!" << endl;
        return 1;
    }

    service.sin_family = AF_INET;
    service.sin_port = htons(PORT);
    service.sin_addr.s_addr = inet_addr(IP);
    result = connect(s, reinterpret_cast<sockaddr*>(&service), sizeof(service));

    if(result == -1) {
        cout << "Connection couldn't be established!" << endl;
        return 1;
    }
    cout << "Connection established!" << endl;
    closesocket(s);
}


Kann mir damit jemand weiterhelfen?
Vielen Dank schon mal im voraus!
dötz
Unregistrierter




Beitrag dötz Unregistrierter 18:26:23 07.10.2009   Titel:              Zitieren

Zitat:

Sollten Sie Nutzer der MS VisualC++ IDE sein, müssen Sie die WS2_32.lib Bibliothek dem Linker bekannt geben. Dazu gehen Sie unter Projekt->Eigenschaften->Linker und tragen, wie im Screenshot zu sehen, die Library unter "Zusätzliche Abhängigkeiten" ein:

http://www.c-plusplus.de/magazin/bilder/sockets_und_das_http-protokoll/03.png
DaDiceman
Mitglied

Benutzerprofil
Anmeldungsdatum: 05.01.2010
Beiträge: 8
Beitrag DaDiceman Mitglied 11:46:47 05.01.2010   Titel:              Zitieren

Danke für den Tipp!


Zuletzt bearbeitet von DaDiceman am 11:47:12 05.01.2010, insgesamt 1-mal bearbeitet
tobif
Unregistrierter




Beitrag tobif Unregistrierter 00:37:58 17.02.2010   Titel:              Zitieren

Hallo,

ich connecte zu einer Website und kann mit SendAll eine Request machen und mit GetLine das Ergebniss auslesen. Die erste Request klappt. Wenn ich nochmals das gleiche Sende mit SendAll erhalte ich eine Leere Antwort. Beim dritten Versuch erhalte ich schlieslich beim Senden den Error Code 10053.

Muss ich für jeden neuen Http-Request nochmals den socket closen und connecten?

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
string request = "HEAD /index.php HTTP/1.1\r\nHost: www.google.de \r\n\r\n";
std::stringstream line;
SendAll(socket, request.c_str(), request.size());
GetLine(socket, line);
std::cout << line.str() << std::endl //klappt

SendAll(socket, request.c_str(), request.size());
GetLine(socket, line);
std::cout << line.str() << std::endl; //leere Antowrt

SendAll(socket, request.c_str(), request.size()); //fehler beim senden
GetLine(socket, line);
std::cout << line.str() << std::endl;



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.
        {
            std::cout << WSAGetLastError();
        }
        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, sizeof(c), 0) > 0; line << c)
    {
       
    }
   // throw CreateSocketError();
return;
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
string request = "HEAD /index.php HTTP/1.1\r\nHost: www.google.de \r\n\r\n";
std::stringstream line;
SendAll(socket, request.c_str(), request.size());
GetLine(socket, line);
std::cout << line.str() << std::endl //klappt

SendAll(socket, request.c_str(), request.size());
GetLine(socket, line);
std::cout << line.str() << std::endl; //leere Antowrt

SendAll(socket, request.c_str(), request.size()); //fehler beim senden
GetLine(socket, line);
std::cout << line.str() << std::endl;



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.
{
std::cout << WSAGetLastError();
}
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, sizeof(c), 0) > 0; line << c)
{

}
// throw CreateSocketError();
return;
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
string request = "HEAD /index.php HTTP/1.1\r\nHost: www.google.de \r\n\r\n";
std::stringstream line;
SendAll(socket, request.c_str(), request.size());
GetLine(socket, line);
std::cout << line.str() << std::endl //klappt

SendAll(socket, request.c_str(), request.size());
GetLine(socket, line);
std::cout << line.str() << std::endl; //leere Antowrt

SendAll(socket, request.c_str(), request.size()); //fehler beim senden
GetLine(socket, line);
std::cout << line.str() << std::endl;



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.
        {
            std::cout << WSAGetLastError();
        }
        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, sizeof(c), 0) > 0; line << c)
    {
       
    }
   // throw CreateSocketError();
return;
}


Hier der komplette Code, der eigentlich dreimal die Zeit messen soll.
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// socket.cpp: Hauptprojektdatei.
#include
"stdafx.h"
#include
<iostream>
#include
<time.h>
#include
<fstream>
#include
<stdexcept> // runtime_error
#include
<sstream>
#include
<winsock2.h>





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.
        {
            std::cout << WSAGetLastError();
        }
        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, sizeof(c), 0) > 0; line << c)
    {
        if (c=='\n'){
            return;}
    }
   // throw CreateSocketError();
return;
}

float StopTime(int socket, std::string request){
    float sec;
    clock_t tvor;
    tvor=clock();
    SendAll(socket, request.c_str(), request.size());
    std::stringstream line;
        GetLine(socket, line);
        std::cout << line.str() << std::endl;


    sec=(static_cast<float>(clock()-tvor))/CLOCKS_PER_SEC;
    return sec;
}

int main()
{
    using namespace std;

#ifndef
linux
    WSADATA w;
    if(int result = WSAStartup(MAKEWORD(2,2), &w) != 0)
    {
        cout << "Winsock 2 konnte nicht gestartet werden! Error #" << result << endl;
        return 1;
    }
#endif


    hostent* phe = gethostbyname("www.google.de");

    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;
    std::cin.ignore();
    string request = "HEAD /index.php HTTP/1.1\r\nHost: www.google.de \r\n\r\n";
    string ein;
        cout << StopTime(Socket,request);
        cin.ignore()
        cout << StopTime(Socket,request);
        cin.ignore();
        cout << StopTime(Socket,request);
        cin.ignore();
    closesocket(Socket);
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// socket.cpp: Hauptprojektdatei.
#include
"stdafx.h"
#include
<iostream>
#include
<time.h>
#include
<fstream>
#include
<stdexcept> // runtime_error
#include
<sstream>
#include
<winsock2.h>





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.
{
std::cout << WSAGetLastError();
}
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, sizeof(c), 0) > 0; line << c)
{
if (c=='\n'){
return;}
}
// throw CreateSocketError();
return;
}

float StopTime(int socket, std::string request){
float sec;
clock_t tvor;
tvor=clock();
SendAll(socket, request.c_str(), request.size());
std::stringstream line;
GetLine(socket, line);
std::cout << line.str() << std::endl;


sec=(static_cast<float>(clock()-tvor))/CLOCKS_PER_SEC;
return sec;
}

int main()
{
using namespace std;

#ifndef
linux
WSADATA w;
if(int result = WSAStartup(MAKEWORD(2,2), &w) != 0)
{
cout << "Winsock 2 konnte nicht gestartet werden! Error #" << result << endl;
return 1;
}
#endif


hostent* phe = gethostbyname("www.google.de");

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;
std::cin.ignore();
string request = "HEAD /index.php HTTP/1.1\r\nHost: www.google.de \r\n\r\n";
string ein;
cout << StopTime(Socket,request);
cin.ignore()
cout << StopTime(Socket,request);
cin.ignore();
cout << StopTime(Socket,request);
cin.ignore();
closesocket(Socket);
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// socket.cpp: Hauptprojektdatei.
#include
"stdafx.h"
#include
<iostream>
#include
<time.h>
#include
<fstream>
#include
<stdexcept> // runtime_error
#include
<sstream>
#include
<winsock2.h>





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.
        {
            std::cout << WSAGetLastError();
        }
        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, sizeof(c), 0) > 0; line << c)
    {
        if (c=='\n'){
            return;}
    }
   // throw CreateSocketError();
return;
}

float StopTime(int socket, std::string request){
    float sec;
    clock_t tvor;
    tvor=clock();
    SendAll(socket, request.c_str(), request.size());
    std::stringstream line;
        GetLine(socket, line);
        std::cout << line.str() << std::endl;


    sec=(static_cast<float>(clock()-tvor))/CLOCKS_PER_SEC;
    return sec;
}

int main()
{
    using namespace std;

#ifndef
linux
    WSADATA w;
    if(int result = WSAStartup(MAKEWORD(2,2), &w) != 0)
    {
        cout << "Winsock 2 konnte nicht gestartet werden! Error #" << result << endl;
        return 1;
    }
#endif


    hostent* phe = gethostbyname("www.google.de");

    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;
    std::cin.ignore();
    string request = "HEAD /index.php HTTP/1.1\r\nHost: www.google.de \r\n\r\n";
    string ein;
        cout << StopTime(Socket,request);
        cin.ignore()
        cout << StopTime(Socket,request);
        cin.ignore();
        cout << StopTime(Socket,request);
        cin.ignore();
    closesocket(Socket);
}
Anlex
Mitglied

Benutzerprofil
Anmeldungsdatum: 07.03.2010
Beiträge: 4
Beitrag Anlex Mitglied 04:09:28 08.03.2010   Titel:              Zitieren

Hallo allerseits! Ich bin momentan noch auf der ersten Seite dieses Threads und arbeite mich da so langsam durch. Habe aber folgendes Problem:

Bei dem "Selfmade nslookup" funktioniert bei mir die Anzeige der Aliases nicht. Wenn ich das nslookup des Terminals benutze, erhalte ich Aliases, die ich mit dem Code aus diesem Thread nicht bekomme (scheine mit dem hiesigen Code GAR KEINE Aliases angezeigt zu bekommen).

Dazu sei noch gesagt, dass ich das ganze für den linuxartigen Teil des Codes mit g++ unter Cygwin kompilieren lasse.

Weiß jemand Rat?


Zuletzt bearbeitet von Anlex am 04:11:01 08.03.2010, insgesamt 2-mal bearbeitet
warum?
Unregistrierter




Beitrag warum? Unregistrierter 23:42:32 15.05.2010   Titel:   funktioniert mit allem außer mit die-stämme            Zitieren

hallo,
hab den quelltext mit vielen Seiten probiert, aber eine Seite funktioniert nicht.
www.die-staemme.de
kann das jemand anderes mal ausprobieren? Da kommt immer Verbingung erfolgreich und dann nichts mehr... Warum ist das so? Wie kann das umgehen?
bot
Unregistrierter




Beitrag bot Unregistrierter 12:30:46 17.05.2010   Titel:              Zitieren

Vielleicht irgendein Bot Schutz ?

"Schutz gegen Cheater"
hjkhjk
Unregistrierter




Beitrag hjkhjk Unregistrierter 17:08:07 17.05.2010   Titel:              Zitieren

bot schrieb:
Vielleicht irgendein Bot Schutz ?

"Schutz gegen Cheater"


Ja, eventuell wird der User-Agent im Header überprüft oder ähnliches, so das Computerprogramme ausgeschlossen werden. Könnte mir das sehr gut vorstellen.
[MOP]
Mitglied

Benutzerprofil
Anmeldungsdatum: 21.11.2009
Beiträge: 94
Beitrag [MOP] Mitglied 22:08:48 19.05.2010   Titel:              Zitieren

Wenn ich die ganze Server-Response in einem std::string x habe, wie extrahiere ich draus den header?
CASIO
Unregistrierter




Beitrag CASIO Unregistrierter 19:35:14 30.06.2011   Titel:              Zitieren

jan1985 schrieb:
Hallo, ich benutze Microsoft Visual Studio und habe Standard C/C++ Kenntnisse, jedoch komme einfach nicht an diesen Linker Fehlermeldungen vorbei:

Fehler 2 error LNK2019: Verweis auf nicht aufgelöstes externes Symbol "__imp__connect@12" in Funktion "_main". main.obj
Fehler 3 error LNK2019: Verweis auf nicht aufgelöstes externes Symbol "__imp__inet_addr@4" in Funktion "_main". main.obj
Fehler 4 error LNK2019: Verweis auf nicht aufgelöstes externes Symbol "__imp__htons@4" in Funktion "_main". main.obj
Fehler 5 error LNK2019: Verweis auf nicht aufgelöstes externes Symbol "__imp__socket@12" in Funktion "_main". main.obj
Fehler 6 error LNK2019: Verweis auf nicht aufgelöstes externes Symbol "__imp__WSAStartup@8" in Funktion "_main". main.obj
Fehler 7 fatal error LNK1120: 6 nicht aufgelöste externe Verweise. C:\Users\Jan\msvc\Sockets\Debug\Sockets.exe

Mein Code sieht so aus:
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
#include
<winsock2.h>
using namespace std;

#define
IP "79.233.22.28"
#define
PORT 80

int main() {
    WSADATA wsa;
    int s;
    sockaddr_in service;
    int result;

    if(result = WSAStartup(MAKEWORD(2,2), &wsa) != 0) {
        cout << "WinSock Library couldn't be loaded!" << endl;
        cout << "Error: " << result << endl;
        return 1;
    }

    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if(s == -1) {
        cout << "Socket couldn't be set up!" << endl;
        return 1;
    }

    service.sin_family = AF_INET;
    service.sin_port = htons(PORT);
    service.sin_addr.s_addr = inet_addr(IP);
    result = connect(s, reinterpret_cast<sockaddr*>(&service), sizeof(service));

    if(result == -1) {
        cout << "Connection couldn't be established!" << endl;
        return 1;
    }
    cout << "Connection established!" << endl;
    closesocket(s);
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
#include
<winsock2.h>
using namespace std;

#define
IP "79.233.22.28"
#define
PORT 80

int main() {
WSADATA wsa;
int s;
sockaddr_in service;
int result;

if(result = WSAStartup(MAKEWORD(2,2), &wsa) != 0) {
cout << "WinSock Library couldn't be loaded!" << endl;
cout << "Error: " << result << endl;
return 1;
}

s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if(s == -1) {
cout << "Socket couldn't be set up!" << endl;
return 1;
}

service.sin_family = AF_INET;
service.sin_port = htons(PORT);
service.sin_addr.s_addr = inet_addr(IP);
result = connect(s, reinterpret_cast<sockaddr*>(&service), sizeof(service));

if(result == -1) {
cout << "Connection couldn't be established!" << endl;
return 1;
}
cout << "Connection established!" << endl;
closesocket(s);
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
#include
<winsock2.h>
using namespace std;

#define
IP "79.233.22.28"
#define
PORT 80

int main() {
    WSADATA wsa;
    int s;
    sockaddr_in service;
    int result;

    if(result = WSAStartup(MAKEWORD(2,2), &wsa) != 0) {
        cout << "WinSock Library couldn't be loaded!" << endl;
        cout << "Error: " << result << endl;
        return 1;
    }

    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if(s == -1) {
        cout << "Socket couldn't be set up!" << endl;
        return 1;
    }

    service.sin_family = AF_INET;
    service.sin_port = htons(PORT);
    service.sin_addr.s_addr = inet_addr(IP);
    result = connect(s, reinterpret_cast<sockaddr*>(&service), sizeof(service));

    if(result == -1) {
        cout << "Connection couldn't be established!" << endl;
        return 1;
    }
    cout << "Connection established!" << endl;
    closesocket(s);
}


Kann mir damit jemand weiterhelfen?
Vielen Dank schon mal im voraus!
EOP
Mitglied

Benutzerprofil
Anmeldungsdatum: 15.09.2007
Beiträge: 690
Beitrag EOP Mitglied 21:10:10 30.06.2011   Titel:              Zitieren

Entsprechende lib einbinden!
wsock32.lib oder ws2_32.lib - hab vergessen wie die heisst.
jhkhjk
Unregistrierter




Beitrag jhkhjk Unregistrierter 00:55:34 04.07.2011   Titel:              Zitieren

EOP schrieb:
Entsprechende lib einbinden!
wsock32.lib oder ws2_32.lib - hab vergessen wie die heisst.
Letztere.
PtrPtr
Unregistrierter




Beitrag PtrPtr Unregistrierter 00:18:39 15.07.2011   Titel:              Zitieren

Artchi schrieb:
Die Kompressionsrate des JPEGs ist nur zu hoch. Man kann deshalb fast nichts erkennen. Ansonst hat das Bild irgendwie einen gewissen Charme. :D


Für eine derartige Grafik ist JPEG völlig ungeeignet. Am besten PNG.
PtrPtr
Unregistrierter




Beitrag PtrPtr Unregistrierter 00:21:32 15.07.2011   Titel:              Zitieren

Hab nochmal nachgeschaut, es ist schon, wie es sich gehört, ein PNG. :D
PlanloserCoder
Unregistrierter




Beitrag PlanloserCoder Unregistrierter 16:49:59 13.09.2011   Titel:   Recv will nicht ganz            Zitieren

Weiß jemand warum bei mir die recv Funktion nicht ganz geht? Also besser gesagt will ich das das er die While schleife verlässt wenn ein bestimmtes Zeichen im String vorliegt... das letzte Zeichen beim empfangen endet mit "]", dennoch läuft die schleife weiter auch wenn ich (c == ']') in den Funktion schreibe. Die Text Datei wird ohne Zeichen einfach weiter gefüllt da recv nicht gestoppt wird. Weiß jemand woran das liegt oder wie ich am besten aus der while schleife raus komme wenn das letzte zeichen ein "]" ist, so das mein Programm nach empfangen aller Daten weiter laufen kann?
Fake oder Echt
Mitglied

Benutzerprofil
Anmeldungsdatum: 03.10.2007
Beiträge: 888
Beitrag Fake oder Echt Mitglied 10:00:12 15.09.2011   Titel:              Zitieren

empfängst und sendest du die daten zeichenweise oder als string ?
und normalerweise läuft recv nicht einfach weiter, es sei denn du sendest halt immer wieder was neu oder du hast iwo ein speicher problem, alles in allem wär der entsprechende code ganz interessant :)

_________________
Entwicklungsumgebung: Code::Blocks 10.05, Windows 7, C++, WinAPI, OpenGL
http://xourscode.lima-city.de/
DaRe
Mitglied

Benutzerprofil
Anmeldungsdatum: 11.09.2011
Beiträge: 218
Beitrag DaRe Mitglied 21:51:56 22.09.2011   Titel:              Zitieren

PlanloserCoder schrieb:
Weiß jemand warum bei mir die recv Funktion nicht ganz geht?

Also ich hoffe doch mal sehr, dass recv geht. Immerhin ist das keine Funktion die man so am Rande nutzt.

PlanloserCoder schrieb:
Also besser gesagt will ich das das er die While schleife verlässt wenn ein bestimmtes Zeichen im String vorliegt... das letzte Zeichen beim empfangen endet mit "]", dennoch läuft die schleife weiter auch wenn ich (c == ']') in den Funktion schreibe. Die Text Datei wird ohne Zeichen einfach weiter gefüllt da recv nicht gestoppt wird. Weiß jemand woran das liegt oder wie ich am besten aus der while schleife raus komme wenn das letzte zeichen ein "]" ist, so das mein Programm nach empfangen aller Daten weiter laufen kann?


-> Ist der TCP Socket blockierend(bin mir selbst nicht sicher, aber wenn der non-blocking ist, müsste recv einfach durchlaufen und einen Fehler returnen geben, wenn keine Daten anliegen)? Ich geh mal von TCP aus, wenn du dich bei HTTP meldest.

-> Was ist denn die genaue Rückgabe von recv?

-> Unter Linux kannst du recv ebenfalls nicht blockierend einstellen. Ist das MSG_DONTWAIT Flag in recv gesetzt?

Am besten du lässt mal ein bisschen Code sehen, dann kann ich mir auch ein besseres Bild machen.


Zuletzt bearbeitet von DaRe am 21:53:02 22.09.2011, insgesamt 1-mal bearbeitet
Skylax
Mitglied

Benutzerprofil
Anmeldungsdatum: 19.01.2012
Beiträge: 1
Beitrag Skylax Mitglied 01:09:51 19.01.2012   Titel:   Bug in 07.cpp            Zitieren

Der Artikel ist zwar jetzt schon ziemlich alt und ich weiß nicht ob das noch jemanden interessiert, aber die letzte Version des Programms (07.cpp, Link im Schlusswort) hat einen Bug im "Chunked download" Teil. Hier wird zunächst die Chunksize mit der GetLine Funktion ausgelesen (welche in den globalBuffer schreibt), um dann mit Recv den html text zu empfangen. Wenn das Ende des Chunks erreicht ist wird mit zweimaligem Aufruf von recv noch das verbleibende CRLF (\r\n) entsorgt (sowie im Artikel beschrieben). Die gemischte Verwendung der lowlevel 'recv' Funktion und high level 'Recv' Funktion ist hier aber problematisch. Ist der Chunk nämlich kleiner als die in GetLine verwendete Puffergröße, so ist das verbleibenden CRLF schon im globalBuffer, die recv löscht dann Zeichen die erst später kommen, während \r\n im globalBuffer verbleibt. Beim nächsten GetLine Aufruf wird dann \r in den sstream geschrieben und die falsche Chunksize ausgelesen, so dass das Programm fälschlicherweise abbricht. Entweder man ersetzt die for-schleife durch Recv(socker, &temp, 2,0) oder man macht es wie folgt:
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
cout << "Downloading... (Chunked)" << endl;
while(true)
{
    stringstream sstream;
    GetLine(Socket, sstream);
    //weiter Zeile auslesen, falls ein \r\n vorliegt:
    if(sstream.str() == "\r")  
    {
       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;    
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
cout << "Downloading... (Chunked)" << endl;
while(true)
{
stringstream sstream;
GetLine(Socket, sstream);
//weiter Zeile auslesen, falls ein \r\n vorliegt:
if(sstream.str() == "\r")
{
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;
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
cout << "Downloading... (Chunked)" << endl;
while(true)
{
    stringstream sstream;
    GetLine(Socket, sstream);
    //weiter Zeile auslesen, falls ein \r\n vorliegt:
    if(sstream.str() == "\r")  
    {
       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;    
}

wobei ich die for-schleife weggelassen und durch einen bedingten GetLine Aufruf ersetzt habe.
C/C++ Forum :: Die Artikel ::  Sockets und das HTTP-Protokoll   Auf Beitrag antworten

Zeige alle Beiträge auf einer Seite




Nächstes Thema anzeigen
Vorheriges Thema anzeigen
Sie können keine Beiträge in dieses Forum schreiben.
Sie können auf Beiträge in diesem Forum antworten.
Sie können Ihre Beiträge in diesem Forum nicht bearbeiten.
Sie können Ihre Beiträge in diesem Forum nicht löschen.
Sie können an Umfragen in diesem Forum nicht mitmachen.

Powered by phpBB © 2001, 2002 phpBB Group :: FI Theme

c++.de ist Teilnehmer des Partnerprogramms von Amazon Europe S.à.r.l. und Partner des Werbeprogramms, das zur Bereitstellung eines Mediums für Websites konzipiert wurde, mittels dessen durch die Platzierung von Werbeanzeigen und Links zu amazon.de Werbekostenerstattung verdient werden kann.

Die Vervielfältigung der auf den Seiten www.c-plusplus.de, www.c-plusplus.info, www.c-sar.de, www.c-plusplus.net und www.baeckmann.de enthaltenen Informationen ohne eine schriftliche Genehmigung des Seitenbetreibers ist untersagt (vgl. §4 Urheberrechtsgesetz). Die Nutzung und Änderung der vorgestellten Strukturen und Verfahren in privaten und kommerziellen Softwareanwendungen ist ausdrücklich erlaubt, soweit keine Rechte Dritter verletzt werden. Der Seitenbetreiber übernimmt keine Gewähr für die Funktion einzelner Beiträge oder Programmfragmente, insbesondere übernimmt er keine Haftung für eventuelle aus dem Gebrauch entstehenden Folgeschäden.