19
In Kapitel 18, »Sockets, MAPI und das Internet«, wurden die WinInet-Klassen vorgestellt, mit denen Sie relativ anspruchsvolle Internet-Anwendungen erstellen können. Im vorliegenden Kapitel wird eine Internet-Anwendung entwickelt, die die Funktionsweise einiger dieser Klassen veranschaulicht. Die Anwendung dient zugleich einem praktischen Zweck: Sie können Sie dafür einsetzen, mehr über die Internet-Präsenz einer Firma oder Organisation zu erfahren. Dazu brauchen Sie weder etwas über Sockets zu lernen, noch Genaueres über die Behandlung von Internet-Protokollen zu wissen.
Stellen Sie sich vor, Sie haben die E-Mail-Adresse einer Person (etwa kate@gregcons.com) und möchten mehr Informationen über die Domäne (in diesem Fall gregcons.com ) in Erfahrung bringen. Oder Sie haben eine gute Idee für einen Domänennamen und möchten herausfinden, ob dieser Name bereits durch einen anderen Internet-Teilnehmer belegt ist. Die hier zu erstellende Abfrageanwendung verucht durch unterschiedliche Methoden, eine Verbindung zu gregcons.com (oder greatidea.org oder einem sonstigen vom Anwender angegebenen Namen) herzustellen, und teilt das Ergebnis dieser Versuche dem Anwender mit.
mit der Abfrageanwendung ermittelnDie Anwendung erhält eine einfache Bedieneroberfläche. Die einzige Information, die vom Anwender benötigt wird, ist der Name der abzufragenden Domäne, und zum Ablegen dieser Information braucht kein eigenes Dokument angelegt werden. Sie könnten einen Menüpunkt mit dem Namen Query erstellen, das ein Dialogfeld für die Angabe des Namens der Site aufruft. Hier bietet sich jedoch der Einsatz einer dialogfeldbasierenden Anwendung an, in deren Dialogfeld die Schaltfläche Query eingebunden wird.
Um das Gerüst für diese Anwendung zu erstellen, wählen Sie im Developer Studio den Befehl Datei/Neu und öffnen das Register Projekte. Markieren Sie MFC-Anwendungs-Assistent (exe), tragen Sie den Projektnamen Query ein, und wählen Sie in Schritt 1 die Anwendungsart Dialogfeldbasierend aus (vgl. Abbildung 19.1). Klicken Sie auf Weiter, damit Schritt 2 des Anwendungs-Assistenten aufgeschlagen wird.
Abbildung 19.1: Für die Abfrageanwendung eignet sich ein dialogfeldbasiertes Programm.
In Schritt 2 des Anwendungs-Assistenten fordern Sie ein Info-Dialogfeld an, verzichten auf eine kontextabhängige Hilfe und auf 3D-Steuerelemente. Es wird weder eine Unterstützung für Automatisierungen, ActiveX-Steuerelemente noch für Windows-Sockets benötigt. (In dieser Anwendung werden keine Socket-Funktionen direkt aufgerufen). Geben Sie einen aussagekräftigen Namen für die Titelleiste des Dialogfelds ein. Die entsprechenden Kontrollkästchen sollten im Anwendungs-Assistenten wie in Abbildung 19.2 aktiviert bzw. deaktiviert werden. Klicken Sie auf Weiter, damit Schritt 3 des Anwendungs-Assistenten aufgeschlagen wird.
Abbildung 19.2: Diese Anwendung benötigt keine kontextsensitive Hilfe, Automatisierung, ActiveX-Steuerelemente oder Sockets.
Die weiteren Schritte des Anwendungs-Assistenten müßten Ihnen inzwischen bekannt sein: Kommentare sollen erzeugt und die MFC-Bibliotheken als gemeinsam genutzte DLL verwendet werden. Die vom Anwendungs-Assistenten vorgeschlagene Klassenbezeichnungen können übernommen werden. Sobald die Einstellungen des Anwendungs-Assistenten angewendet und fertiggestellt sind, können Sie damit beginnen, das Kernstück der Abfrageanwendung zu erstellen.
Vom Anwendungs-Assistenten wird ein leeres Dialogfeld erstellt, das Ihnen als Ausgangspunkt dient (siehe Abbildung 19.3). Um dieses Dialogfeld bearbeiten zu können, öffnen Sie das Register ResourceView, zeigen den Inhalt des Ordners Abfrage-Ressourcen und dann des Ordners Dialog an und klicken zweimal auf die Ressource IDD_QUERY_DIALOG. Die folgenden Schritte machen aus diesem Dialogfeld die Bedieneroberfläche für die Abfrageanwendung.
Abbildung 19.3: Vom Anwendungs-Assistenten wird automatisch ein leeres Dialogfeld erstellt.
Das fertige Dialogfeld und die Format-Eigenschaften des großen Eingabefelds sollten in etwa Abbildung 19.4 entsprechen.
Abbildung 19.4: Die Bedieneroberfläche der Abfrage wird in Form eines einzelnen Dialogfelds erstellt.
Wenn der Anwender auf die Schaltfläche Query klickt, sollte eine Abfrage nach der Site durchgeführt werden. Der letzte Schritt beim Erstellen der Oberfläche besteht darin, die Schaltfläche Query mit Hilfe des Klassen-Assistenten mit Programmcode zu verbinden. Dazu führen Sie folgende Arbeitsschritte aus:
Abbildung 19.5: Fügen Sie eine Funktion hinzu, welche die Aktion beim Klicken auf die Schaltfläche Query bestimmt. Die ID dieser Funktion ist immer noch IDOK.
Abbildung 19.6: Verbinden Sie IDC_HOST mit CQueryDlg::m_host.
Klicken Sie auf OK, um den Klassen-Assistenten zu schließen. Jetzt brauchen Sie nur noch den Inhalt der Funktion CQueryDlg::OnQuery zu programmieren, die den Wert in m_host für die Erzeugung der Ausgabezeilen für m_out verwendet.
Als erste Verbindungsart bei der Erkundung der Internet-Präsenz einer Domäne sollte eine Verbindung über das HTTP-Protokoll getestet werden, da viele Sites Web-Seiten besitzen. Die einfachste Art eines HTTP-Verbindungsaufbaus besteht in der Anwendung der WinInet-Klasse CInternetSession und dem Aufruf ihrer OpenURL-Funktion. Als Ergebnis wird eine Datei geliefert, deren erste Zeilen Sie in m_out anzeigen lassen können. Fügen Sie zunächst am Anfang der Quelldatei QueryDlg.cpp die folgende Zeile ein:
#include "afxinet.h"
Dadurch sind die WinInet-Klassen für Ihren Code zugänglich. Da diese Anwendung eine Reihe von URL-Adressen ausprobieren wird, fügen Sie in CQueryDlg eine Funktion mit dem Namen TryURL ein. Diese Funktion akzeptiert einen CString-Parameter mit dem Namen URL und liefert einen Rückgabewert vom Typ void. Dazu öffnen Sie im Arbeitsbereich das Register ClassView, klicken mit der rechten Maustaste auf CQueryDlg und wählen Member-Funktion hinzufügen, um TryURL als protected-Mitgliedsfunktion aufzunehmen. Die neue Funktion TryURL wird von CQueryDlg::OnQuery aufgerufen (vgl. Listing 19.1). Ergänzen Sie OnQuery um diesen Code.
Listing 19.1: CQueryDlg::OnQuery() |
void CQueryDlg::OnQuery()
{
const CString http = "http://";
UpdateData(TRUE);
m_out = "";
UpdateData(FALSE);
TryURL(http + m_host);
TryURL(http + "www." + m_host);
}
Der Aufruf von UpdateData(TRUE) belegt m_host mit dem vom Anwender eingegebenen Wert. Der Aufruf von UpdateData(FALSE) füllt das schreibgeschützte Eingabefeld IDC_OUT mit dem Wert der zurückgesetzten Variable m_out. Danach folgen zwei Aufrufe von TryURL. Angenommen der Anwender hat microsoft.de eingegeben, so würde der erste Aufruf die Adresse http://microsoft.de testen und der zweite die Adresse http://www.microsoft.de. Den Inhalt von TryURL finden Sie in Listing 19.2.
Listing 19.2: CQueryDlg::TryURL() |
void CQueryDlg::TryURL(CString URL)
{
CInternetSession session;
m_out += "Trying " + URL + "\r\n";
UpdateData(FALSE);
CInternetFile* file = NULL;
try
{
//Wir wissen mit Sicherheit, daß es sich hier um eine
//Internet-Datei handelt, eine Typumwandlung ist also
//sicher
file = (CInternetFile*) session.OpenURL(URL);
}
catch (CInternetException* pEx)
{
//Bei fehlerhaftem Ablauf wird file einfach auf
//NULL gesetzt.
file = NULL;
pEx->Delete();
}
if (file)
{
m_out += "Connection established. \r\n";
CString line;
for (int i=0; i < 20 && file->ReadString(line); i++)
{
m_out += line + "\r\n";
}
file->Close();
delete file;
}
else
{
m_out += "No server found there. \r\n";
}
m_out += "-------------------------\r\n";
UpdateData(FALSE);
}
Im weiteren Verlauf dieses Abschnitts wird dieser Code noch einmal präsentiert, allerdings jeweils nur einige Zeilen. Zunächst wird die Internet-Sitzung aufgebaut, indem Sie eine Instanz von CInternetSession erstellen. Dieser Konstruktor besitzt mehrere Parameter, deren Standardwerte jedoch für diese Anwendung ausreichen werden:
Für dwAccessType wird standardmäßig der in der Windows-Registrierung konfigurierte Wert verwendet. Um unter Windows Ihren Standard-Internet-Zugang einzustellen, klicken Sie zweimal auf das Desktop-Symbol Arbeitsplatz, dann auf das Symbol Systemsteuerung und in der Systemsteuerung auf das Symbol Internet. Im Dialogfeld Eigenschaften von Internet öffnen Sie das Register Verbindung (siehe Abbildung 19.7) und füllen es entsprechend aus.
Abbildung 19.7: Ist Ihre Internet-Verbindung einmal eingerichtet, so kann sie von allen Anwendungen über der Registrierung abgerufen werden.
Wenn Sie eine asynchrone (nichtblockierende) Sitzung einrichten möchten (die dafür sprechenden Gründe wurden in Kapitel 18, »Sockets, MAPI und das Internet«, im Abschnitt »Windows-Sockets« angeführt), muß INTERNET_FLAG_ASYNC eine der Optionen im Parameter dwFlags sein. Zusätzlich müssen Sie die Mitgliedsfunktion EnableStatusCallback aufrufen, um die Rückruf-Funktion (Callback-Funktion) einzurichten. Wenn durch die Sitzung eine Anforderung erfolgt û etwa als Aufruf von OpenURL, was später in TryURL vorkommt û und keine sofortige Antwort möglich ist, so liefert eine nichtblockierende Sitzung den Pseudo-Fehlercode ERROR_IO_PENDING. Sobald dann eine Antwort möglich ist, wird von diesem Sitzungstyp die Rückruf-Funktion automatisch aufgerufen.
Für diese einfache Anwendung besteht kein Bedarf, den Anwender andere Aufgaben oder eine Interaktion mit der Oberfläche durchführen zu lassen, während er auf die Antwort wartet. Daher wird die Sitzung als blockierende Sitzung definiert, und auch für die anderen Parameter werden die Standardwerte verwendet.
CInternetSession session;
Nachdem von TryURL die Sitzung aufgebaut wurde, sorgt diese Funktion dafür, daß in m_out eine Zeile eingefügt wird, welche die URL-Adresse wiedergibt, die als Parameter geliefert wurde. Die Zeichen "\r\n" stehen für »Return« und »Neue Zeile«; sie trennen die in m_out eingefügten Zeilen. UpdateData(FALSE) überträgt die Werte letztlich in das Dialogfeld.
m_out += "Trying " + URL + "\r\n";
UpdateData(FALSE);
Als nächstes folgt ein Aufruf der Mitgliedsfunktion OpenURL dieser Sitzung. Diese Funktion liefert einen Zeiger auf einen von mehreren verschiedenen Dateitypen, da eine URL-Adresse auf vier verschiedene Protokolle verweisen kann:
Da sowohl CGopherFile als auch CHttpFile Erben von CInternetFile sind und da Sie sicher sein können, daß an TryURL keine URL-Adresse des Typs file:// übergeben wird, ist es sicher, den gelieferten Zeiger in CInternetFile umzuwandeln.
Läßt sich die URL nicht öffnen, nimmt file den Wert NULL an oder OpenURL wirft eine Ausnahme. (Hintergrundinformationen zu Ausnahmen finden Sie in Kapitel 26, »Ausnahmen, Templates und die neuesten Ergänzungen zu C++«.) Da diese Anwendung dem Test erfundener URL-Adressen bzw. von Adressen, über deren korrekte Syntax und Schreibweise Sie nicht ganz sicher sind, dient, ist es nicht erstaunlich, wenn einige dieser URLs nicht funktionieren. Als Ergebnis sollten Sie diese Ausnahmen selbst abfangen und nur dafür sorgen, daß Laufzeitfehler vermieden werden. In diesem Fall reicht es, sicherzustellen, daß file den Wert NULL hat, wenn eine Ausnahme ausgelöst wird. Um die Ausnahme zu löschen und Lücken im Speicher zu vermeiden, rufen Sie CException::Delete auf. Diese Funktion wird zwar in der Online-Dokumentation nicht erwähnt, existiert jedoch und löscht die Ausnahme sicher. Der Code-Block mit dem Aufruf von OpenURL findet sich in Listing 19.3.Ausnahmenloeschen
Listing 19.3: CQueryDlg::TryURL() |
CInternetFile* file = NULL;
try
{
//Wir wissen mit Sicherheit, daß es sich hier um eine
//Internet-Datei handelt, eine Typumwandlung ist also
//sicher.
file = (CInternetFile*) session.OpenURL(URL);
}
catch (CInternetException* pEx)
{
//Bei fehlerhaftem Ablauf wird file einfach auf
//NULL gesetzt.
file = NULL;
pEx->Delete();
}
Beträgt file nicht NULL, wird durch diese Routine ein Teil des gefundenen Web-Seiteninhalts angezeigt. Zunächst wird eine andere Zeile in m_out ausgegeben. Dann ruft die Routine in einer for-Schleife CInternetFile::ReadString auf, um die Cstring-Variable line mit den Zeichen in file bis zum ersten \r\n zu füllen. Diese Zeichen werden abgeschnitten. Dieser Code ergänzt m_out einfach um line (und ein weiteres \r\n). Sollen mehr oder weniger als die ersten 20 Zeilen angezeigt werden, passen Sie die Zahl in dieser for-Schleife entsprechend an. Sobald die ersten paar Zeilen gelesen wurden, wird die Datei von TryURL geschlossen und die CInternetFile-Instanz gelöscht. Diesen Code-Block sehen Sie in Listing 19.4.
Listing 19.4: CQueryDlg::TryURL() |
if (file)
{
m_out += "Connection established. \r\n";
CString line;
for (int i=0; i < 20 && file->ReadString(line); i++)
{
m_out += line + "\r\n";
}
file->Close();
delete file;
}
Wenn sich die Datei nicht öffnen ließ, wird eine entsprechende Meldung in m_out ausgegeben.
else
{
m_out += "No server found there. \r\n";
}
Anschließend wird an m_out eine gestrichelte Linie angeheftet, unabhängig davon, ob die Datei existierte oder nicht. Und schließlich bringt ein letzter Aufruf von UpdateData(FALSE) den neuen m_out-Inhalt auf den Bildschirm:
m_out += "-------------------------\r\n";
UpdateData(FALSE);
}
Jetzt können Sie diese Anwendung erstellen und ausführen. Wenn Sie microsoft.de in das Textfeld eingeben und auf Query klicken, werden Sie feststellen, daß sowohl unter der Adresse http://microsoft.de als auch unter http://www.microsoft.de Web-Seiten zu finden sind. In Abbildung 19.8 sehen Sie das Ergebnis dieser Abfrage.
Abbildung 19.8: Die Abfrage findet die Web-Seiten von Microsoft.
Werden von der Abfrage die Web-Seiten weder unter dem angegebenen Domänenamen noch unter www. plus dem Domänenamen gefunden, so folgt daraus keineswegs, daß die Domäne nicht existiert und erst recht nicht, daß das Unternehmen, das den Domänenamen besitzt, über keine Web-Seite verfügt. Es ist allerdings weniger wahrscheinlich, daß das Unternehmen existiert und auch eine Web-Seite besitzt. Ist HTML-Code zu sehen, so wissen Sie mit Sicherheit, daß das Unternehmen existiert und eine Web-Seite besitzt. Vielleicht können Sie den HTML-Code ja selbst lesen; auf jeden Fall können Sie jetzt mit einem Web-Browser wie dem Internet Explorer von Microsoft eine Verbindung zu der Site herstellen.
Bei der Untersuchung eines Site-Namens sollten Sie überprüfen, ob es auch einen FTP-Site gibt. Die meisten FTP-Sites haben Namen wie ftp.company.com, wenn auch die Form des Namens für ältere Sites oft variiert. Das überprüfen dieses Sitzes ist nicht so einfach wie das Aufrufen der Funktion TryURL für HTTP-Sites, da TryURL davon ausgeht, daß die URL zu einer Datei führt und URL-Adressen wie ftp.greatidea.org auf eine Dateiliste verweisen, die nicht einfach geöffnet und gelesen werden kann. Man könnte zwar die Funktion TryURL noch weiter ausbauen, doch wäre dies etwas kompliziert; daher empfiehlt es sich eher, eine neue Funktion in die Klasse aufzunehmen, z.B. TryFTPSite(CString host). (Klicken Sie im Register ClassView mit der rechten Maustaste auf CQueryDlg, und wählen Sie Member-Funktion hinzufügen. Die Funktion kann den Rückgabewert void liefern, der Zugriffsstatus ist public.
Die Funktion TryFTPSite muß eine Verbindung zu der Sitzung aufbauen, und wenn die Verbindung hergestellt ist, muß sie Informationen abrufen, die zu m_out hinzugefügt werden können, um dem Anwender zu zeigen, daß eine Verbindung aufgenommen wurde. Das Abrufen einer Dateiliste ist etwas komplex, und da diese Anwendung nur illustrativen Charakter hat, soll die einfachere Aufgabe, den Namen des Standard-FTP-Verzeichnisses abzurufen, genügen. Den Code finden Sie in Listing 19.5.
Listing 19.5: CQueryDlg::TryFTPSite() |
void CQueryDlg::TryFTPSite(CString host)
{
CInternetSession session;
m_out += "Trying FTP site " + host + "\r\n";
UpdateData(FALSE);
CFtpConnection* connection = NULL;
try
{
connection = session.GetFtpConnection(host);
}
catch (CInternetException* pEx)
{
connection = NULL;
pEx->Delete();
}
if (connection)
{
m_out += "Connection established. \r\n";
CString line;
connection->GetCurrentDirectory(line);
m_out += "default directory is " + line + "\r\n";
connection->Close();
delete connection;
}
else
{
m_out += "No server found there. \r\n";
}
m_out += "-------------------------\r\n";
UpdateData(FALSE);
}
Dieser Code ist dem Code von TryURL sehr ähnlich, nur wird nicht mit session.OpenURL eine Datei geöffnet, sondern mit session.GetFtpConnection eine FTP-Verbindung hergestellt. Ausnahmen werden wieder abgefangen und im wesentlichen ignoriert, wobei die Routine einfach sicherstellt, daß der Verbindungszeiger nicht verwendet wird. Der Aufruf von GetCurrentDirectory liefert das Verzeichnis auf der entfernten Site. Der Rest der Routine ist genau wie bei TryURL.
Fügen Sie am Ende von OnQuery die folgenden zwei Zeilen ein, damit diese neue Funktion aufgerufen wird:
TryFTPSite(m_host);
TryFTPSite("ftp." + m_host);
Erstellen Sie die Anwendung, und führen Sie sie aus. Abbildung 19.9 zeigt die Anwendung Query nach der Abfrage: Unter der Adresse microsoft.com wurde kein FTP-Site gefunden, erfolgreich war der Versuch jedoch mit der Adresse ftp.microsoft.com. Es dauert etwas, bis die Ergebnisse im Ausgabefenster erscheinen. Wenn es Ihnen zu lange dauert, können Sie dies durch die Verwendung asynchroner Sockets oder Programmfäden korrigieren, so daß frühe Ergebnisse bereits angezeigt, während spätere noch empfangen werden. Doch für eine einfache Beispielanwendung wie diese warten Sie einfach geduldig, bis die Ergebnisse erscheinen. Dies kann einige Minuten dauern, abhängig vom Netzverkehr zwischen der Site Ihres Providers und der Microsoft-Site, der Datenübertragungsrate zwischen Ihrem Anschluß und dem Provider usw.
Abbildung 19.9: Die Abfrage findet einen FTP-Site von Microsoft.
Wenn die Anwendung Query weder Web-Seiten noch einen FTP-Site findet, existiert die Domäne vielleicht gar nicht, oder sie verfügt über keine anderen Internet-Dienste außer E-Mail, doch noch sind nicht alle Ermittler-Tricks ausgespielt. Die Ergebnisse der folgenden Untersuchungen werden Ihre Kenntnisse über vorhandene Sites definitiv erweitern.
Wie bei FTP-Sites funktioniert TryURL nicht für das Abfragen einer Gopher-Site wie gopher.company.com, da keine einzelne Datei, sondern eine Liste mehrerer Dateinamen ausgegeben wird. Die Lösung ist wieder, eine eigene Funktion zu schreiben, diesmal mit dem Namen TryGopherSite. Diese Funktion ist fast identisch mit der Funktion TryFTPSite, nur wird eben eine CGopherConnection aufgebaut und anstelle einer einzelnen Zeile, die das Standardverzeichnis beschreibt, eine einzelne Zeile ausgegeben, die den mit der Site verbundenen Gopher-Lokator beschreibt. Fügen Sie die Funktion TryGopherSite in die Klasse CQueryDlg ein, indem Sie im Register ClassView mit der rechten Maustaste auf CQueryDlg klicken und wieder Member-Funktion hinzufügen wählen. Der Code für TryGopherSite befindet sich in Listing 19.6.
Listing 19.6: CQueryDlg::TryGopherSite() |
void CQueryDlg::TryGopherSite(CString host)
{
CInternetSession session;
m_out += "Trying Gopher site " + host + "\r\n";
UpdateData(FALSE);
CGopherConnection* connection = NULL;
try
{
connection = session.GetGopherConnection(host);
}
catch (CInternetException* pEx)
{
connection = NULL;
pEx->Delete();
}
if (connection)
{
m_out += "Connection established. \r\n";
CString line;
CGopherLocator locator = connection->CreateLocator(NULL, NULL,
GOPHER_TYPE_DIRECTORY);
line = locator;
m_out += "first locator is " + line + "\r\n";
connection->Close();
delete connection;
}
else
{
m_out += "No server found there. \r\n";
}
m_out += "-------------------------\r\n";
UpdateData(FALSE);
}
GOPHER_TYPE_TEXT_FILE
GOPHER_TYPE_DIRECTORY
GOPHER_TYPE_CSO
GOPHER_TYPE_ERROR
GOPHER_TYPE_MAC_BINHEX
GOPHER_TYPE_DOS_ARCHIVE
GOPHER_TYPE_UNIX_UUENCODED
GOPHER_TYPE_INDEX_SERVER
GOPHER_TYPE_TELNET
GOPHER_TYPE_BINARY
GOPHER_TYPE_REDUNDANT
GOPHER_TYPE_TN3270
GOPHER_TYPE_GIF
GOPHER_TYPE_IMAGE
GOPHER_TYPE_BITMAP
GOPHER_TYPE_MOVIE
GOPHER_TYPE_SOUND
GOPHER_TYPE_HTML
GOPHER_TYPE_PDF
GOPHER_TYPE_CALENDAR
GOPHER_TYPE_INLINE
OPHER_TYPE_UNKNOWN
GOPHER_TYPE_ASK
GOPHER_TYPE_GOPHER_PLUS
In der Regel werden Lokatoren für Dateien oder Verzeichnisse nicht erstellt, sondern der Server wird danach gefragt. Der Lokator, der als Rückgabewert des Ausrufs von CreateLocator geliefert wird, beschreibt den Lokator, der zu dem erkundeten Site gehört.
Fügen Sie am Ende von OnQuery das folgende Zeilenpaar ein, um die neue Funktion TryGopherSite aufzurufen:
TryGopherSite(m_host);
TryGopherSite("gopher." + m_host);
Erstellen Sie die Anwendung erneut, und führen Sie sie aus. Möglicherweise müssen Sie einige Minuten auf die Ergebnisse warten. In Abbildung 19.10 sehen Sie, daß die Abfrage zwei Gopher-Sites für harvard.edu gefunden hat. In beiden Fällen beschreibt der Lokator den Site selbst. Dies reicht aus als Beweis, daß unter harvard.edu ein Gopher-Site existiert, und mehr wird von der Anwendung Query auch nicht erwartet.
Abbildung 19.10: Das Programm Query findet zwei Harvard-Gopher-Sites.
Es gibt noch ein Protokoll, das Ihnen Informationen über einen Site bereitstellen kann. Es ist eines der ältesten Protokolle des Internet, nämlich Finger. Sie können damit einen bestimmten Teilnehmer eines Site ermitteln. Viele Sites haben Finger zwar deaktiviert, doch viele andere werden Ihnen als Anwort auf Finger immer noch wertvolle Informationen zukommen lassen.
Es gibt keine MFC-Klasse oder API-Funktion, deren Name das Wort Finger beinhaltet, doch bedeutet dies nicht, daß sich die bereits vorgestellten Klassen nicht für eine Finger-Abfrage eignen. Dieser Abschnitt wendet einen Trick an, der auf der Kenntnis der Protokolle Finger und Gopher basiert. Die WinInet-Klassen sind nicht nur ein Segen für neue Internet-Programmierer, die die Funktionsweise des Internet nicht so genau kennen, sondern haben auch alten Hasen viel zu bieten, die wissen, was hinter den Kulissen vor sich geht.
Wie bereits im Abschnitt »Windows-Sockets« des Kapitels 18, »Sockets, MAPI und das Internet«, erörtert, sind an jeder Internet-Transaktion ein Host und ein Port beteiligt. Bekannte Dienste verwenden Standard-Port-Nummern. Wenn Sie etwa CInternetSession::OpenURL mit einer URL aufrufen, die mit http:// beginnt, so werden Sie hinter den Kulissen mit Port 80 des entfernten Host verbunden. Und wenn Sie GetFtpConnection aufrufen, wird eine Verbindung mit Port 21 des entfernten Host hergestellt. Gopher verwendet Port 70. Wenn Sie Abbildung 19.10 genauer betrachten, werden Sie feststellen, daß der Lokator, der den Site gopher.harvard.edu beschreibt, dabei den Port 70 erwähnt.
Die Gopher-Dokumentation hat die Erklärung dafür: Wenn Sie einen Lokator mit einem Host-Namen Port 70, Gopher-Typ 0 (GOPHER_TYPE_TEXT_FILE ist gleich 0) und eine Zeichenfolge mit einem Dateinamen erstellen, so sendet jeder Gopher-Client einfach diese Zeichenfolge an Port 70 û und zwar unabhängig davon, ob es sich um einen Dateinamen handelt oder nicht. Die Reaktion des an diesem Port wartenden Gopher-Servers ist das Senden der Datei.
Nun ist Finger ebenfalls ein einfaches Protokoll. Wenn Sie eine Zeichenfolge an Port 79 eines entfernten Host senden, so reagiert der dort wartende Finger-Server auf die Zeichenfolge, indem er eine Finger-Antwort sendet. Ist die Zeichenfolge einfach \r\n, so ist die Standardantwort eine Liste aller Anwender auf diesem Host sowie einiger Informationen über sie, etwa ihre richtigen Namen. (Viele Sites betrachten dies als ein Eindringen in die Privatsphäre bzw. als Sicherheitslücke und deaktivieren Finger deshalb. Doch viele andere Sites wiederum stellen genau dieselbe Information auf Ihren Web-Seiten zur Verfügung.)
Wenn man nun diese Informationen alle zusammenfaßt, ergibt sich folgendes: Wenn Sie einen Gopher-Lokator mit Port 79 û anstelle des Standardports 70 û und einem leeren Dateinamen erstellen, können Sie unter Verwendung der MFC-WinInet-Klassen eine Finger-Abfrage durchführen. Zunächst ergänzen Sie die Klasse CQueryDlg um eine weitere Funktion, nämlich TryFinger, die Daten vom Typ CString annimmt und Daten vom Typ void liefert. Der Code für diese Funktion ähnelt wiederum den Code für TryGopherSite, nur wird eine Verbindung zu Port 79 hergestellt:
connection = session.GetGopherConnection(
host,NULL,NULL,79);
Sobald die Verbindung hergestellt ist, wird ein Textdateilokator erstellt:
CGopherLocator locator =
connection->CreateLocator(NULL, NULL,
GOPHER_TYPE_TEXT_FILE);
Diesmal wird der Lokator nicht einfach in eine CString-Variable geschrieben, sondern zum Öffnen einer Datei verwendet:
CGopherFile* file =
connection->OpenFile(locator);
Dann werden die ersten 20 Zeilen dieser Datei ausgegeben, so wie TryURL bereits die ersten 20 Zeilen der vom Web-Server gelieferten Datei ausgegeben hat. Den Code dafür finden Sie in Listing 19.7.
Listing 19.7: CQueryDlg::TryFinger() Excerpt |
if (file)
{
CString line;
for (int i=0; i < 20 && file->ReadString(line); i++)
{
m_out += line + "\r\n";
}
file->Close();
delete file;
}
Alles zusammen ergibt den Inhalt der Funktion TryFinger, der in Listing 20.8 noch einmal angezeigt wird.
Listing 19.8: CQueryDlg::TryFinger() |
void CQueryDlg::TryFinger(CString host)
{
CInternetSession session;
m_out += "Trying to Finger " + host + "\r\n";
UpdateData(FALSE);
CGopherConnection* connection = NULL;
try
{
connection = session.GetGopherConnection(host,NULL,NULL,79);
}
catch (CInternetException* pEx)
{
connection = NULL;
pEx->Delete();
}
if (connection)
{
m_out += "Connection established. \r\n";
CGopherLocator locator = connection->CreateLocator(NULL, NULL, åGOPHER_TYPE_TEXT_FILE);
CGopherFile* file = connection->OpenFile(locator);
if (file)
{
CString line;
for (int i=0; i < 20 && file->ReadString(line); i++)
{
m_out += line + "\r\n";
}
file->Close();
delete file;
}
connection->Close();
delete connection;
}
else
{
m_out += "No server found there. \r\n";
}
m_out += "-------------------------\r\n";
UpdateData(FALSE);
}
Am Ende von OnQuery wird die folgende Zeile eingefügt, um diese neue Funktion aufzurufen:
TryFinger(m_host);
Erstellen Sie die Anwendung wieder, und führen Sie sie aus. Abbildung 19.11 zeigt das Ergebnis einer Abfrage des Site whitehouse.gov, und zwar den Finger-Abschnitt am Ende des Ausgabefensters.
Abbildung 19.11: Die Abfrage erhält die E-Mail-Adressen des Finger-Servers des Weißen Hauses.
Ein letztes Protokoll bietet noch Informationen über Sites. Es handelt sich wieder um ein Protokoll, das von den WinInet-Klassen nicht direkt unterstützt wird. Der Name dieses Protokolls ist Whois, und es ist ein Dienst, der nur von wenigen Servern angeboten wird. Die Server, die diesen Dienst anbieten, werden von den Organisationen gewartet, die die Domänennamen registrieren. Domänennamen, die mit com enden, werden beispielsweise von einer Organisation namens InterNIC registriert, die den Whois-Server rs.internic.net betreibt (das rs steht für Registration Services). Wie auch Finger reagiert Whois auf eine Zeichenfolge, die an seinen Port gesendet wird; der Whois-Port ist 43. Im Gegensatz zu Finger senden Sie im Lokator keine leere Zeichenfolge, sondern den Namen des Host, den Sie nachschlagen möchten. Sie stellen jedesmal eine Verbindung zu rs.internic.net her. (Überzeugte Whois-Server bieten den Anwendern die Möglichkeit, dies zu ändern, doch geschieht dies so gut wie nie.)
Fügen Sie, wie gewohnt, eine Funktion mit dem Namen TryWhois hinzu. Sie akzeptiert Daten des Typs CString host und liefert void. Der Code ist in Listing 19.9 angeführt.
Listing 19.9: CQueryDlg::TryWhois() |
void CQueryDlg::TryWhois(CString host)
{
CInternetSession session;
m_out += "Trying Whois for " + host + "\r\n";
UpdateData(FALSE);
CGopherConnection* connection = NULL;
try
{
connection = session.GetGopherConnection
("rs.internic.net",NULL,NULL,43);
}
catch (CInternetException* pEx)
{
connection = NULL;
pEx->Delete();
}
if (connection)
{
m_out += "Connection established. \r\n";
CGopherLocator locator = connection->CreateLocator(NULL, host,
GOPHER_TYPE_TEXT_FILE);
CGopherFile* file = connection->OpenFile(locator);
if (file)
{
CString line;
for (int i=0; i < 20 && file->ReadString(line); i++)
{
m_out += line + "\r\n";
}
file->Close();
delete file;
}
connection->Close();
delete connection;
}
else
{
m_out += "No server found there. \r\n";
}
m_out += "-------------------------\r\n";
UpdateData(FALSE);
}
Fügen Sie am Ende von OnQuery die folgende Zeile ein:
TryWhois(m_host);
Erstellen Sie die Anwendung ein letztes Mal, und führen Sie sie aus. In Abbildung 19.12 sehen Sie den Whois-Abschnitt des Berichts für mcp.com û die Domäne von Macmillan Computer Publishing.
Da Sie nun neuen Code nach dem Finger-Abschnitt dieser Anwendung eingefügt haben, kann man es nicht mehr ignorieren, wenn der Finger-Code keine Verbindung herstellen kann. Wenn der Aufruf von OpenFile in TryFinger versucht, eine Datei auf einem Host zu öffnen, der keinen Finger-Server betreibt, wird eine Ausnahme geworfen. Die Kontrolle wird nicht an OnQuery zurückgegeben, und TryWhois wird nie aufgerufen werden. Um dies zu verhindern, müssen Sie den Aufruf von OpenFile mit einem try- und catch-Block umrahmen. In Listing 19.10 sehen Sie, welche Änderungen vorzunehmen sind.
Listing 19.10: Änderungen an TryFinger |
// Ersetzen Sie die folgende Zeile:
CGopherFile* file = connection->OpenFile(locator);
// durch diese Zeilen:
CGopherFile* file = NULL;
try
{
file = connection->OpenFile(locator);
}
catch (CInternetException* pEx)
{
file = NULL;
pEx->Delete();
}
Ändern Sie TryFinger, erstellen Sie Query noch einmal, und fragen Sie einen Site ab, der keinen Finger-Server betreibt, z.B. microsoft.com. Sie sollten den Whois-Abschnitt der Anwendung erfolgreich erreichen.
Abbildung 19.12: Query erhält echte Adressenund Namen vom InterNIC-Whois-Server.
Die in diesem Kapitel erstellte Anwendung Query macht viel, doch könnte sie noch mehr erkunden. Es gibt E-Mail- und News-Protokolle, auf die man zugreifen könnte, wenn man die WinInet-Klassen noch etwas mehr ausweitete. Ähnlich wie oben könnten Sie sie verwenden, um eine Verbindung zu den Standard-Ports dieser Dienste herzustellen. Sie könnten auch eine Verbindung zu einigen der bekanntesten Suchmaschinen im Web herstellen und Abfragen absetzen, indem Sie URL-Adressen entsprechend den von diesen Suchmaschinen verwendeten Muster formulieren. Auf diese Weise können Sie die Suche im Internet automatisieren, die die meisten von uns etwas unorganisiert durchführen, wenn wir nach einem Domänennamen oder einer bestimmten Firma suchen.
Wenn Sie mehr über Internet-Protokolle und Portnummern erfahren wollen oder wissen möchten, was passiert, wenn ein Client eine Verbindung zu einem Server herstellt, sollten Sie das Que-Buch » Internet-Applikationen mit Visual C++« lesen. Dieses Buch wurde allerdings für Visual C++ 2.0 geschrieben, und wenn auch alle Anwendungen in dem Buch mit späteren MFC-Versionen kompiliert und ausgeführt werden können, würde man sie heute viel kürzer und einfacher programmieren. Denoch ist die Einsicht in die Funktionsweise der Protokolle, die Sie hier gewinnen können, wertvoll.
Die WinInet-Klassen können noch viel mehr als Sie hier gesehen haben. So werden sie in der Anwender Query nicht dafür verwendet, richtige Dateien über das Internet abzurufen. Mit Visual C++ 5.0 wurden zwei WinInet-Beispielanwendungen geliefert, die das Abrufen von Dateien anschaulich erläutern:
Für die nächsten Monate sind von Microsoft einige Neuigkeiten zu erwarten. Werfen Sie immer mal wieder wegen Bibliotheken und Software-Entwicklungs-Kits, die das Entwikkeln von Internet-Software noch leichter und schneller machen, einen Blick auf deren Web-Site www.microsoft.de.
Dieses Kapitel stellte Ihnen die WinInet-Klassen als eine Möglichkeit vor, Internet-Programme zu schreiben. Einige verwandte Kapitel, die für Sie von Interesse sein könnten, sind die folgenden: