Von C nach Java, Teil 3: HTML-Dokumente aus dem Internet laden

Seite 2: HTML-Zugriff

Inhaltsverzeichnis

Zum Prototyping der Funktionen gibt es nicht viel zu erklären. Wie diese Funktionen zu verwenden und zu implementieren sind, zeigt die main()-Funktion der getURL.c:

int main(int argc, char *argv[]) {
int s, l, argcnt=1;
char *url, buffer[BUFFER_SZ];

if (argc < 2) usage();
#ifdef WIN32
{
WSADATA wsaData;
if (WSAStartup(0x0202, &wsaData)) {
fprintf (stderr,"WSAStartup() FAILED.\n");
}
}
#endif
while (--argc) {
url=argv[argcnt++];
if (ReceiveHtmlDocument(url, buffer, &l)<0) {
exit(errno);
}
printf ("%d bytes received from %s\n", l, url);
close(s);
}
}

Als Argument erwartet die Funktion einen URL, falls der Aufruf des Programms keinen enthält, beendet die usage()-Funktion die Programmausführung mit einem entsprechenden Hinweis. Beim Initialisieren der Socket-Bibliothek muss man noch zwischen Unix- und Windows-Umgebungen unterscheiden. In der Unix-Umgebung sind keine besonderen Schritte notwendig, während unter Windows explizit der Aufruf WSAStartup erfolgen muss, der nicht anderes tut, als die wsock32.dll im Speicher zu lokalisieren oder in den Speicher zu laden, falls es bis dahin noch nicht geschehen ist.

Nun kann die weitere Ausführung folgen, und es wird für jeden in der Kommandozeile angegebenen URL die Funktion ReceiveHtmlDocument() aufgerufen, die als Parameter den String des URLs, einen Puffer für das HTML-Dokument und die Adresse einer Integer-Variablen, die die Größe des Puffers aufnehmen soll, erwartet.

Hier zunächst die Connect()-Funktion, die innerhalb von ReceiveHtmlDocument() eingangs aufgerufen wird, um eine Verbindung zum Webserver herzustellen.

int Connect(char *host, int port) {
struct hostent *h;
struct sockaddr_in addr;
int s;

if ((h=gethostbyname(host))==NULL) {
fprintf(stderr,"gethostbyname() FAILED with %d: %s\n", errno,
strerror(errno));
return -1;
}
if ((s=socket(AF_INET,SOCK_STREAM,0))<0) {
fprintf(stderr,"socket() FAILED with %d: %s\n", errno,
strerror(errno));
return -1;
}
memset(&addr, 0, sizeof(struct sockaddr_in));
addr.sin_family=AF_INET;
memcpy(&addr.sin_addr,h->h_addr_list[0],h->h_length);
addr.sin_port=htons(80);
if (connect(s, (struct sockaddr *) &addr,
sizeof(struct sockaddr_in))<0) {
fprintf(stderr,"connect() FAILED with %d: %s\n", errno,
strerror(errno));
return -1;
}
return s;
}

In Java wäre das ein Einzeiler. Hier ist hingegen zunächst ist der Hostname über einen Aufruf von gethostbyname() aufzulösen. Die Funktion gibt den Zeiger auf eine hostent-Struktur zurück, die unter Unix in netdb.h und unter Windows in winsock.h definiert ist. Teil dieser hostent-Struktur ist die IP-Adresse, die der Connect-Aufruf benötigt. Da connect() sie allerdings – neben weiteren Parametern – wiederum in einer sockaddr_in-Struktur erwartet, ist einiges an zusätzlichem Code erforderlich, der in dem Codebeispiel aufgeführt und weitgehend selbsterklärend ist. Als TCP-Port für die Verbindung zum Webserver wird hier fix 80 verwendet. Sollte ein anderer Port verwendet oder der Port über eine Option dem Programm übermittelt werden, ist der Code entsprechend zu ändern.

Erwähnenswert zudem die Funktion htons() (host to network short), die einen Short-Wert in ein für die Struktur sockaddr_in erforderliches "Network-ordered" Format bringt. Die Umkehrfunktion hierzu heisst ntohs(). Die Funktion gibt es auch für long-Werte (htonl(), ntohl()), unter anderem für die Konvertierung von IP-Adressen, was in dem obigen Beispiel allerdings nicht erforderlich ist.

Die Funktion Connect() gibt einen gültigen Socket-Deskriptor zurück, oder aber -1, wenn irgendetwas schiefgelaufen sein sollte.

Für das Aufteilen eines URL-Strings in seine Bestandteile ruft das Beispiel die Funktion crackURL() auf. Sie erkennt das Protokoll, den Hostnamen und den kompletten Pfadnamen des Dokuments, das der Nutzer abrufen möchte:

int crackURL(char *url, char *hostname, char *urls) {
char *s, *d;
int l;

if ((s=strstr(url, "://"))==NULL) {
fprintf(stderr,"missing protocol unit in URL!\n");
return -1;
}
if ((d=strchr(s+3, '/'))==NULL) {
fprintf(stderr,"missing URL unit in URL!\n");
return -1;
}
l=(d-(s+3)); strncpy(hostname,s+3,l); hostname[l]=0;
strcpy(urls, d);
return l;
}

Sie gibt die Länge des Pfadnamens des Dokuments oder aber -1 zurück, wenn Fehler aufgetreten sind. Als Parameter erwartet die Funktion den URL (Eingabe), den Hostnamen (Ausgabe) und den Pfadnamen des Dokuments (Ausgabe), wobei der Aufrufer dafür zu sorgen hat, dass genügend Platz für die Aufnahme der Werte zur Verfügung steht. Will man diese Dinge exakt umsetzen, wäre hier nochmals das Doppelte an Code fällig, die sich der Autor der Übersicht halber an dieser Stelle spart.