Inhaltsverzeichnis 1. 2. 3 4. Motivation ................................................................................................................... 2 Überblick..................................................................................................................... 2 TracePlus/Winsock2 ................................................................................................... 4 Grundlagen .................................................................................................................. 5 4.1 Dynamic Link Libraries ...................................................................................... 5 4.1.1 Allgemeines über DLLs .............................................................................. 5 4.1.2 Vorteile von DLLs ...................................................................................... 6 4.1.3 Exportieren von Funktionen aus einer DLL ............................................... 6 4.1.4 Implizites und Explizites Linken ................................................................ 8 4.1.5 Suchpfad nach einer DLL ......................................................................... 10 4.1.6 „Memory Sharing“ zwischen DLLs................................................................. 10 4.2 Architektur der Winsock2-API ......................................................................... 11 4.2.1 Unterstützung mehrerer Protokoll-Familien ............................................. 11 4.2.2 Kompatibilität mit Winsock1.1 Applikationen ......................................... 12 4.2.3 Befehle für die Datenkommunikation ....................................................... 13 4.2.4 Blockierende und nicht-blockierende Funktionen .................................... 14 5. Entwicklung .............................................................................................................. 14 5.1 Erster Ansatz: Implementierung einer neuen Winsock2-DLL ......................... 14 5.1.1 Automatisches Erzeugen des Codes ......................................................... 19 5.1.2 Implementierungsaspekte ......................................................................... 20 5.1.3 Installation................................................................................................. 20 5.2 Zweiter Ansatz: Die „Debug/Trace Version“ der Winsock2-DLL................... 21 5.2.1 Architektur der „Debug/Trace Version“ der Winsock2-DLL .................. 21 5.2.2 WSAPreApiNotify und WSAPostApiNotify............................................ 22 5.2.3 Implementierung ....................................................................................... 24 5.3 Zusammenfassung beider Ansätze .................................................................... 27 6. Daten erfassen und visualisieren ............................................................................... 27 6.1 Winsock2-API-Monitor .................................................................................... 27 6.2 Performance Monitor ........................................................................................ 28 6.3 LOG-Datei ........................................................................................................ 28 6.3.1 Ausgabe der LOG-Dateien ....................................................................... 29 6.3.2 Performance Factor ................................................................................... 30 6.3.3 Übersetzung der LOG-Datei ..................................................................... 30 7. Installation................................................................................................................. 33 8. Konfigurationsprogramm .......................................................................................... 33 9. Zusammenfassung und Ausblick .............................................................................. 36 Literaturverzeichnis .......................................................................................................... 38 -1- 1. Motivation Netzwerktechnologien und Kommunikationsprotokolle ermöglichen dem Nutzer bzw. den Applikationen verschiedene Dienste zu nutzen. ATM-Netzwerke bieten z.B. die Möglichkeit, für jede einzelne Verbindung einen QoS auszuhandeln. Soll diese Möglichkeit genutzt werden, dann müssen detaillierte Informationen über Kommunikationsanforderungen der Applikation an die unteren Netzwerkschichten vorliegen. Dazu gehört zum einen der Bandbreitenbedarf einer Applikation, z.B. Häufigkeit, Dauer, oder dauerhaft genutzte Bandbreite. Zum anderen gehören dazu Anforderungen an die Übertragungsqualität, z.B. Verzögerung, Verzögerungsschwankungen oder durchschnittliche Datenverlustraten. Die Anwendungen im Bereich der Internetkommunikation variieren zwischen einfachem Datenaustausch und komplexen interaktiven Multimedia-Applikationen. Demnach stellen die Applikationen unterschiedliche Anforderungen an die Netzwerkschnittstelle. Es gibt viele Programme, die es erlauben, den Datenaustausch auf unteren Ebenen zu kontrollieren. Die so genannten „Sniffer“ erlauben es, den Datenverkehr auf dem Ethernet, was dem Layer 2 des ISO/OSI-Modells entspricht, zu beobachten. Andere Programme sind in der Lage, Statistiken über die IP-Pakete (Layer 3) bzw. TCP- oder UDP-Pakete (Layer 4) zu erstellen. Mit diesen Programmen kann man allerdings keine Aussagen über die Anforderungen einer Applikation und ihr Verhalten an der API (Application Programmer Interface) treffen. 2. Überblick In dieser Arbeit wird ein Tool entwickelt, das die Analyse des Verhaltens der Applikationen an der API-Schnittstelle ermöglicht. Das Tool erfasst die Aufrufe von Netzwerkfunktionen mit ihren Parametern, z.B. die Paketgröße beim Senden und Empfangen. Zuerst wird mit „TracePlus®/Winsock" ein kommerzielles Tool vorgestellt, das diese Aufgabe erfüllt. Die Vor- und Nachteile von TracePlus werden beschrieben. Danach wird ein eigenes Tool entwickelt. Es werden zum Teil die Parameter, mit denen die Netzwerkfunktionen aufgerufen werden, protokolliert. Es werden Zeitstempel geschrieben, damit eine genauere Analyse der QoS-Anforderungen der Applikationen möglich wird. Das Tool wird auf der Plattform Windows2000 entwickelt. Die API ist Winsock2, die in der DLL (Dynamic Link Library) Ws2_32.dll realisiert ist. Bevor die Entwicklung des Tools beschrieben wird, werden einige Grundlagen über DLLs und über die Winsock2API vorgestellt. Die Entwicklung des Tools selbst gliedert sich in zwei Schritte: -2- Der erste Schritt ist die Erfassung der Funktionsaufrufe der Winsock2-API mit ihren Parametern. Hier soll ein Verfahren implementiert werden, welches den Zugriff auf die Schnittstelle zwischen den Applikationen und der Winsock2-API ermöglicht (siehe Abb.1). Zwei Verfahren werden vorgestellt: a) Im ersten Verfahren wird eine neue Winsock2-DLL implementiert, welche die alte DLL ersetzt. b) Im zweiten Verfahren wird eine spezielle Version der Winsock2-DLL verwendet: die so genannte „Debug/Trace Version“. Es wird eine Dt_DLL (Dt steht für Debug/Trace) geschrieben, mit der die Winsock2-DLL kommuniziert. Der zweite Schritt ist die Visualisierung der Funktionsaufrufe mit ihren Parametern. Hier werden drei Verfahren diskutiert: a) Anzeigen der Funktionsaufrufe durch einen Winsock2-API-Monitor b) Visualisieren der Funktionsaufrufe mit dem Performance Monitor (PerfMon.exe). c) Protokollieren der relevanten Daten in einer LOG-Datei und Visualisieren dieser Daten nachträglich, z.B. in Excel-Diagrammen Applikation API Messung Winsock2 Abb.1: Vereinfachte Beschreibung des Winsock2-API-Monitoring Anschließend wird jeweils ein Verfahren ausgewählt und ein Einblick in die Auswertung gemacht. -3- 3. TracePlus®/Winsock TracePlus ist ein Tool, das in der Lage ist, Prozesse zu starten und die Winsock2-APIAufrufe auf einem Monitor anzuzeigen oder in einer Datei zu speichern. Abb. 2 zeigt, wie TracePlus den Internet Explorer startet und die Winsock2-API-Aufrufe anzeigt. Abb.2: „API View“ von TracePlus mit dem Internet Explorer. Bei einem gestarteten Prozess verfügt TracePlus über vier verschiedene Fenster: „API View“, „Data View“, „Socket View“ und „Packet Statistics“. „API View“ zeigt die Aufrufe der Winsock2-Funktionen mit ihren Parametern und mit Zeitstempeln an. „Data View“ stellt den Inhalt der gesendeten bzw. empfangenen Pakete dar. Eine Unterscheidung zwischen ASCII- und hexadezimalen Daten ist möglich. „Socket View“ verfolgt die Änderung des Zustands eines Sockets während seines Lebenszyklus (Allocated, Bound, Connected, Closed, Blocking, Not Blocking). „Packet Statistics“ zeigt weitere Informationen über die gesendeten und empfangenen Pakete an: Paketgröße, Sende- bzw. Empfangszeitpunkt und Zeitspanne beim Senden oder Empfangen mit einer Genauigkeit von Millisekunden. Eine Demo-Version von TracePlus kann unter http://www.sstinc.com/ herunter geladen werden. Dieses Programm ist gut geeignet, um einige Internetanwendungen zu „debuggen“ oder Erkenntnisse über bestehende Anwendungen zu gewinnen. Man kann -4- z.B. genaue Informationen über das HTTP-Protokoll erhalten, wenn man TracePlus mit einem Internet Browser startet. Doch für zeitkritische Anwendungen wie „Voice Over IP“ oder Multimediaanwendungen hilft uns TracePlus nicht weiter: Die Genauigkeit der Zeitangaben in Millisekunden ist zu grob. Innerhalb einer Millisekunde können mehrere Funktionen aus der Winsock2-API aufgerufen werden, insbesondere kann mehrmals gesendet und empfangen werden. Außerdem zeigt der Test von TracePlus enttäuschende Ergebnisse: TracePlus stürzt einfach ab, wenn es zusammen mit der „Voice Over IP“ – Anwendung „HiNet Xpress“ gestartet wird. Bei „Netmeeting“ ist TracePlus nicht in der Lage, die Funktionsaufrufe der Winsock2 zu verfolgen. Das Tool liefert einfach gar keine Ausgaben. Dadurch zeigt sich, dass TracePlus für unsere Anforderungen nicht geeignet ist. Multimedia-Anwendungen sind ein wichtiges Gebiet, in dem die Analyse der Anforderung einer Applikation an der API-Schnittstelle von Bedeutung ist. Der Inhalt der gesendeten und empfangenen Daten ist nicht relevant, denn es handelt sich meistens nicht um ASCII-Daten. Deshalb wurde in dieser Arbeit auf dieses kommerzielle Tool verzichtet und ein eigenes Programm entwickelt. 4. Grundlagen In diesem Abschnitt werden Grundlagen über DLLs (Dynamic Link Libraries) und über die Winsock2-API vorgestellt, die für das Verständnis dieser Arbeit erforderlich sind. 4.1 Dynamic Link Libraries Hier werden DLLs definiert und ihre Charakteristika vorgestellt. 4.1.1 Allgemeines über DLLs DLLs sind Module, die Funktionen enthalten, welche von anderen Modulen aufgerufen werden können. Eine DLL wird zur Laufzeit durch ein anderes Modul (eine ausführbare EXE-Datei oder eine andere DLL) geladen. Eine Instanz der DLL mit den zugehörigen Variablen wird an den Speicherbereich, der für den laufenden Prozess reserviert ist, angehängt. DLLs können zwei Arten von Funktionen definieren: externe und interne Funktionen. Externe Funktionen können von anderen Modulen aufgerufen werden. Interne Funktionen können nur innerhalb der DLL selbst verwendet werden. -5- 4.1.2 Vorteile von DLLs Es gibt mehrere Gründe, DLLs einzusetzen: Erstens lässt sich die Größe der ausführbaren Anwendungsdateien verringern, indem man die von mehreren Anwendungen gemeinsam genutzte Funktionalität in DLLs unterbringt. Zweitens kann der Code der DLLs aktualisiert und modifiziert werden, ohne die ausführbare Anwendung neu kompilieren zu müssen. Man kann sogar neue Funktionen implementieren und exportieren, die neuen Anwendungen bereitgestellt werden, ohne dabei alte Anwendungen zu beeinflussen. Schließlich kann man DLLs mit nahezu jeder anderen Programmiersprache unter Windows verwenden. Die Win32-API ist als Sammlung von DLLs implementiert. Die Winsock2-API ist z.B. in der Datei Ws2_32.dll implementiert. In den nächsten zwei Abschnitten wird beschrieben, wie man Funktionen aus einer DLL exportieren und in ein Modul importieren kann. 4.1.3 Exportieren von Funktionen aus einer DLL Eine DLL ist ähnlich aufgebaut wie eine ausführbare Datei, allerdings gibt es zwei entscheidende Unterschiede: Eine DLL ist allein nicht ausführbar. Eine DLL besitzt eine Export-Tabelle. In der Export-Tabelle stehen die Namen der Funktionen, die von der DLL exportiert werden. Die Export-Tabelle einer DLL kann man sich mit dem DOS-Befehl DUMPBIN oder mit dem Programm „Dependency Walker“1 anschauen. Als Beispiel betrachten wir hier eine einfache DLL, die zwei Funktionen exportiert: gimme_a_string1 und gimme_a_string2. 1 Ein Tool von Visual C++ -6- const char * gimme_a_string1(void) { return "Hello "; } const char * gimme_a_string2(void) { return "World\n"; Listing 4.1: Implementierung von gimme_a_string1 und gimme_a_string2. Diese zwei Funktionen sollen in einer ausführbaren Datei wie folgt aufgerufen werden: printf(gimme_a_string1()); printf(gimme_a_string2()); Listing 4.2: Aufruf von gimme_a_string1 und gimme_a_string2. Damit wird die gesamte Ausgabe „Hello World“. Es gibt zwei Methoden, eine Funktion aus einer DLL zu exportieren: Mit dem Schlüsselwort __declspec(dllexport) (siehe Listing 4.3) Mit einer DEF-Datei, die verwendet wird, um die Export-Tabelle zu definieren. (siehe Listing 4.4). ……….. __declspec(dllexport) const char * gimme_a_string1(void); __declspec(dllexport) const char * gimme_a_string2(void); ……….. Listing 4.3: Exportieren mit __declspec(dllexport) LIBRARY STR_DLL DESCRIPTION "a sample for using DLLs" ; Here goes explicit function export EXPORTS gimme_a_string1 gimme_a_string2 Listing 4.4: Exportieren mit einer DEF-Datei. Die Interpretation einer DEF-Datei durch den Linker bietet auch einige Freiheiten: Es gibt z.B. die Möglichkeit, eine Funktion mit einem anderen Namen zu exportieren als der Name, mit dem sie in dem Code der DLL definiert wird; z.B.: -7- EXPORTS string1 = gimme_a_string1 string2 = gimme_a_string2 Listing 4.5: Exportieren mit anderen Namen. Diese beiden Funktionen werden im Code mit gimme_a_string1 und gimme_a_string2 definiert und sind nach außen nur mit string1 bzw. string2 sichtbar. Es besteht auch die Möglichkeit, Funktionen aus anderen DLLs zu exportieren; z.B.: EXPORTS gimme_a_string1 gimme_a_string2=Proxy_Dll.gimme_a_string2 Listing 4.6: Exportieren aus anderen DLLs. In diesem Beispiel wird die Funktion gimmme_a_string2 exportiert, die in Proxy_Dll.dll implementiert ist. Wenn die Implementierung der Funktion gimme_a_string2 in der Proxy_Dll wie folgt aussieht, const char * gimme_a_string2(void) { return "Bob\n"; } Listing 4.7: Implementierung von gimme_a_string2 in Proxy_Dll. dann ist die gesamte Ausgabe des Programms “Hello Bob” und nicht “Hello World“. In diesem Fall muss die Datei STR_DLL.dll mit der Proxy_DLL.lib durch den Linker verknüpft werden. 4.1.4 Implizites und Explizites Linken Das übliche Verfahren, eine Funktion in ein Modul zu importieren, besteht darin, die Funktion mit dem Schlüsselwort __declspec(dllimport) in dem Code des Moduls zu deklarieren. ……….. __declspec(dllimport) const char * gimme_a_string1(void); __declspec(dllimport) const char * gimme_a_string2(void); ……….. Listing 4.8: Importieren mit __declspec(dllimport). Wenn man mit dem Entwicklungstool Visual C++ eine DLL erzeugt, wird eine HeaderDatei erzeugt, die sowohl für die DLL als auch für die Module, welche diese DLL -8- verwenden, benutzt werden kann. Mit dem Makro STR_DLL_EXPORTS kann man den Fall jeweils unterscheiden. Dieses Makro wird in der DLL von Visual C++ automatisch definiert.2 #ifdef STR_DLL_EXPORTS #define STR_DLL_API __declspec(dllexport) #else #define STR_DLL_API __declspec(dllimport) #endif Listing 4.9: Abkürzung von __declspec(dllimport) und __declspec(dllexport) mit Hilfe eines Makros. Beim Kompilieren der DLL wird zusätzlich zu der Datei STR_DLL.dll eine Datei STR_DLL.lib erzeugt. Module, die Funktionen aus der DLL benutzen sollen, müssen mit dieser LIB-Datei durch den Linker verknüpft werden, damit die Bezeichnungen gimme_a_string1 und gimme_a_string2 im Programmcode aufgeschlüsselt werden können. Die Funktionen gimme_a_string1 und gimme_a_string2 können so aufgerufen werden, als ob sie in demselben Modul definiert wären. Dieses Verfahren heißt „Implizites Linken“. Doch es gibt ein zweites Verfahren, eine DLL zur Laufzeit zu linken: Die DLL wird explizit mit dem Befehl „LoadLibrary” geladen. Die Funktionen werden mit dem Befehl „GetProcAddress“ gesucht und können über einen „Pointer to function“ ausgeführt werden. ……….. typedef const char* (*LPFN_GIMME_A_STRING)(void); // declare type pointer to function ……….. HINSTANCE hDLL; // handle to DLL LPFN_GIMME_A_STRING lpfn_gimme_a_string1; // declare function pointer LPFN_GIMME_A_STRING lpfn_gimme_a_string2; // declare function pointer ……….. hDLL = LoadLibrary("STR_DLL"); ............. lpfn_gimme_a_string1 = (LPFN_GIMME_A_STRING)GetProcAddress(hDLL,"gimme_a_string1"); ............. lpfn_gimme_a_string2 = (LPFN_GIMME_A_STRING)GetProcAddress(hDLL,"gimme_a_string2"); .............. printf(lpfn_gimme_a_string1()); printf(lpfn_gimme_a_string2()); ……….. Listing 4.10: Explizites Linken. 2 Wähle das Projekt der DLL aus; gehe auf dem Menü Project/Settings und dann auf der Seite C/C++ sieht man bei Preprocessor definitions die vordefinierten Makros. -9- 4.1.5 Suchpfad nach einer DLL Wenn eine DLL geladen werden soll, muss das Betriebssystem die DLL finden. Der Suchpfad der DLL ist wie folgt aufgebaut: Die DLL wird zunächst im Verzeichnis gesucht, in dem sich die ausführbare Datei befindet. Falls die Suche erfolglos war, wird in einem durch die globale Variable PATH definierten Pfad gesucht. Der Pfad kann mit dem DOS-Befehl PATH gelesen und verändert werden. Wenn die DLL nicht gefunden wird, gibt es eine Fehlermeldung vom Betriebssystem. Bei manchen DLLs, die durch das Betriebssystem selbst verwendet werden, (z.B. die Ws2_32.dll) kann das System gar nicht gestartet werden! 4.1.6 „Memory Sharing“ zwischen DLLs Der entscheidende Vorteil der DLLs ist, dass mehrere Prozesse sich ein Code-Segment teilen. Das Code-Segment wird geteilt, aber jeder Prozess besitzt sein eigenes DatenSegment; d.h. wenn n Prozesse eine DLL laden, dann gibt es n Instanzen der DLL und ein einziges Segment für den Code. Wenn das Betriebssystem eine DLL lädt, die von einem Prozess benötigt wird, dann wird eine Instanz dieser DLL am Speicherbereich des Prozesses angehängt. Dieser Bereich wird vom Betriebssystem streng eingegrenzt und von anderen Prozessen getrennt. Doch es gibt einen Mechanismus, mit dem mehrere Prozesse auf die gleichen Daten parallel zugreifen können. Das von [24] übernommene Listing 4.10 beschreibt, wie die Daten in der DLL deklariert werden müssen, damit sie nicht bei jedem Prozess neu initialisiert werden und von allen Prozessen, die diese DLL laden, geteilt werden. // Global and static member variables that are not shared. ... #pragma data_seg("SHARED") // Begin the shared data segment. // Define simple variables // Integers, char[] arrays and pointers // Do not define classes that require 'deep' copy constructors. #pragma data_seg() // End the shared data segment and default back to // the normal data segment behavior. // tell to the linker that there variables have to be shared with other processes. #pragma comment(linker, "/section:SHARED,RWS") Listing 4.11: „Memory Sharing“ zwischen DLLs. Der Zugriff auf die gemeinsamen Daten sollte mit einer „Critical Section“ synchronisiert werden. - 10 - 4.2 Architektur der Winsock2-API In diesem Abschnitt werden die neuen Konzepte, welche die Winsock2 enthält, beschrieben. Unter anderem wird hier die Unterstützung anderer Protokoll-Familien außer TCP/IP hervorgehoben. 4.2.1 Unterstützung mehrerer Protokoll-Familien Im Gegensatz zu der Winsock1.1 unterstützt die Winsock2 mehrere Protokoll-Familien und nicht nur die Protokolle der TCP/IP-Suite. Die Schnittstelle zwischen der WinsockDLL und dem unterliegenden Protokoll-Stack ist bei Winsock1.1 nur für TCP/IP geeignet. Winsock2 bietet eine einheitliche Schnittstelle (SPI) für alle „Transport Service Provider“ und „Name Service Provider“. Die Einführung eines neuen Service Providers erfordert nicht die Implementierung einer neuen Ws2_32.dll sondern nur die Anpassung an die SPI-Schnittstelle. Abb. 2 ist von der MSDN-Library übernommen und zeigt die Architektur der Winsock2 mit beiden API- und SPI-Schnittstellen. Die Winsock2-DLL ist ein Interface zwischen den Applikationen und den Service Providern. In der Winsock2 selbst sind keine Protokolle implementiert. Wenn eine Applikation ein Socket anlegen will, ruft sie die Funktion „socket“ mit dem passenden Wert des Arguments „type“ auf. SOCKET socket(int af, int type, int protocol ); Listing 4.12: Prototyp von socket. 3 Die gängigsten Socket-Typen sind TCP (type = 1) und UDP (type = 2). Die Funktion „WSCEnumProtocols“ wird dann an der SPI-Schnittstelle aufgerufen, um Informationen über die installierten Transportprotokolle zu finden. „WSCEnumProtocols“ ist mit der API-Funktion „WSAEnumProtocols“ vergleichbar. Mit „WSAEnumProtocols“ kann die Applikation selbst Informationen über die installierten Protokolle herausfinden und eventuell dynamisch ein Transportprotokoll wählen. 3 SOCKET ist ein Makro für unsigned int. Ein „Socket Descriptor“ ist also ein unsigned int. - 11 - Abb.2: Unterstützung mehrerer Protokoll-Familien durch die Winsock2. 4.2.2 Kompatibilität mit Winsock1.1 Applikationen Winsock2 unterstützt auch Applikationen, die mit Winsock1.1. entwickelt wurden. Mit einer Kompatibilitätskomponente kann jede Applikation, die mit der Winsock1.1 ausführbar ist, an die Winsock2 zur Laufzeit angepasst werden. Die Funktion „WSAStartUp“ wird beim Laden der Winsock2-DLL als erste aufgerufen. In dieser Funktion wird ausgehandelt, welche Version der Winsock-DLL die Applikation unterstützt und danach wird entschieden, ob die Winsock-Aufrufe durch die Kompatibilitätskomponente gehen sollen. - 12 - Abb.3: Unterstützung von Winsock1.1-Applikationen durch die Winsock2. 4.2.3 Befehle für die Datenkommunikation Der Datenverkehr läuft über Sockets. Die Sockets werden mit dem Befehl „socket“ oder „WSASocket“ aufgebaut und mit „closesocket“ geschlossen. Bei Server-Applikationen können mit dem Befehl „accept“ zusätzlich Sockets angelegt werden. Die Datenkommunikation selbst geschieht mit send, sendto, recv, recvfrom, WSASend, WSASendTo, WSARecv oder WSARecvFrom. Bei send, recv, sendto und recvfrom ist die Paketgröße über dem Parameter len zu finden. int send(SOCKET s, const char FAR *buf, int len, int flags); Listing 4.13: Prototyp von send. WSASend und die anderen Funktionen ermöglichen einer Applikation das Senden bzw. Empfangen einer Menge von Datenpuffern, die in einem Array gespeichert wird. Damit kann man das zweifache Kopieren der Daten vermeiden, welches bei einzelnen Aufrufen von einfachen Send- und Receive-Befehlen entstehen würde. - 13 - int WSASend( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesSent, DWORD dwFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); Listing 4.14: Prototyp von WSASend. lpBuffers ist das Array mit den Datenpuffern und lpNumberOfBytesSent ist ein Array mit den jeweiligen Größen des Puffers. Für unsere Ziele betrachten wir die gesamte Menge der Daten als ein Paket und rechnen die Summe der Puffergrößen zusammen. 4.2.4 Blockierende und nicht-blockierende Funktionen Man kann unter den Netzwerkfunktionen zwischen zwei Kategorien unterscheiden: Funktionen, die sofort beendet werden z.B. bind, getsockopt, getpeername, Funktionen, die für ihre Ausführung eine unvorhersehbare Zeit brauchen, z.B. recv. Die Funktion recv blockiert den Prozess, bis Daten aus dem anderen Rechner ankommen. Bei BSD-Sockets und früheren 16-Bit-Windows-Sockets1.1 muss eine solche blockierende Funktion wie folgt behandelt werden. Es wird in einer Schleife und in regelmäßigen Abständen geprüft, ob neue Daten im Puffer angekommen sind. Falls ja, wird recv aufgerufen und die Schleife abgebrochen. Somit wird der gesamte Prozess nicht blockiert und andere Threads können parallel zu der Schleife weiter laufen. 5. Entwicklung 5.1 Erster Ansatz: Implementierung einer neuen Winsock2-DLL Im Abschnitt 4.1 wurden einige Eigenschaften von DLLs aufgezählt. Wie schon erwähnt, kann der Code einer DLL geändert werden, ohne die Applikationen, die diese DLL benutzen, neu kompilieren zu müssen. D.h. wenn wir eine neue DLL namens Ws2_32.dll schreiben und die „alte“ Winsock2 DLL zu Ws2_32Back.dll umbenennen, dann wird bei Netzwerkanwendungen die neue DLL geladen. In diesem Ansatz wird eine neue Winsock2-DLL implementiert, welche die gleichen Funktionen exportiert wie die alte Winsock2. Diese Funktionen rufen selbst die gleichen Funktionen aus der alten Winsock2-DLL auf und können dazwischen die Funktionsaufrufe und ihre Parameter protokollieren. Abb. 4 veranschaulicht dieses Verfahren. - 14 - 4 1 Ws2_32.dll 2 I Ws2_32Back.dll II 3 Abb.4: Implementierung einer neuen Winsock2-DLL. Bsp. Die Funktion socket. (1) Eine Applikation ruft die Funktion socket aus der neuen Winsock2. Die neue Winsock2 hat die Möglichkeit in (I) Parameter und Zeitstempeln zu protokollieren. (2) Die neue Winsock2 ruft sie die Funktion socket aus der alten Winsock2. (3) Die alte Winsock2 gibt einen „Socket Descriptor“ zurück. Die neue Winsock2 kann in (II) wieder protokollieren. (4) Die neue Winsock2 liefert den „Socket Descriptor“ zu der Applikation zurück. Innerhalb der Funktion „socket“ muss die ursprüngliche „socket“-Funktion von Ws2_32Back aufgerufen werden. Die Bezeichnung der neuen Funktion ist __socket, um eine Rekursion im Code zu vermeiden. SOCKET __socket(int af, int type, int protocol){ ….. socket (int af, int type, int protocol) ….. } Listing 5.1: Implementierung von socket in der neune Winsock2-DLL. Diese Bezeichnung __socket wird in der DEF-Datei maskiert: EXPORTS …. socket = __socket …. Listing 5.2: Eintrag von socket in der DEF-Datei. Doch damit socket innerhalb von __socket aufgerufen werden kann, muss socket importiert werden und die LIB-Datei der alten Winsock2-DLL Ws2_32.lib muss mit der neuen Winsock2-DLL durch den Linker verknüpft werden. Abb. 5 stellt diese Idee dar. - 15 - Ws2_32.lib 1 2 Ws2_32.dll Ws2_32Back.dll Export-Tabelle Code accept __accept {...} bind __bind {...} ....... ....... socket __socket(...){ ... socket(...); .... } ...... Export-Tabelle accept bind ....... socket Code accept (...){....} bind (...){....} ....... socket(...){....} ...... .... .... Abb.5: Implementierung neuer Winsock2-Funktionen mit Hilfe von alten Funktionen. Beispiel: socket. (1) Die alte Funktion socket wird innerhalb des Codes der neuen Funktion __socket aufgerufen. Das Betriebssystem sucht in Ws2_32.lib, wie das Code-Segment der alten Funktion socket zu erreichen ist. (2) Die Datei Ws2_32.lib soll darauf hinweisen, dass socket in Ws2_32Back implementiert ist und einen „Pointer to function“ auf socket liefern. Allerdings weist Ws2_32.lib darauf hin, dass die Funktion socket in der Datei Ws2_32.dll ist, die jetzt Ws2_32Back.dll heißt. Abb. 6 zeigt, was bei dem Aufruf von socket innerhalb von __socket tatsächlich geschieht. Die LIB-Datei ist auch in Binärcode geschrieben, so dass eine effiziente kleine Änderung nicht möglich ist. - 16 - Ws2_32.lib 1 2 Ws2_32.dll Ws2_32Back.dll Export-Tabelle Code accept __accept {...} bind __bind {...} ....... ....... socket __socket(...){ ... socket(...); .... } ...... Export-Tabelle accept bind ....... socket Code accept (...){....} bind (...){....} ....... socket(...){....} ...... .... .... Abb.6: Implementierung neuer Winsock2-Funktionen mit Hilfe von alten Funktionen am Beispiel von socket. (1) Die alte Funktion socket wird innerhalb des Codes der neuen Funktion __socket aufgerufen. Das Betriebssystem sucht in Ws2_32.lib, wie das Code-Segment der alten Funktion socket zu erreichen ist. (2) Die Datei Ws2_32.lib weist darauf hin, dass die Funktion socket in der Datei Ws2_32.dll implementiert ist. Es ergibt sich eine Rekursion. Die Datei Ws2_32.lib ist immer erforderlich, wenn Funktionen aus einer DLL implizit importiert werden. In dem Beispiel von Explizitem Linken im Abschnitt 4.1.4 scheint der Aufruf von exportierten Funktionen umständlich zu sein. Doch hier eignet sich das explizite Linken gut. SOCKET socket( int af, int type, int protocol) { /* evtl. Parameter und Zeit Ausgabe */ ……. LPFN_SOCKET lpfn_socket = (LPFN_SOCKET)GetProcAddress(hDLL,"socket"); SOCKET rt = lpfn_socket( af, type, protocol); /* evtl. noch mal Parameter und Zeit Ausgabe */ ….... return rt; } Listing 5.3: Implementierung von socket mit Explizitem Linken. - 17 - Dazu muss die Ws2_32Back.dll in der Dll_Main der neuen DLL geladen werden. Dll_Main wird aufgerufen, wenn ein Prozess die DLL lädt und wenn der Prozess die DLL freigibt (meistens wird die DLL erst freigegeben, wenn der Prozess beendet wird). Dll_Main wird auch bei jedem Thread-Beginn und -Ende aufgerufen. Listing 5.4 zeigt den Code von Dll_Main in der neuen Winsock2 DLL. Das Argument ul_reason_for_call weist darauf hin, um welchem Fall es sich gerade handelt. BOOL APIENTRY Dll_Main( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: hDLL = LoadLibrary("Ws2_32Back"); if (!hDLL) exit(-1); /* weitere Initialisierungen */ ……... break; case DLL_THREAD_ATTACH: if(!hDLL) { hDLL = LoadLibrary("Ws2_32Back"); if (!hDLL) exit(-1); ……... } break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: FreeLibrary(hDLL); ……... break; } return TRUE; } Listing 5.4: Implementierung von Dll_Main in der neuen Winsock2-DLL. Die Funktionen werden mit einer DEF-Datei exportiert: - 18 - LIBRARY WS2_32 EXPORTS ; Here goes the explicit export declaration accept @1 bind @2 ….. socket @23 ….. WSACleanup @116 Listing 5.5: Die DEF-Datei der neuen Winsock2-DLL. Die Zahlen, die in der rechten Spalte stehen, sind so genannte „Ordinals“. Hier werden die Funktionen mit einer Ordnungszahl exportiert. In [6] wird das Exportieren von Funktionen mit einer Ordnungszahl erklärt. Aus Effizienzgründen werden auch alle „Pointer to function“ (lpfn_accept, lpfn_bind …) als globale Variablen deklariert und einmalig in der DllMain berechnet. Eine grobe Zeitmessung zeigt, dass die Berechnung aller dieser „Pointer to function“ weniger als eine Millisekunde dauert. Dadurch wird die Applikation beim Laden der DLL nur unbedeutend beeinflusst. Der Code einzelner Funktionen reduziert sich wie folgt. SOCKET socket( int af, int type, int protocol) { /* evtl. Parameter und Zeit Ausgabe */ ……. SOCKET rt = lpfn_socket( af, type, protocol); /* evtl. noch mal Parameter und Zeit Ausgabe */ ….... return rt; } Listing 5.6: Reduzierte Implementierung von socket in der neuen Winsock2-DLL. 5.1.1 Automatisches Erzeugen des Codes Es ist nicht denkbar, den Code aller Funktionen per Hand zu schreiben, denn dies beansprucht mühsame Arbeit und ist sehr fehleranfällig. Diese Vorgehensweise erschwert jede Änderung oder Erweiterbarkeit des Codes. Deshalb wird der Code mit einem Perl-Script automatisch erzeugt. Der Code der Funktionen besitzt dieselbe Struktur. Die Funktionsprototypen werden aus den Dateien winsock2.h und ws2spi.h ausgelesen und in einer Datenstruktur gespeichert. Anschließend kann der Code aus dieser Datenstruktur automatisch erzeugt werden. Einige kleine Änderungen werden zusätzlich von Hand gemacht. - 19 - 5.1.2 Implementierungsaspekte Bei der Entwicklung der Winsock2 wurden einige Aspekte festgestellt, die hier erläutert werden. Die Winsock2-DLL muss vollständig implementiert werden, bevor sie einsatzbereit ist. Selbst einfache Client- oder Server-Applikationen, die nur wenige Funktionen aus der BSD-Familie wie accept, socket, listen, send und recv anwenden, können nicht laufen, wenn die Implementierung sich auf solche Funktionen beschränkt. Es gibt andere Funktionen, die erforderlich sind, selbst wenn sie nicht explizit durch die Applikation aufgerufen werden. Die Funktion WSAStartup z.B. wird, wie im Abschnitt 4.2.1 schon erwähnt, immer als erste aufgerufen. Sie bestimmt, auf welche Version der Winsock-DLL die Applikation zugeschnitten ist, und setzt eventuell die Kompatibilitätskomponente ein. Keine weitere Funktion aus der Winsock2-DLL kann aufrufen werden, wenn WSAStartup nicht erfolgreich abgeschlossen wird (d.h. der Rückgabewert ist gleich Null). Weitere Funktionen sind erforderlich, um Sockets anzulegen. Wird socket von einer Applikation aufgerufen, so wird WSCEnumProtocols aufgerufen, um die erforderlichen Informationen über die auf dem Rechner installierten Service Provider zu finden (siehe Abschnitt 4.2.1). Der erste Service Provider, der die gewünschte Adressfamilie und den Typ des Sockets unterstützt, wird verwendet. Solange WSCEnumProtocols von der Winsock2-DLL nicht exportiert wird, können keine Sockets angelegt werden und dadurch auch keine Daten ausgetauscht werden. Der Rückgabewert von socket ist immer INVALID_SOCKET. 5.1.3 Installation Nachdem die neue Winsock2-DLL mit verschiedenen Internetanwendungen erfolgreich getestet wurde, kann sie im System installiert werden (siehe auch Abschnitt 7). Beim Systemstart werden einige Prozesse gestartet, welche die Winsock2-DLL laden. Bei Windows 2000 werden z.B. WinMgmt.exe, winlogon.exe, services.exe und weitere Programme gestartet. Die neue Winsock2-DLL wurde von diesen Prozessen fehlerfrei verwendet und das System ist erfolgreich gestartet. Dieser Schritt der Entwicklung hatte eine besondere Bedeutung. Dadurch zeigte sich, dass die neue Winsock2-DLL die gleiche Funktionalität erfüllen kann wie die ursprüngliche Winsock2-DLL. Der bisher beschriebene Ansatz hat mit unseren Anforderungen übereingestimmt. Durch seine Entwicklung wurden viele Erkenntnisse über DLLs allgemein und insbesondere über die Winsock2-DLL gewonnen. Doch wir haben uns schließlich für einen anderen Ansatz entschieden. Im nächsten Abschnitt werden der zweite Ansatz und seine Vorteile vorgestellt. - 20 - 5.2 Zweiter Ansatz: Die „Debug/Trace Version“ der Winsock2-DLL Die „Microsoft Platform SDK“ bietet eine wenig bekannte Version der Winsock2-DLL. Diese Version wird als „Debug/Trace Winsock2“ bezeichnet. Sie ist fast auf unsere Anforderungen zugeschnitten. Sie wurde veröffentlicht mit dem Ziel, Entwicklern beim „Debugging“ ihrer Internetanwendungen zu unterstützen. Mit dieser DLL können die Entwickler die Funktionsaufrufe der Winsock2 nachvollziehen. Sie ermöglicht eine volle Kontrolle über die Parameter, mit denen diese Funktionen aufgerufen werden, und über die jeweiligen Rückgabewerte. Diese Werte können eventuell modifiziert werden. Ein Funktionsaufruf kann vorgetäuscht werden. Diese DLL ermöglicht auch die Kontrolle über die Winsock2-Funktionsaufrufe durch andere DLLs, in denen Service Provider implementiert sind. Mit anderen Worten: wir haben nicht nur die Kontrolle über die API-Schnittstelle, sondern auch über die SPISchnittstelle. Diese DLL soll Entwicklern dabei helfen, auftretende Probleme in der Applikation, in der Winsock2 oder im Service Provider zu lokalisieren 5.2.1 Architektur der „Debug/Trace Version“ der Winsock2-DLL Die „Debug/Trace Version“ der Winsock2-DLL kommuniziert mit einer zusätzlichen DLL namens Dt_DLL (Dt steht für Debug/Trace). Die Schnittstelle zwischen beiden Modulen ist durch zwei Funktionen definiert, welche von der Dt_Dll exportiert werden. Diese Funktionen heißen WSAPreApiNotify und WSAPostApiNotify. Sie werden bei der Ausführung einer Funktion aus der Winsock2-DLL am Anfang und am Ende aufgerufen. Ruft eine Applikation eine beliebige Funktion aus der Winsock2-DLL, so ruft diese Funktion selbst WSAPreApiNotify aus der Dt_DLL, bevor etwas anders ausgeführt wird. Bei diesem Aufruf werden die Parameter mitgeliefert, mit denen die Winsock2-Funktion aufgerufen wurde, sowie zusätzliche Informationen über diesen Aufruf. Wenn die Ausführung der Winsock2-Funktion beendet ist, ruft sie als letztes WSAPostApiNotify auf. WSAPreApiNotify und WSAPostApiNotify werden auch ausgeführt, wenn die Winsock2 selbst eine Funktion aus einem Service Provider aufruft. Die Aufrufe von WSAPreApiNotify und WSAPostApiNotify bleiben für die Applikation unsichtbar. Abb. 7 ist aus der Dokumentation der „Debug/Trace Version“ der Winsock2-DLL [25] übernommen und erklärt die Interaktion zwischen der Winsock2-DLL und Dt_Dll bei der Ausführung von connect. Die Funktionen WSAPreApiNotify und WSAPostApiNotify werden mit GetProcAddress gefunden. Falls sie nicht erreicht werden können, weil die Dt_Dll nicht gefunden wird oder weil die Dt_DLL diese Funktionen nicht exportiert, so läuft die Ausführung der jeweiligen Winsock2-Funktion ohne weiteres ab. - 21 - WinSock 2 DLL 8 dt_dll.dll 1 2 7 3 4 6 service provider 5 A typical execution path for WSAConnect involving the debug/trace layer is shown. (1) A client application calls WSAConnect. The WinSock 2 DLL calls the WSAPreApiNotify entry point in dt_dll.dll passing WSAConnect call information. The dt_dll.dll decodes the call info and returns. (2) The WinSock 2 DLL proceeds until it is about to call the provider. (3) The WinSock 2 DLL calls WSAPreApiNotify with WSPConnect call information. The dt_dll.dll decodes the information and returns. (4) The WinSock 2 DLL calls the service provider’s WSPConnect function, which (5) performs the connect and returns. (6) The WinSock 2 DLL calls WSAPostApiNotify with WSPConnect call information. The dt_dll.dll decodes and returns. The WinSock 2 DLL completes its processing. (7) the WinSock 2 DLL calls WSAPostApiNotify with WSAConnect call information. The dt_dll.dll decodes and returns. (8) The WinSock 2 DLL returns the final result to the application. Abb.7: Typical execution through the debug/trace layer 5.2.2 WSAPreApiNotify und WSAPostApiNotify Diese beiden Funktionen sind wie folgt definiert: - 22 - BOOL WINAPIV WSAPreApiNotify( IN INT NotificationCode, OUT LPVOID ReturnCode, IN LPSTR LibraryName, ...); BOOL WINAPIV WSAPostApiNotify( IN INT NotificationCode, OUT LPVOID ReturnCode, IN LPSTR LibraryName, ...); Listing 5.7: Die Prototypen von WSAPreApiNotify und WSAPostApiNotify. NotificationCode ist eine Integer-Zahl, welche die gerade aufgerufene Winsock2Funktion kennzeichnet. Der Code der Winsock2-API- und -SPI-Funktionen ist in der Header-Datei Dt_Dll.H definiert. Bei einer neueren Version der Winsock kann dieser Code erweitert werden. ……….. #define DTCODE_accept #define DTCODE_bind #define DTCODE_closesocket ……….. #define DTCODE_socket ……….. 1 2 3 21 Listing 5.8: Definition von NotificationCode in der Header-Datei von Dt_Dll. ReturnCode ist ein Pointer auf denjenigen Wert, der zurückgeliefert werden soll. Hier hat man die Möglichkeit bei WSAPostApiNotify diesen Wert zu lesen bzw. zu ändern. LibraryName ist der Name der verwendeten Bibliothek. Im Fall der API-Funktionen ist es die Winsock2. Im Fall der SPI-Funktionen ist es die Datei, in welcher der Service Provider implementiert ist. Schließlich kommt eine Parameterliste mit einer variablen Länge. Diese Liste enthält Pointer zu den Parametern, mit denen die Winsock2-Funktion aufgerufen wird. Die Extraktion der Parameter aus der Parameterliste wird später erklärt. Wie erwähnt, kann die Ausführung von Funktionen aus der Winsock2 den Applikationen vorgetäuscht werden. Das geschieht, indem man ReturnCode den gewünschten Rückgabewert zuweist und die Funktion WSAPreApiNotify mit der Rückgabe true beendet. - 23 - Wenn WSAPreApiNotify true zurückliefert, wird die Ausführung der Winsock2 abgebrochen und der Wert zugeliefert, der von Returnvalue adressiert wird. Sonst wird die Funktion weiter ausgeführt. Das gleiche gilt für WSAPostApiNotify. In der Implementierung von connect sieht der Aufruf von WSAPreApiNotify etwa wie folgt aus. BOOL should_skip; int connect_return; should_skip = (* lpWSAPreApiNotify) ( DTCODE_connect, // NotificationCode (LPVOID) & connect_return, // ReturnCode “winsock2.dll”, // LibraryName & s, & name, & namelen); if (should_skip) { return(connect_return); } Listing 5.9: Implementierung von connect in der „Debug/Trace Version“ der Winsock2-DLL. Somit erlaubt uns die Dt_Dll mit ihren Funktionen WSAPreApiNotify und WSAPostApiNotify volle Kontrolle über die Funktionsaufrufe an den API- und SPISchnittstellen. Rückgabewerte und Parameter können über ihre Pointer geändert werden und Funktionsaufrufe können abgelehnt werden. Jedoch möchten wir diese DLL nur verwenden, um Erkenntnisse über das Verhalten der Applikationen an der APISchnittstelle zu gewinnen und werden diese Werte nicht ändern. 5.2.3 Implementierung An Hand von NotificationCode kann man erkennen, welche Funktion gerade aufgerufen wird und dementsprechend die Parameter von der Parameterliste extrahieren. Listing 5.10 erklärt dieses Verfahren an Hand des Beispiels der Funktion socket. - 24 - BOOL WINAPIV WSAPreApiNotify(IN LibraryName, ...) { INT NotificationCode, OUT LPVOID ReturnCode, IN LPSTR *LogFile << "\n" << NotificationCode << " + "; va_list vList; // declare parameter list va_start(vList, LibraryName); // initialize parameter list switch(NotificationCode) { case DTCODE_socket: { int af = (int)*va_arg(vList, int *); // extract parameter from parameter list int type = (int)*va_arg(vList, int *); int protocol = (int)*va_arg(vList, int *); *LogFile << "t " << type; break; } /* weitere case-Anweisungen */ …… } return false; } // WSAPreApiNotify Listing 5.10: Implementierung von WSAPreApiNotify. Die Parameterliste va_list wird mit va_start initialisiert. Die einzelnen Argumente werden mit va_arg in der Reihenfolge aus der Liste extrahiert. Somit kann man alle Parameter auslesen, mit denen die Funktion socket aufgerufen wird. Hier folgt auch die Implementierung von WSAPostApiNotify für den Fall der Funktion socket. - 25 - BOOL WINAPIV WSAPostApiNotify(IN LibraryName, ...) { INT NotificationCode, OUT LPVOID ReturnCode, IN LPSTR *LogFile << "\n" << NotificationCode << " - "; va_list vList; va_start(vList, LibraryName); switch(NotificationCode) { case DTCODE_socket: { SOCKET * s = (SOCKET *)ReturnCode; *LogFile << "s " << *s; // write the socket descriptor in the log file break; } /* weitere case-Anweisungen */ …… } return false; } // WSAPostApiNotify Listing 5.11: Implementierung von WSAPostApiNotify. Hier wird der Parameter ReturnCode verwendet. Der Rückgabewert von socket ist ein „Socket Descriptor“. Dieser ist mit einem „File Descriptor“ im Dateisystem vergleichbar. Nachdem das Socket abgelegt ist, kann der „Socket Descriptor“ für die Ausführung von Send- und Receive-Befehlen und anderen Operationen verwendet werden. Wenn NotificationCode gleich DTCODE_socket ist, dann ist ReturnCode ein Pointer auf dem „Socket Descriptor“. Somit können dieser Wert gespeichert werden und nachfolgende Send- und Receive-Befehle jeweils ihren Sockets richtig zugeordnet werden. In der „Windows Sockets 2 SDK“ befindet sich ein Beispiel für die Implementierung der Dt_DLL von Jeffrey C. Diese Implementierung vermeidet die Switch-Anweisung mit einem Aufruf von LOG-Funktionen über „Pointer of functions“, die dynamisch bestimmt werden. Mit diesem Ansatz kann man eine übergroße Switch-Anweisung vermeiden, wenn die Parameter mehrerer Funktionen protokolliert werden sollen. Schließlich können WSAPre- und WSAPostApiNotify mit 175 verschiedenen Werten von NotificationCode aufgerufen werden. Doch in unserer Implementierung wurde auf dieses „elegante“ Verfahren verzichtet. Bei Jeffrey C. wird die Parameterliste va_list auf Grund ihrer variablen Länge mehrmals aufgeteilt und zusammengestellt, damit die Parameter zwischen Unterprogrammen weitergegeben werden können. Dieses Verfahren beeinflusst die Performance der Winsock2-DLL. Deshalb werden alle Fälle innerhalb von WSAPreApiNotify bzw. WSAPostApiNotify in einer Switch-Anweisung behandelt. Mit diesem „einfachen“ Programmierstil ist unser Code auch nicht unübersichtlich geworden, zumal - 26 - wir uns auf wenige Winsock2-Funktionen beschränkt haben. In [25] wird der Ansatz von Jeffrey C. genauer beschrieben. 5.3 Zusammenfassung beider Ansätze Nachdem der zweite Ansatz vorgestellt wurde, stellt man fest, dass er bedeutend einfacher ist. Die Hauptaufgabe besteht darin, die zwei Funktionen WSAPre- und WSAPostApiNotify zu implementieren. Im ersten Ansatz umfasste der Quellcode der Winsock2-DLL über 2000 automatisch erzeugte Zeilen, nur um die Funktionen weiterzuleiten, und bevor weitere Logging-Funktionalität implementiert wird. Der Umfang der Implementierung von Dt_Dll hingegen umfaßt weniger als 400 Zeilen. Mit der „Debug/Trace Version“ der Winsock2-DLL kann man auch einen Einblick in die Service Provider gewinnen, so dass das Verständnis der Funktionalität der Winsock2 gefördert wird. Außerdem ist die Dt_Dll mit einem neuen Betriebsystem einsatzbereit, sobald die passende „Debug/Trace Version“ der Winsock2-DLL zur Verfügung steht. Die Dt_Dll wurde mit Windows 2000 implementiert. Die „Debug/Trace Version“ der Winsock2 wurde aus der CD „Microsoft Platform SDK, April 2001“ übernommen. Sie wurde laut Microsoft-Angaben mit der Beta-Version von Windows XP getestet. 6. Daten erfassen und visualisieren Nachdem wir einen Eingangspunkt in die Winsock2-DLL implementiert und ein Verfahren realisiert haben, mit dem wir von den Funktionsaufrufen aus der Winsock2DLL benachrichtigt werden und auf die Parameter zugreifen können, mit denen diese Funktionen aufgerufen werden, möchten wir diese Ereignisse visualisieren bzw. in Dateien protokollieren und auswerten. Unser Ziel ist, das Verhalten der Applikation an der API-Schnittstelle zu untersuchen. Insbesondere sind einige Informationen relevant für die Analyse dieses Verhaltens, z.B. die Lebensdauer eines Sockets, die Zeitspanne zwischen zwei Send- oder ReceiveBefehlen und die Paketgröße. Eventuell können sich nachträglich weitere Informationen als relevant erweisen. 6.1 Winsock2-API-Monitor Um die Daten zu visualisieren, sind einige Möglichkeiten denkbar. Erwünscht wäre z.B. ein Monitor, der die Aufrufe von Funktionen aus der Winsock2-DLL mit ihren Parametern unmittelbar und in Echtzeit anzeigt4. Doch die Leistung der CPU wird durch die unverzögerte Ausgabe der Funktionsaufrufe mit ihren Parametern stark beeinträchtigt. Die Ausführung der Kommunikationsbefehle wird gebremst. Applikationen, die EchtzeitAnforderungen haben, wie Multimedia-Übertragung bspw. „Voice Over IP“, werden 4 Vgl. den API-Monitor von TracePlus. In der Homepage von SST Inc. wird behauptet, dass TracePlus echtzeitfähig sei. Dies ist allerdings zu bezweifeln. - 27 - nicht mehr echtzeitfähig. Außerdem werden die Messwerte der Zeitspannen nicht mehr aussagekräftig, wenn ein großer Teil der Ausführung der Kommunikationsbefehle in der Ausgabe, und nicht in der eigentlichen Ausführung des Befehls vergeht. 6.2 Performance Monitor Der Performance Monitor (Perfmon.exe) ist ein Feature von Windows NT/2000, mit dem mehrere Systemeigenschaften wie Prozessorleistung, Netzwerkverkehr, Dienste und Treiber visualisiert und analysiert werden können. Die Daten, die der Performance Monitor visualisieren kann, sind so genannte „Counter“. In der „Registry“ stehen Einträge für die existierenden Counter und jeweils eine DLL, mit der die Counter berechnet werden können. Diese DLL wird von dem Prozess PerfMon.exe geladen und muss drei Funktionen exportieren: Open, Collect und Close. Die Counter werden mit Collect in regelmäßigen Zeitabständen gelesen und auf dem Performance Monitor visualisiert. Die Größe eines Pakets beim Senden oder Empfangen kann mit dem Performance Monitor visualisiert werden, wenn sie in einem Array gespeichert wird und der Prozess PerfMon.exe diese Daten über eine DLL lesen kann. Diese DLL muss die Funktionen Open, Collect und Close exportieren. Dies ist unabhängig davon, welcher Ansatz bei dem Zugriff auf die Daten implementiert wird. Beim ersten Ansatz würde die neue Winsock2DLL zusätzlich zu den üblichen Winsock2-Funktionen die drei Funktionen Open, Collect und Close exportieren. Beim zweiten Ansatz würden diese drei Funktionen von der Dt_Dll zusätzlich zu WSAPre- und WSAPostApiNotify exportiert werden. Der Performance Monitor und die Applikation müssen sich einen gemeinsamen Speicherbereich teilen, in dem diese Daten gelesen bzw. geschrieben werden. Mit dem in Abschnitt 4.1.6 vorgestellten Verfahren „Memory Sharing“ kann sich der Performance Monitor die Daten mit einer Applikation teilen. Somit können diese Daten mit dem Performance Monitor visualisiert werden. Doch dieses Verfahren erfordert eine aufwändige Synchronisation des Zugriffs auf das gemeinsame Daten-Segment, denn die Daten werden für andere Prozesse blockiert, wenn ein Prozess auf sie zugreift. Der Performance Monitor und die jenige Applikation würden sich gegenseitig stören. Diese Synchronisation würde die Ausführung der Kommunikationsbefehle öfter blockieren. Damit ist eine aussagekräftige Visualisierung der Daten durch den Performance Monitor nicht möglich. 6.3 LOG-Datei Nachdem die Nachteile des Winsock2-API-Monitors und des Performance Monitors vorgestellt wurden, bleibt uns nur eine Möglichkeit, das Verhalten der Applikation an der API-Schnittstelle zu kontrollieren: Die Daten werden in einer Log-Datei gespeichert und sämtliche unmittelbaren Ausgaben auf dem Bildschirm vermieden. Selbst die Ausgaben in der Datei müssen möglichst gering bleiben. Hier kommt uns der NotoficationCode von - 28 - der „Debug/Trace Version“ der Winsock2-DLL zugute: Eine kleine Zahl in die Datei zu schreiben, ist viel effizienter als den ganzen Namen der Funktion zu schreiben. Die LOGDatei kann nachträglich mit einem Perl-Script dekodiert werden. 6.3.1 Ausgabe der LOG-Dateien Listing 6.1 zeigt einen Abschnitt einer LOG-Datei, die mit der Ausführung von „Internet Explorer“ erzeugt wurde. % Winsock API Log File % Module: E:\Program Files\Internet Explorer\IEXPLORE.EXE % Node: ALADIN % Initiated: 10-20-2001, 15:6:12 % Frequency 1193182 % Start 1382167807 54 + 1382169073 54 - 1382172247 21 + 1382177450 t 102 + 1382180302 102 - 1382188435 21 – 1382190986 s ...... 43 + 1395041212 s 736 92 + 1395041254 92 - 1395041318 …… 25 + 1401918166 25 - 1401918208 % Performance factor: 0.220271 % 2 736 l 32 Listing 6.1: Ausschnitt einer Winsock2-API-LOG-Datei. Die Zeilen, die mit dem Zeichen ‚%’ anfangen, geben allgemeine Informationen über den gesamten Prozess, z.B. Modul-Name, NetBIOS-Name des Knoten und die Uhrzeit, zu der Winsock2 DLL geladen wird. Beim Aufruf von WSAPre bzw. WSAPostApiNotify wird der NotificationCode der jeweiligen Funktion in die LOG-Datei geschrieben. Das Zeichen ‚+’ steht dafür, dass die Funktion aufgerufen wird, und ‚-’ steht dafür, dass sie beendet wird. Danach kommt ein Counter der Zeit in hoher Genauigkeit. Dieser Counter wird mit dem Befehl QueryPerformanceCounter hardwarenah gemessen. Dieser Befehl wird nicht von jeder Hardware unterstützt. Der Befehl QueryPerformanceFrequency gibt an, wie oft in einer Sekunde dieser Counter inkrementiert wird. Wenn z.B. diese Frequenz in der Größenordnung von 106 ist, dann ist die Genauigkeit des Counters im Bereich von Mikrosekunden. Die Frequenz wird am Anfang gemessen und in die Datei ausgegeben, damit sie später für die Übersetzung des Counters in natürliche Zeiteinheiten verwendet - 29 - wird. Der Counter Start 1382167807 gibt den Zeitpunkt an, an dem die Winsock2-DLL geladen wird. So wird später die relative Zeit am Startpunkt berechnet. Zusätzlich dazu kommt bei einigen Funktionen die Ausgabe von wichtigen Parametern. So wird z.B. beim Aufruf von socket (NotificationCode = 21) protokolliert, welchen Typ von Socket die Applikation wünscht. Die gängigsten Socket-Typen sind TCP (type = 1) und UDP (type = 2). Beim Beenden von socket wird der Rückgabewert, also der „Socket Descriptor“, gespeichert. Bei Send- und Receive-Befehlen wird die Länge des Pakets gespeichert. Damit die Syntaxanalyse der LOG-Datei später vereinfacht wird, steht vor diesen Zahlen jeweils ein Zeichen, das erklärt, welche Bedeutung diese Zahl hat. Unter anderem steht s für den Socket Descriptor, t für den Typ des Sockets und l für die Länge des Pakets. 6.3.2 Performance Factor Zum Schluss der LOG-Datei kommt der „Performance factor“: der Anteil der Ausführung der Logging-Befehle zu der gesamten Ausführung des Winsock2-Befehls in Prozent. Die Zeit zum Schreiben in die Datei und die Zeit zur Ausführung des gesamten Befehls wird bei jeder Winsock2-Operation mit dem Befehl QueryPerformanceCounter gelesen und addiert. Zum Schluss wird der durchschnittliche Anteil in Prozent ausgerechnet. Der „Performance Factor“ entspricht dem Einfluss der Protokollierung der erforderlichen Daten auf die Ausführung der Winsock2-Funktionsaufrufe. Er variiert nach Applikation und Hardware. Bei einer CPU mit 466 MHZ Taktfrequenz und 128 MB RAM bleibt der „Performance Factor“ bei Internet Explorer meistens unter 1%. Bei anderen Applikationen wie Netscape steigt er leider manchmal bis 33%. Die Daten könnten in einem statischen Array gespeichert werden und erst innerhalb eines Threads niedriger Priorität vom Array zu der Datei kopiert werden. Dies macht allerdings das Logging nicht schneller. Zeitmessungen weisen nach, dass der Zugriff auf ein statisches Array innerhalb von WSAPre- oder WSAPostApiNotify nicht schneller ist als der Zugriff auf eine Datei. Das parallele Thread würde trotz seiner niedrigen Priorität den Prozess nur langsamer machen. Die aufwändige Entwicklung eines solchen Verfahrens ist die Mühe nicht Wert. Die Daten werden mit einfachen Schreib-Befehle in die Datei geschrieben. *LogFile << NotificationCode << " + "; 6.3.3 Übersetzung der LOG-Datei Die Log-Dateien können auf zwei verschiedenen Arten mit Perl-Scripten dekodiert und interpretiert werden. Ein Script namens Ws2_API.pl übersetzt einfach jeweils den - 30 - NotificationCode in Funktionsnamen, rechnet den Zeit-Counter in Mikrosekunden um und übersetzt weitere Daten wie „Socket Descriptor“ und Paketgröße in Klartext. Mit Hilfe von Frequency und Start, die aus der LOG-Datei gelesen werden, kann die relative Zeit ab dem Laden der Dt_Dll in Mikrosekunden ausgerechnet werden. Winsock API Log File Module: E:\Program Files\Internet Explorer\IEXPLORE.EXE Node: ALADIN Initiated: 11-2-2001, 19:27:11 Performance factor: 0.0590816 % WSAStartup WSAStartup socket WSAStartup WSAStartup socket htonl htonl bind bind getsockname getsockname connect ntohs ntohs connect select gethostbyname inet_addr Called returned Called Called returned returned Called returned Called returned Called returned Called Called returned returned Called Called Called 00.000.126 00.003.997 00.006.233 00.231.857 00.232.114 00.255.904 00.255.936 00.255.960 00.255.984 00.256.291 00.256.317 00.256.406 00.256.431 00.256.470 00.256.486 00.256.579 00.256.608 00.397.361 00.587.152 Type UDP Socket 804 Socket 804 Socket 804 Socket 804 Listing 6.2: Ein Ausschnitt aus einer HTML-Datei: Die Übersetzung der Winsock2-Befehle mit dem Script Ws2_API.pl. Ein zweites Script namens Ws2_Sockets.pl kann Statistiken über jeden Socket erstellen. Es wird bei jedem Socket die Lebensdauer, die Anzahl der gesendeten und empfangenen Pakete, die maximale, minimale und durchschnittliche Paketgröße jeweils beim Senden und Empfangen ausgerechnet. - 31 - Zu jedem Socket wird eine Tabelle für die gesendeten Pakete und eine Tabelle für die empfangenen Pakete im Zusammenhang mit der Zeit erstellt. Mit diesen Tabellen können die Daten in eine Excel-Tabelle exportiert werden und damit Excel-Diagramme erstellt werden, damit der Zusammenhang zwischen Zeit und Paketgröße, und die Zeitspannen zwischen zwei Send- bzw. Receive-Befehlen graphisch analysiert werden können. Sockets Statistics: Socket Type Allocated at Duration 804 UDP 00.255.904 N.A. 956 TCP 00.715.513 11.108.808 1184 TCP 11.696.324 08.090.950 1192 TCP 11.698.564 N.A. 1204 TCP 11.701.841 N.A. 980 TCP 11.826.042 N.A. Packets Action Number of Packets Min size Max size Avg. size Send 26 1 1 1 Received 26 32 32 32 Send 414 474 444 Received 18 1 8192 3544 Send 422 502 450 Received 28 1 8192 2853 Send 422 433 427 Received 16 1 1289 578 Send 422 429 425 Received 10 1 1024 488 Send 421 427 424 1 1024 455 2 3 3 2 2 Received 9 Listing 6.3: Ein Ausschnitt aus einer HTML-Datei: Die Berechnung der Sockets-Statistiken mit dem Script Ws2_Sockets.pl. Winsock2 1400 1200 Byte 1000 800 600 400 200 0 0 5000000 10000000 15000000 20000000 25000000 30000000 Zeit (m ikrosek.) Abb.8: Excel-Diagramm der empfangenen Paketgröße im Zusammenhang mit der Zeit auf einem Socket. - 32 - 7. Installation In der Entwicklungsphase ist es schwer, die System-Winsock2-DLL zu ersetzen. Die Winsock2-DLL befindet sich in dem Verzeichnis WINNT\System32\ und wird schon beim Systemstart geladen. Versucht man diese Datei zu verschieben, zu löschen oder umzubenennen, bekommt man eine Fehlermeldung: „Diese Datei wird von Windows benutzt“. Bei einem System, auf dem mehr als ein Betriebsystem installiert ist, kann man diese Datei manipulieren, solange ein anderes Betriebsystem läuft. Wenn die zu ersetzende DLL auf einer NTFS-Partition installiert ist, ist diese Partition allerdings von anderen Betriebssystemen nicht sichtbar. Es gibt die Möglichkeit, dem Betriebssystem über eine Shell oder ein Programm zu benachrichtigen, dass bestimmte System-Dateien bei einem neuen Start ersetzt werden sollen5. Dies wäre eine Lösung für Rechner mit einem einzigen Betriebssystem oder für NTFS-Partitionen. Diese Möglichkeit wurde allerdings in dieser Arbeit nicht behandelt, weil die Entwicklung auf einem Rechner mit zwei Betriebssystemen lief. Jedoch besteht während der Entwicklungsphase ein Problem: Wenn die Winsock2-DLL oder die Dt_DLL fehlerhaft ist, kann das System nicht starten. Man hat keine Möglichkeit, Fehler zu suchen und den Code zu „debuggen“. Man könnte einen Nachbarrechner über eine serielle Schnittstelle anschließen und auf dem zweiten Rechner einen Debugger starten. Das wäre aber ziemlich umständlich und würde die Entwicklung verlangsamen. Hier wird ein einfacher Trick verwendet. Wenn eine DLL während eines Prozesses geladen werden soll, sucht das Betriebsystem nach der DLL zunächst im selben Verzeichnis, in dem sich die ausführbare Datei befindet. Erst wenn die DLL in diesem Verzeichnis nicht existiert, wird über den globalen Pfad gesucht. Wir können die „Debug/Trace Version“ der Winsock2-DLL und die Dt_Dll in dieses Verzeichnis kopieren und mit einzelnen Applikationen testen. Dieses Verfahren war sehr effizient bei der Entwicklung. So wurde die Dt_Dll entwickelt und mit verschiedenen Internetanwendungen erfolgreich getestet, vor allem mit dem Internet Explorer, NetMeeting, Netscape, Telnet und „Secure Shell Client“. 8. Konfigurationsprogramm Wenn die Ws2_32.dll und Dt_Dll installiert sind, fehlen noch einige Eingaben, die das Protokollieren der Winsock-Aufrufe steuern. 5 Es gibt viele Programme, die die Winsock2-DLL laden. Wenn das Protokollieren der Winsock2-Aufrufe für alle Programme gelten soll, bekommt man sehr schnell Vergleiche die Installation von Service Pack von Windows. - 33 - eine große Anzahl von Dateien, die gar nicht interessieren. Daher ist es sinnvoll zu bestimmen, bei welchen Internetanwendungen protokolliert werden soll. Die Aufrufe der Winsock-Funktionen sind nicht alle relevant für die Analyse der Anforderungen der Applikation an der API-Schnittstelle. Das Protokollieren der SPI-Aufrufe ist z.B. interessant, um Erkenntnisse über die SPI-Schnittstelle zu gewinnen. Doch für unsere Ziele ist es nicht relevant. Einige API-Funktionen wie gethostbyname oder gethostbyaddr können auch keine Aussagen über das Verhalten der Applikation bringen. Am meisten interessant sind diejenigen Befehle, die den Zustand eines Sockets ändern oder die Befehle für den Datenverkehr: Send und Receive. Für diese Einstellungen ist ein Programm mit einer Benutzeroberfläche entwickelt worden. Dieses Konfigurationsprogramm schreibt die Einstellungen in die „RegistryDatenbank“. Wenn ein Prozess die Winsock2-DLL lädt, dann wird die Dt_Dll ebenfalls geladen. In der Dll_Main von Dt_Dll werden die Einträge aus der „Registry“ gelesen. Beim erstmaligen Starten des Konfigurationsprogramms, wird ein neuer Schlüssel in die „Registry“ unter HKEY_LOCAL_MACHINE\ SYSTEM\CurrentControlSet\Services\WinSock2\Parameter\ namens Dt_Parameter eingefügt. In diesem Schlüssel sind drei Unterschlüssel namens: „Filter“, „LogDir“ und „Modules“. Der Schlüssel „Modules“ bestimmt, bei welchen Programmen protokolliert wird. Der Schlüssel „Filter“ legt fest, welche Funktionen protokolliert werden. Der Schlüssel „LogDir“ bestimmt, in welchem Verzeichnis die LOG-Datei erzeugt wird, wenn das Programm in der Modul-Liste steht. Abb. 8 zeigt die Benutzeroberfläche des Konfigurationsprogramms. Die untersten Schaltflächen „API View“ und „Socket Statistics“ ermöglichen die Auswahl einer LOGDatei und die direkte Ausführung der Perl-Scripte Ws2_API.pl bzw. Ws2_Sockets.pl, sobald sich diese in dem gleichen Verzeichnis wie die LOG-Datei befinden. Man kann auch die Einstellungen direkt in die „Registry-Datenbank“ mit dem Programm „regedit.exe“ eingeben und die Perl-Scripte in einem DOS-Fenster ausführen. Allerdings werden mit dem Konfigurationsprogramm diese Operationen viel praktikabler. - 34 - Abb.9: Konfigurationsprogramm der Winsock2 Startet man ein Programm, welches auf der Modulliste steht, wird noch nachgefragt, ob die Winsock2-API-Aufrufe geloggt werden sollen (Abb. 9). Diese zusätzliche Abfrage wird in der Dll_Main der Dt_DLL ausgeführt und ist zugleich ein Nachweis dafür, dass die Dt_DLL geladen wurde. - 35 - Abb.10: Zusätzliche Abfrage, ob die Winsock2-API-Aufrufe geloggt werden sollen. Achtung: Man darf auf keinen Fall, Programme in die Modulliste einfügen, die beim Systemstart schon gestartet werden wie z.B. WinMgmt.exe, winlogon.exe, services.exe. Beim Systemstart kann diese Abfrage vom Benutzer nicht verarbeitet werden. Das System kann nicht starten. Diese Abfrage läuft im Code über den Befehl „MessageBox“, der in User32.dll definiert ist. Für Programme, die beim Systemstart geladen werden und andere Programme, die über keine Benutzeroberfläche verfügen, könnte man in dem Code überprüfen, ob die User32.dll schon geladen ist. Damit könnte man ein solches Problem vermeiden. Dieses Verfahren wurde aber in dieser Arbeit nicht realisiert. 9. Zusammenfassung und Ausblick In dieser Arbeit wurde ein Tool entwickelt, das die Analyse des Verhaltens der Applikationen an der API-Schnittstelle ermöglicht. Das Tool erfasst die Aufrufe von Netzwerkfunktionen und ihre Parameter. Zuerst wurde mit TracePlus ein kommerzielles Tool vorgestellt, das diese Aufgabe erfüllt. Die Vor- und Nachteile von TracePlus wurden beschrieben. Danach wurden einige Grundlagen über DLLs und die Winsock2-API vorgestellt. Die Unterstützung mehrerer Protokollfamilien wurde hervorgehoben. Die Kompatibilitätskomponente wurde vorgestellt. Die Entwicklung des Tools selbst gliederte sich in zwei große Schritte: Die Erfassung der Funktionsaufrufe der Winsock2-API und ihrer Parameter. 1. In einem Verfahren wurde eine neue Winsock2-DLL implementiert. 2. Im anderen Verfahren wurde die „Debug/Trace Version“ der Winsock2-DLL eingesetzt. Letztendlich wurde das zweite Verfahren ausgewählt. Die Visualisierung der Funktionsaufrufe und ihrer Parameter. Hier wurden drei Verfahren diskutiert: 1. Anzeigen der Funktionsaufrufe auf einen Monitor - 36 - 2. Implementierung einer Schnittstelle zwischen der Winsock2-DLL und dem Performance Monitor (PerfMon.exe) 3. Protokollieren der Daten in einer LOG-Datei und Visualisierung in ExcelDiagrammen. Der Umfang dieser Arbeit war nicht ausreichend für eine ausführliche Auswertung der Ergebnisse. Jedoch werden hier einige Feststellungen erläutert, die weitere Arbeiten in diese Richtung motivieren sollen. Es wurde eine Nichtübereinstimmung mit den Ergebnissen von TracePlus festgestellt. Unser Programm zeigt öfter Funktionen auf, die durch die Applikation gar nicht aufgerufen werden. Einige „Open Source“-Anwendungen bestätigen das. Wie schon im Abschnitt 4.2.2 erwähnt, werden z.B. WSAStartUp und WSCEnumProtocols aufgerufen, selbst wenn die Applikation das nicht tut. Außerdem werden alle Aufrufe von recv durch WSARecv ersetzt. In Winsock2 wird WSARecv automatisch aufgerufen, wenn neue Daten ankommen. Damit wird gesichert, dass der Prozess durch den Aufruf von recv nicht blockiert wird. Möglicherweise werden diese zusätzlichen Aufrufe bzw. die Konvertierung von einigen Aufrufen teilweise durch die Kompatibilitätskomponente der Winsock2 abgewickelt. Eventuell werden sie auch durch eine weitere DLL des Betriebssystems durchgeführt. TracePlus beschränkt sich auf die Aufrufe der Applikation selbst, weil es einzelne Prozesse startet und wahrscheinlich in der Lage ist, die Winsock2-Aufrufe aus der ausführbaren Datei einzufangen. - 37 - Literaturverzeichnis [1] MSDN Library: Platform SDK Documentation; DLLs Processes, and Threads; Dynamic Link Libraries; About Dynamic Link Libraries [2] MSDN Library: Platform SDK Documentation; DLLs Processes, and Threads; Dynamic Link Libraries; Advantages of Dynamic Linking [3] MSDN Library: Visual C++ Documentation; DLL Tasks; Link Implicitly [4] MSDN Library: Visual C++ Documentation; DLL Tasks; Link Explicitly [5] MSDN Library: Visual C++ Documentation; DLL Tasks; Export from a DLL using a .DEF files [6] MSDN Library: Visual C++ Documentation; DLL Tasks; Export from a DLL by ordinal rather by name [7] MSDN Library: Platform SDK Documentation; Networking and Directory Services; Windows Sockets Version 2; Windows Sockets Version 2 API; New Concepts, Additions, and Changes for Windows Sockets 2, Windows Sockets 2 Architecture [8] MSDN Library: Platform SDK Documentation; Networking and Directory Services; Windows Sockets Version 2; Windows Sockets Version 2 API; New Concepts, Additions, and Changes for Windows Sockets 2; Windows Sockets 2 Architecture; Simultaneous Access to Multiple Transport Protocols [9] MSDN Library: Platform SDK Documentation; Networking and Directory Services; Windows Sockets Version 2; Windows Sockets Version 2 API; New Concepts, Additions, and Changes for Windows Sockets 2; Windows Sockets 2 Architecture; Backward Compatibility for Windows Sockets 1.1 Applications [10] MSDN Library: Platform SDK Documentation; Networking and Directory Services; Windows Sockets Version 2; Windows Sockets Version 2 API; New Concepts, Additions, and Changes for Windows Sockets 2; Overlapped I/O and Event Objects [11] MSDN Library: Platform SDK Documentation; Networking and Directory Services; Windows Sockets Version 2; Windows Sockets Version 2 API; New Concepts, Additions, and Changes for Windows Sockets 2; Making Transport Protocols Available to Windows Sockets; Debug and Trace Facilities [12] MSDN Library: Platform SDK Documentation; Networking and Directory Services; Windows Sockets Version 2; Windows Sockets Version 2 API; Windows Sockets Programming Considerations; Windows Socket 1.1 Blocking Routines and EINPROGRESS [13] MSDN Library: Platform SDK Documentation; Networking and Directory Services; Windows Sockets Version 2; Windows Sockets Version 2 API; References; Functions [14] MSDN Library: Platform SDK Documentation; Networking and Directory Services; Windows Sockets Version 2; Windows Sockets Version 2 API; References; Structures [15] MSDN Library: Platform SDK Documentation; Networking and Directory Services; Windows Sockets Version 2; Windows Sockets Version 2 SPI [16] MSDN Library: Platform SDK Documentation; Bases Services; Performance Monitoring; Performance Monitoring Architecture - 38 - Abb.4: Implementier ung einer neuen Winsock2DLL. Bsp. Die Funktion socket. (1) Eine Applikation ruft die Funktion socket aus der neuen Winsock2. Die neue Winsock2 hat die Möglichkeit in (I) Parameter und Zeitstempeln zu protokollieren . (2) Die neue Winsock2 ruft sie die Funktion socket aus der alten Winsock2. (3) Die alte Winsock2 gibt einen „Socket Descriptor“ z urück. Die neue Winsock2 kann in (II) wieder protokollieren . (4) Die neue Winsock2 liefert den „Socket Descriptor“ z u der Applikation zurück. [17] MSDN Library: Periodicals; Custom Performance Monitoring for Your Windows NT Applications [18] MSDN Library: Platform SDK Documentation; Bases Services; Windows System Information, Registry Functions [19] MSDN Library: Platform SDK Documentation; Bases Services; Windows System Information, Registry Structures [20] MSDN Library: Technical Articles; Microsoft Office and Visio; Microsoft Office; Automating Microsoft Office 97 and Microsoft Office 2000 [21] “Visual C++ in 21 Tagen” http://www.mut.com/media/buecher/VCPLUS6/data/start.htm [22] “Technical Work Forums For Computer Professionals” http://www.tek-tips.com/ [23] “SST Homepage” http://www.sstinc.com [24] “How to share a data segment in a DLL” http://www.codeproject.com/dll/data_seg_share.asp [25] “WinSock 2 Debug and Trace Facilities”: SDK Platform CD; \Samples\NetDs\WinSock\Dt_Dll\DbgSpec.doc [26] Kurzfassung des Projektantrags DANCE; AG Müller. - 39 -