Vorheriges Kapitel: ActiveX Steuerelemente       Inhaltsverzeichnis Inhaltsverzeichnis       Die Downloadseite Download       Kontakt Kontakt

 

Computernetzwerke

 

 

Netztopologien
Router
Protokolle
Internetadressen
Windows Sockets
Windows Sockets und Die MFC
MFC Mechanismus für Netzwerkkommunikation
Das Beispielprogramm zu Windows Sockets
ISAPI Servererweiterungen(ISAs)
ISAPI und die MFC
Das Beispielprogramm zu ISAs

 

Wenn man zwei Computer miteinander verbindet, entsteht schon ein Netzwerk. Diese Verbindung findet über Kabel und eventuell zusätzlicher Hardware statt. Im Computer selbst, muß sich eine sogenannte Netzwerkkarte befinden, die an das Kabel angeschlossen wird. In einem Netzwerk tauschen die Rechner Daten, bzw. Bits um. Für die Realisierung und Koordinierung des Datentransportes wird natürlich Software benötigt. Stellen wir uns den einfachsten Fall vor: Zwei Rechner sind über eine einzige Leitung miteinander verbunden. Beim Datentransport zwischen den beiden Rechnern fällt sofort ein Problem auf. Was ist, wenn beide Computer gleichzeitig Daten senden wollen? Man kann ja nicht nur einem Rechner das Recht gewähren, alle Daten auf einmal zu senden, das kann ja unter Umständen ewig lange dauern. Eine gerechtere Lösung wäre es, Daten beider Rechner in kleinen Paketen zu verteilen, und diese abwechselnd auf die Leitung zu schicken. Eine derartige Methode bildet in der Tat die Grundlage des Datentransportes in vielen Computernetzen. Daher sagt man auch, daß der Datentransport in diesen Netzen paketorientiert sei. Nun stellen wir uns vor, daß unser Netzwerk nicht aus zwei, sondern aus drei Rechnern besteht, die über ein einzige Leitung miteinander verknüpft sind. Nehmen wir an, Rechner eins möchte seine Daten an Rechner drei schicken. Dazwischen liegt aber Rechner zwei. Also, damit Rechner drei auch tatsächlich die Datenpakete von Rechner eins bekommen kann, müssen die Datenpakete eine eindeutige Zieladresse haben. Zu einem sicheren und zuverlässigen Datentransport gehört es, daß Rechner eins von Rechner drei auch die Zusicherung bekommt, daß die Daten tatsächlich bei ihm angekommen sind. Damit Rechner drei weiß, von wem er das Datenpaket bekommen hat, muß das Datenpaket auch die eigene Quelladresse bei sich tragen.

In großen Netzwerkverbunden wie dem Internet können aufeinanderfolgende Datenpakete auf sehr unterschiedliche Wege an die Zieladresse gelangen. Damit der Empfängerrechner die Rheinfolge der ankommenden Datenpakete zurückverfolgen kann, tragen die Datenpakete unter anderem auch ihre laufenden Seriennummer bzw. Sequenznummer mit sich.

 

 

Es gibt unterschiedliche Möglichkeiten, Computer miteinander zu verkabeln. Die Art, wie man die Computer miteinander vernetzt, bezeichnet man Netzwerktopologie. Die wichtigsten drei Netzwerktopologien sind Bustopologie, Ringtopologie und Sterntopologie. Im folgenden werden diese drei Topologien etwas näher erläutert:

Bustopologie:

Bei der Bustopologie werden die Rechner untereinander so verkabelt, daß die beiden Enden der Leitung frei bleiben. Diese sozusagen offenen Enden werden Mit zwei Terminatoren(Abschlußwiderstände) abgeschlossen. Die Leitung besteht normalerweise aus Koaxialkabel. Die Störanfälligkeit ist natürlich in einer derartigen Struktur groß. Der Ausfall des Kabels führt zur Unterbrechung der Kommunikation innerhalb des Netzwerks.

Ringtopologie:

Wie der Name schon sagt, werden bei einer Ringtopologie alle Rechner des Netzwerkes in Form eines Ringes untereinander verkabelt. Im Gegensatz zur Bustopologie liegen die Enden des Kabels hier nicht frei. Die Daten fließen hier nur in einer Richtung. In Sachen Störanfälligkeit besteht hier der gleiche Nachteil, wie bei einer Bustopologie. Ein Ausfall des Kabels kann zur einer Unterbrechung der Kommunikation zwischen zwei Rechnern führen.

Sterntopologie:

Bei einer Sterntopologie sind die Computer über einen zentralen Rechner, den sogenannten Hub, miteinander vernetzt. Der Hub ist also eine Art zentrale Vermittlungsstelle im Netz. Der Vorteil der Sterntopologie gegenüber der Bustopologie und Ringtopologie liegt in ihrer Ausfallsicherheit. Wenn ein Kabel zwischen dem Hub und einem Rechner defekt sein sollte, dann funktioniert trotzdem die Kommunikation zwischen dem Hub und den übrigen Rechnern. Fällt der Hub aus, dann ist natürlich das gesamte Netzwerk lahmgelegt. Der erhöhte Kostenaufwand für den Aufbau des Netzwerks geht zu Lasten der Sterntopologie.

 

 

Genauso wie man einzelne Computer zu einem Netzwerk untereinander verbinden kann, so hat man auch einzelne Netzwerke zu großen Netzwerkverbunden , wie dem Internet untereinander vernetzt. Die Verbindung zwischen zwei Netzwerken wird über Routern hergestellt. Ein Router ist ein Rechner, der zwei Netzwerkkarten besitzt. Die eine Netzwerkkarte ist an einem Netzwerk und die zweite an dem anderem Netzwerk angeschlossen. Die Verbindung zwischen zwei Netzwerken kann auch über die Telefonleitung und Modem hergestellt werden. So gesehen, kann man einen Router als Bindeglied zwischen zwei Netzwerken definieren. Das Internet besteht heutzutage aus mehr als 20000 einzelnen Netzwerken. Bis die Datenpakete im Internet von der Quelle A zum Ziel B gelangen, können sie leicht eine Weltreise durch die Zahlreichen Routern auf der Welt unternommen haben. Die aufeinanderfolgenden Datenpakete eines zusammenhängenden Dokumentes können auf ihrer Durchreise sehr unterschiedliche Wege genommen haben.

 

 

Die Vernetzung der Rechner dient dem Zweck der Kommunikation. Nachdem man zwischen den Rechnern hardwaremäßig eine physikalische Verbindung mit Kabeln usw. hergestellt hat, muß die Software für die eigentliche Kommunikation sorgen. Um die Bedeutung und Funktion der Protokolle zu verdeutlichen, kann man ein Beispiel aus der Gesellschaft bringen. Damit eine reibungslose Kommunikation zwischen den einzelnen Menschen in der Gesellschaft stattfinden kann, muß sich jeder einzelne Person bestimmten Benimmregeln unterwerfen. In der Komputerwelt bilden die Protokolle diese Benimmregeln für die Rechner. Es gibt im Internet mehr als 100 TCP/IP Protokolle, die unter "TCP/IP-Suite" bezeichnet werden. Diese Protokolle werden z.B. für den Verbindungsaufbau, Datenübertragung, Kommunikationsüberwachung usw. benutzt. Es gibt verbindungsorientierte und verbindungsfreie Protokolle. Bei einem verbindungsorientierten Protokoll, werden zwei Computer so untereinander verbunden, daß sie miteinander gegenseitig kommunizieren können. Bei einem verbindungsfreien Protokoll entsteht keine individuelle Verbindung zum Zielrechner. Der Absender schickt seine Daten in der Hoffnung, daß sie auch beim Empfänger ankommen werden. Aber es gibt keine Garantie dafür.

TCP/IP Protokolle:

TCP/IP besteht aus mehr als 100 Protokollen. Die wichtigsten drei sind jedoch das TCP, IP und UDP. Das TCP(Transmission Control Protocol) ist ein verbindungsorientiertes Protokoll. Die Datenübertragung ist mit TCP sicherer. IP(Internet Protocol) und UDP(User Datagram Protocol) sind verbindungsfreie Protokolle. Als Visual C++ Programmierer muß man die Details über die einzelnen Protokolle nicht wissen. Falls Sie sich doch hierfür wirklich interessieren, bleibt Ihnen der Gang in die Bibliothek nicht erspart.

 

 

Eine Adresse im Internet wird als IP-Adresse bezeichnet. Eine Netzwerkkarte in einem Computer wird mit einer IP-Adresse verbunden. Eine IP-Adresse ist eine vier Byte große Zahl. Eine typische IP-Adresse kann wie folgt aussehen:

192.224.144.6. Die IP-Adresse liegt eigentlich in binärer Form vor. Um sich die Zahlenkombination besser merken zu können, hat man jedes Byte in die entsprechende Dezimal Zahl umgewandelt und mit einem Punkt auseinander getrennt. Diese Darstellungsart heißt Quadrupel Format.

Jede IP-Adresse muß weltweit eindeutig sein. Daher liegt es nahe, daß es ein zentrales Organ geben muß, das solche IP-Adressen vergibt. Die Organisation, die hierfür zuständig ist, heißt (NIC). NIC steht für Network Information Center. Nun werden Sie fragen, ob NIC tatsächlich für jeden Computer im Netz eine IP-Adresse selbst vergibt? Die Anwort ist nein. Die IP-Adresse besteht aus einem Netzwerknummernteil und einem Hostnummernteil. NIC vergibt lediglich den Netzwerknummernteil, der weltweit eindeutig ist. Die Hostnummer innerhalb eines Netzwerks werden z.B. vom Netzwerkadministrator an einzelne Computer vergeben. Das Verhältnis zwischen dem Netzwerknummernteil und Hostnummernteil ist in allen IP-Adressen nicht gleich. Der Netzwerknummernteil, der von NIC vergeben wurde, kann in einer IP-Adresse, bis zu drei Byte lang sein. Tatsächlich wird ein Netzwerk nach dem Anteil des Netzwerknummernteils an der gesamten IP-Adresse klassifiziert. Der Netzwerknummernteil in einem Class A Netz ist ein Byte, der Hostnummernteil drei Byte. In einem Class C Netz ist der Netzwerknummernteil drei Byte und der Hostnummernteil nur ein Byte. Hier können in einem Netzwerk höchstens 256 Hostnummern vergeben werden. Class A und Class B Netze, die größere Hostnummer zur Verfügung stellen können, sind praktisch längst vergeben. Heutzutage werden nur noch Class C Netze vergeben.

 

 

 

Windows Sockets

 

Sie wissen bereits, daß Sie für die Kommunikation mit einem anderen Rechner im Netz Datenpakete zum Zielrechner schicken müssen. Diese Datenpakete werden natürlich von einem Programm im Zielrechner empfangen und verarbeitet. Damit ein Datenpaket zum richtigen Programm in den Zielrechner gelangen kann, muß es außer der IP-Adresse des Zielrechners noch eine weitere Adresse kennen, die das gewünschte Programm auf dem Zielrechner identifiziert. Das ist auch tatsächlich der Fall. Die besagte Adresse wird als Portnummer des Programms bezeichnet. Programme kommunizieren untereinander im Netzwerk über Sockets. Ein Socket ist nichts anderes als IP-Adresse des Zielrechners + Portnummer des Programms, mit dem auf dem Zielrechner kommuniziert werden soll. Man kann ein Socket auch als ein Endpunkt oder Bindeglied in der Netzwerkkommunikation bezeichnen. Im Gegensatz zum IP-Protokoll kennen TCP und UDP die Portnummer eines Programms.

 

 

 

Für den Umgang mit Sockets stellt uns die MFC zwei Klassen zur Verfügung, nämlich CAsyncSocket und CSocket. Zuerst werden im folgenden die drei MFC Klassen, die beim Umgang mit Sockets eine wichtige Rolle spielen, etwas näher erläutert:

Die MFC Klasse CAsyncSocket:

Diese Klasse ist wie die meisten MFC Klassen von CObject abgeleitet worden. Ein Objekt dieser Klasse stellt ein Windows Socket dar. Wer seine eigenen Socket Klassen von CAsyncSocket ableitet, muß einen tiefen Einblick in Netzwerkkommunikation haben. Die Abwicklung vieler Details, die in Verbindung mit Netzwerkkommunikation eine Rolle spielen, muß vom Programmierer selbst bewältigt werden. Daher ist es immer ratsam, die eigenen Socketklassen von der MFC Klasse CSocket abzuleiten.

Die MFC Klasse CSocket:

CSocket ist von der MFC Klasse CAsyncSocket abgeleitet worden. Diese Klasse bietet einen hohen Maß an Abstraktion und ist am besten geeignet für Leute, die nicht unbedingt Experte in Sachen Netzwerkkommunikation sind. Außer den üblichen Member-Funktionen bietet die Klasse CSocket eine Reihe von Callbackfunktionen, von denen die Beiden Funktionen OnAccept und OnReceive sehr wichtig sind. Auf diese Callbackfunktionen werden wir später in unserem Beispielprogramm regler zurückgreifen, und sie dort auch erläutern.

Die MFC Klasse CSocketFile:

CSocketFile ist von der MFC Klasse CFile abgeleitet worden. Ein Objekt der Klasse CSocketFile ist wiederum ein CFile-Objekt, welches zum Senden und Empfangen von Daten über Sockets in einem Netzwerk benutzt wird.

 

 

Die Klasse CSocket verwendet eine Version des MFC Serialisierungsprotokolls, das die Daten eines CSocket-Objektes über ein CArchive-Objekt in beiden Richtungen transportieren kann. In einer Netzwerkkommunikation über Sockets sollten Sie wie folgt vorgehen:

1. Erzeugen Sie zuerst ein CSocket Objekt, ein CSocketFile-Objekt und zwei CArchive Objekte.

2. Ordnen Sie dem CSocketFile -Objekt das CSocket-Objekt.

3. Jeweils ein CArchive-Objekt für das Speichern(Senden) von Daten und Laden(Empfangen) von Daten dem CSocketFile-Objekt zuordnen.

4. Am Ende der Sitzung, die erzeugten Objekte wieder löschen.

Sie haben bereits den Mechanismus der Serialisierung in der Dokument-Ansicht Architektur kennengelernt. Bei der Netzwerkkommunikation können gewisse Analogien hierzu hergestellt werden. In der Doc-View Architektur werden die zu speichernden Daten zuerst ins Archive gelagert und mit Hilfe eines CFile-Objektes auf den Datenträger gespeichert. In der Netzwerkkommunikation überträgt das Archive die Daten mit Hilfe eines CSocketFile-Objektes auf ein Socket. (Datenträger ist hier also keine Festplatte, sondern das Socket). Es ist noch zu erwähnen, daß ein CArchive-Objekt einen Puffer verwaltet. Das Leeren des Puffers ist dem Senden der Daten gleichzusetzen und das Auffüllen des Puffers ist mit dem Empfangen der Daten zu assoziieren.

 

 

Das Beispielprogramm zu dem Thema Windows Sockets heißt regler. Dieses Programm kann sowohl als Server, als auch als Client gestartet werden. Wenn Sie in einem Netzwerk arbeiten, starten Sie dieses Programm auf einen Rechner als Server und auf einem anderen Rechner im Netzwerk als Client und tragen Sie in dem Dialog des Clients die IP-Adresse des Servers ein. Wenn Sie nun den Schieberegler auf einen Rechner bewegen, bewegt sich der Schieberegler in dem anderen Rechner auch entsprechend und der momentane Wert des Reglers aktualisiert sich. Falls Sie nicht über ein Netzwerk verfügen, können Sie das Programm trotzdem testen. Sie müssen das Programm zwei mal auf Ihren Rechner starten. Es muß einmal als Server und einmal als Client gestartet werden. Es gibt eine feste IP-Adresse, mit der Sie sozusagen ein Netzwerk simulieren können. Diese IP-Adresse lautet: (127.0.0.1).


Abbildung

Abbildung

Nun zum visuellen Aufbau des Programms: erzeugen Sie bitte mit Hilfe des AppWizards ein neues SDI Projekt namens regler. Im Schritt 4 des AppWizards markieren Sie die Option Windows Sockets und leiten Sie im Schritt 6 die Ansicht von CFormView ab. Positionieren Sie bitte im Resource-Editor einen Slider im Dialog IDD_REGLER_FORM. Nun brauchen Sie ein weiteres Dialog, das beim Start des Programms aufgerufen wird, um festzulegen ob das Programm als Server oder Client laufen soll. Positionieren Sie bitte auf dieses Dialog zwei Checkbuttons für den Server- oder Clientbetrieb und eine Editbox für die IP-Adresse. Benennen Sie bitte die Klasse des Dialoges als DlgSetup. Um das Projekt einigermaßen vernünftig gestalten zu können, müssen wir drei eigene Klassen dem Projekt hinzufügen. Die beiden ersten Klassen CListeningSocket und CClientSocket werden von der MFC Klasse CSocket abgeleitet und die Klasse CMsg wird von CObject abgeleitet. Fangen wir mit der Klasse CListeningSocket an. Wie man eine eigene Klasse einem Projekt hinzufügt, haben Sie bereits im Kapitel Dokument und Ansicht kennengelernt. Es handelt sich hier um fortgeschrittene Programmierung und aus diesem Grunde wird nur das erläutert, was unbedingt nötig ist. Die Headerdatei der Klasse CListeningSocket sieht wie folgt aus:

 

class CReglerDoc;
class CListeningSocket : public CSocket
{
	DECLARE_DYNAMIC(CListeningSocket)
private:
	CListeningSocket(const CListeningSocket& rSrc); // no implementation
	void operator=(const CListeningSocket& rSrc); // no implementation
// Construction
public:
	CListeningSocket(CReglerDoc* pDoc);
// Attributes
public:
	CReglerDoc* m_pDoc;
	
public:
	// Vom Klassen-Assistenten generierte virtuelle Funktionsüberschreibungen
	//{{AFX_VIRTUAL(CListeningSocket)
	public:
	virtual void OnAccept(int nErrorCode);
	//}}AFX_VIRTUAL
	// Generierte Nachrichtenzuordnungsfunktionen
	//{{AFX_MSG(CListeningSocket)
		
	//}}AFX_MSG
// Implementierung
public:
	virtual ~CListeningSocket();
#ifdef _DEBUG
	virtual void AssertValid() const;
	virtual void Dump(CDumpContext& dc) const;
#endif
 
};

Die Implementierung der Klasse CListeningSocket sieht wie folgt aus:

#include "stdafx.h"
#include "regler.h"
#include "Lstnsock.h"
#include "reglerdoc.h"
#include "msg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CListeningSocket
IMPLEMENT_DYNAMIC(CListeningSocket, CSocket)
CListeningSocket::CListeningSocket(CReglerDoc* pDoc)
{
	m_pDoc = pDoc;
}
// CSocket Implementation
CListeningSocket::~CListeningSocket()
{
}
#ifdef _DEBUG
void CListeningSocket::AssertValid() const
{
	CSocket::AssertValid();
}
void CListeningSocket::Dump(CDumpContext& dc) const
{
	CSocket::Dump(dc);
};
#endif //_DEBUG
 
 
 
//Die folgenden Zeilen nicht bearbeiten. Sie werden vom Klassen-Assistenten benötigt.
#if 0
BEGIN_MESSAGE_MAP(CListeningSocket, CSocket)
	//{{AFX_MSG_MAP(CListeningSocket)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()
#endif	// 0
/////////////////////////////////////////////////////////////////////////////
// Member-Funktion CListeningSocket 
void CListeningSocket::OnAccept(int nErrorCode) 
{
	// TODO: Speziellen Code hier einfügen und/oder Basisklasse aufrufen
		m_pDoc->ProcessPendingAccept();
	        CSocket::OnAccept(nErrorCode);
}

Die Klasse CListeningSocket werden Sie benötigen, wenn Sie das Programm als Server starten. Sie werden ein Objekt dieser Klasse erzeugen, und den Server dann auf Horchen stellen. Die Callbackfunktion OnAccept , die Sie mit Hilfe des ClassWizards erstellen können, müssen Sie auf jeden Fall überschreiben. Wenn ein Client versuchen wird, Verbindung zum horchenden Server aufzunehmen, wird OnAccept aufgerufen. Hier müssen alle Modalitäten drin stehen, wie der Server ein Verbindungsgesuch des Clients abwickelt. Nun schauen wir uns die Klasse CClientSocket an. Die Headerdatei sieht wie folgt aus:

class CReglerDoc;
class CMsg;
class CClientSocket : public CSocket
{
DECLARE_DYNAMIC(CClientSocket)
private:
	CClientSocket(const CClientSocket& rSrc); //keine Implementierung
	void operator=(const CClientSocket& rSrc); // keine Implementierung
// Construction
public:
	CClientSocket(CReglerDoc* m_pDoc);
// Attributes
public:
	 CReglerDoc* m_pDoc;
	
public:
	// Vom Klassen-Assistenten generierte virtuelle Funktionsüberschreibungen
	//{{AFX_VIRTUAL(CClientSocket)
	public:
	virtual void OnReceive(int nErrorCode);
	//}}AFX_VIRTUAL
	// Generierte Nachrichtenzuordnungsfunktionen
	//{{AFX_MSG(CClientSocket)
		// HINWEIS - Der Klassen-Assistent fügt hier Member-Funktionen ein und entfernt diese.
	//}}AFX_MSG
// Implementierung
	public:
virtual ~CClientSocket();
#ifdef _DEBUG
	virtual void AssertValid() const;
	virtual void Dump(CDumpContext& dc) const;
#endif
};

Die Implementierung von CClientSocket sieht folgendermaßen aus:

#include "stdafx.h"
#include "regler.h"
#include "lstnsock.h"
#include "Clntsock.h"
#include "reglerdoc.h"
#include "Msg.h"
#include "reglerdoc.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CClientSocket
IMPLEMENT_DYNAMIC(CClientSocket, CSocket)
CClientSocket::CClientSocket(CReglerDoc* pDoc)
{
	m_pDoc = pDoc;
	
}
/////////////////////////////////////////////////////////////////////////////
// CClientSocket Overridable callbacks
 
/////////////////////////////////////////////////////////////////////////////
// CSocket Implementation
CClientSocket::~CClientSocket()
{
	if(m_pDoc->IstClient){
	 if (m_pDoc->m_pArchiveOut_Client != NULL)
		 delete m_pDoc->m_pArchiveOut_Client;
	 if (m_pDoc->m_pArchiveIn_Client != NULL)
		 delete m_pDoc->m_pArchiveIn_Client;
	 if (m_pDoc->m_pFile_Client != NULL)
		 delete m_pDoc->m_pFile_Client;
	}
	
if(m_pDoc->IstServer){
	
	 if (m_pDoc->m_pArchiveOut_Server != NULL)
		 delete m_pDoc->m_pArchiveOut_Server;
	 if (m_pDoc->m_pArchiveIn_Server != NULL)
		 delete m_pDoc->m_pArchiveIn_Server;
	 if (m_pDoc->m_pFile_Server != NULL)
		 delete m_pDoc->m_pFile_Server;
	}
 
}
//Die folgenden Zeilen nicht bearbeiten. Sie werden vom Klassen-Assistenten benötigt.
#if 0
BEGIN_MESSAGE_MAP(CClientSocket, CSocket)
	//{{AFX_MSG_MAP(CClientSocket)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()
#endif	// 0
/////////////////////////////////////////////////////////////////////////////
// Member-Funktion CClientSocket 
void CClientSocket::OnReceive(int nErrorCode) 
{
	// TODO: Speziellen Code hier einfügen und/oder Basisklasse aufrufen
	m_pDoc->ProcessPendingRead();
	CSocket::OnReceive(nErrorCode);
}
#ifdef _DEBUG
void CClientSocket::AssertValid() const
{
	CSocket::AssertValid();
}
void CClientSocket::Dump(CDumpContext& dc) const
{
	CSocket::Dump(dc);
}
#endif //_DEBUG

Mit den Objekten dieser Klasse werden Daten zwischen dem Server und Client hin und her transportiert. Wie Sie sehen können ist bei dieser Klasse die Callbackfunktion OnReceive zuerst mit ClassWizard erzeugt und dann überschrieben worden. OnReceive müssen Sie immer überschreiben, sonst wird das Programm ewig lang auf Empfang bleiben und alles andere wird blockiert. Daher müssen in dieser Funktion alles Modalitäten enthalten sein, die das Empfangen von Daten betreffen. Nun schauen wir uns die Klasse CMsg an. Diese Klasse verkörpert das Datenpaket selbst. Die Headerdatei von CMsg sieht wie folgt aus:

class CMsg : public CObject
{
protected:
	DECLARE_DYNCREATE(CMsg)
public:
	CMsg();
// Attributes
public:
	CString m_strText;
	
	CStringList m_msgList_Server;
	CStringList m_msgList_Client;
// Operations
public:
	void Init();
// Implementation
public:
	virtual ~CMsg();
	virtual void Serialize(CArchive& ar); // overridden for document i/o
#ifdef _DEBUG
	virtual void AssertValid() const;
	virtual void Dump(CDumpContext& dc) const;
#endif
};

Im folgenden wird die Implementierung von CMsg gzeigt:

IMPLEMENT_DYNCREATE(CMsg, CObject)
/////////////////////////////////////////////////////////////////////////////
// CMsg construction/destruction
CMsg::CMsg()
{
	Init();
}
CMsg::~CMsg()
{
}
/////////////////////////////////////////////////////////////////////////////
// CMsg Operations
void CMsg::Init()
{
	
	m_strText = _T("");
	m_msgList_Server.RemoveAll();
        m_msgList_Client.RemoveAll();
}
/////////////////////////////////////////////////////////////////////////////
// CMsg serialization
void CMsg::Serialize(CArchive& ar)
{
	if (ar.IsStoring())
	{
	
		ar << m_strText;
	}
	else
	{
	
		ar >> m_strText;
	}
	
	
	m_msgList_Client.Serialize(ar);
	m_msgList_Server.Serialize(ar);
}
/////////////////////////////////////////////////////////////////////////////
// CMsg diagnostics
#ifdef _DEBUG
void CMsg::AssertValid() const
{
	CObject::AssertValid();
}
void CMsg::Dump(CDumpContext& dc) const
{
	CObject::Dump(dc);
}
#endif //_DEBUG

Die Klasse CMsg übernimmt ihre Serialisierung selbst. Sowohl auf der Serverseite als auch auf der Clientseite werden die Nachrichten in Auflistungsklassen gelagert. Jetzt ist es an der Zeit, die Dokumentklasse zu zeigen. Die Headerdatei des Dokuments sieht wie folgt aus:

class CListeningSocket;
class CClientSocket;
class CMsg;
class CReglerDoc : public CDocument
{
protected: // Nur aus Serialisierung erzeugen
	CReglerDoc();
	DECLARE_DYNCREATE(CReglerDoc)
// Attribute
	
public:
	CArchive* m_pArchiveIn_Client;
	CArchive* m_pArchiveOut_Client;
	CSocketFile* m_pFile_Server;
	CSocketFile* m_pFile_Client;
	CArchive* m_pArchiveIn_Server;
	CArchive* m_pArchiveOut_Server;

	CListeningSocket* m_pSocket;
	CClientSocket* m_pClient;
	CClientSocket* m_pServer;
	CStringList m_msgList;
	CString m_name;
	BOOL IstClient;
	BOOL IstServer;
// Operationen
public:
        BOOL ConnectSocket(LPCTSTR lpszAddress, UINT nPort);
        void DisplayMsg(LPCTSTR lpszText);
        void ProcessPendingAccept();
        void ProcessPendingRead();
        void SendMsgtoClient(CString str);
        void SendMsgtoServer(CString str);
        void ReceiveMsgfromServer();
        void ReceiveMsgfromClient();
// Operationen
public:
// Überladungen
	// Vom Klassenassistenten generierte Überladungen virtueller Funktionen
	//{{AFX_VIRTUAL(CReglerDoc)
	public:
	virtual BOOL OnNewDocument();
	virtual void Serialize(CArchive& ar);
	//}}AFX_VIRTUAL
// Implementierung
public:
	virtual ~CReglerDoc();
#ifdef _DEBUG
	virtual void AssertValid() const;
	virtual void Dump(CDumpContext& dc) const;
#endif
protected:
// Generierte Message-Map-Funktionen
protected:
	//{{AFX_MSG(CReglerDoc)
		// HINWEIS - An dieser Stelle werden Member-Funktionen vom Klassen-Assistenten eingefügt und entfernt.
		// Innerhalb dieser generierten Quelltextabschnitte NICHTS VERÄNDERN!
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

Die Implementierung der Dokumentklasse sieht folgendermaßen aus:

#include "reglerDoc.h"
#include "Clntsock.h"
#include "Msg.h"
#include "lstnsock.h"
#include "reglerview.h"
#include "dlgsetup.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CReglerDoc
IMPLEMENT_DYNCREATE(CReglerDoc, CDocument)
BEGIN_MESSAGE_MAP(CReglerDoc, CDocument)
	//{{AFX_MSG_MAP(CReglerDoc)
		// HINWEIS - Hier werden Mapping-Makros vom Klassen-Assistenten eingefügt und entfernt.
		// Innerhalb dieser generierten Quelltextabschnitte NICHTS VERÄNDERN!
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CReglerDoc Konstruktion/Destruktion
CReglerDoc::CReglerDoc()
{
	// ZU ERLEDIGEN: Hier Code für One-Time-Konstruktion einfügen
        m_pFile_Client=NULL;
        m_pArchiveIn_Client=NULL;
        m_pArchiveOut_Client=NULL;
        m_pFile_Server=NULL;
        m_pArchiveIn_Server=NULL;
        m_pArchiveOut_Server=NULL;
	
        m_pSocket=NULL;
        m_pClient=NULL;
        m_pServer =NULL;
        IstClient=FALSE;
        IstServer=FALSE;
}
CReglerDoc::~CReglerDoc()
{
}
BOOL CReglerDoc::OnNewDocument()
{
	if (!CDocument::OnNewDocument())
		return FALSE;
	// ZU ERLEDIGEN: Hier Code zur Reinitialisierung einfügen
	// (SDI-Dokumente verwenden dieses Dokument)
DlgSetup Dialog;
if(IDOK!=Dialog.DoModal())
return FALSE;
	
	 if ( Dialog.m_server==TRUE)
	 {
			
	 	m_pSocket = new CListeningSocket(this);
		if (m_pSocket->Create(701))
		{
			if (m_pSocket->Listen()){
				IstServer=TRUE;
	
	               AfxMessageBox("Bereit zur Verbindungsaufnahme",MB_ICONINFORMATION);
			 	return TRUE;
			}
		}
	}
	
	 if ( Dialog.m_client==TRUE)
	 {
		if(ConnectSocket(Dialog.m_ip_adress,701))
		{
			IstClient=TRUE;
			
                      AfxMessageBox("Die Verbindung wurde hergestellt",MB_ICONINFORMATION);
			 return TRUE;
		}
	}
	return FALSE;
}
 
 
/////////////////////////////////////////////////////////////////////////////
// CReglerDoc Serialisierung
void CReglerDoc::Serialize(CArchive& ar)
{
	if (ar.IsStoring())
	{
		// ZU ERLEDIGEN: Hier Code zum Speichern einfügen
	}
	else
	{
		// ZU ERLEDIGEN: Hier Code zum Laden einfügen
	}
}
/////////////////////////////////////////////////////////////////////////////
// CReglerDoc Diagnose
#ifdef _DEBUG
void CReglerDoc::AssertValid() const
{
	CDocument::AssertValid();
}
void CReglerDoc::Dump(CDumpContext& dc) const
{
	CDocument::Dump(dc);
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CReglerDoc Befehle
BOOL CReglerDoc::ConnectSocket(LPCTSTR lpszAddress, UINT nPort)
{
	
	 m_pClient = new CClientSocket(this);
	if (!m_pClient->Create())
	{
		delete m_pClient;
		m_pClient = NULL;
		return FALSE;
	}
	
        if(!m_pClient->Connect(lpszAddress, nPort ))
	{
	
			delete m_pClient;
			m_pClient = NULL;
			AfxMessageBox("Die Verbindung konnte nicht hergestellt"
                                       "werden",MB_OK );
			return FALSE;
	
	}
	 m_pFile_Client = new CSocketFile(m_pClient);
	 m_pArchiveIn_Client = new CArchive(m_pFile_Client,CArchive::load);
	 m_pArchiveOut_Client = new CArchive(m_pFile_Client,CArchive::store);
	
	 return TRUE;
}
 
 
 
void CReglerDoc::ProcessPendingAccept() 
{
	CClientSocket* pSocket = new CClientSocket(this);
        m_pServer=pSocket;
	
	if (m_pSocket->Accept(*m_pServer))
	{
	
	 m_pFile_Server = new CSocketFile(m_pServer);
	 m_pArchiveIn_Server = new CArchive(m_pFile_Server,CArchive::load);
	 m_pArchiveOut_Server = new CArchive(m_pFile_Server,CArchive::store);
	
	}
	else
		delete m_pServer;
}
 
void CReglerDoc::DisplayMsg(LPCTSTR lpszText)
{
	for(POSITION pos=GetFirstViewPosition();pos!=NULL;)
	{
		CView* pView = GetNextView(pos);
		CReglerView* pChatView = DYNAMIC_DOWNCAST(CReglerView, pView);
		
		 if (pChatView != NULL)
			 pChatView->Message(lpszText);
	}
	
}
void CReglerDoc::ProcessPendingRead() 
{
	if(IstClient){
	do
	{
		ReceiveMsgfromServer();
		
	}
	while(!m_pArchiveIn_Client->IsBufferEmpty());
	
	}
	if(IstServer){
do
	{
		ReceiveMsgfromClient();
		
	}
	while(!m_pArchiveIn_Server->IsBufferEmpty());
	}
}
void CReglerDoc::SendMsgtoServer(CString str)
{
	if(m_pArchiveOut_Client !=NULL)
	{
		CMsg msg;
		msg.m_strText=str;
                msg.m_msgList_Client.AddTail(str);
		
		//TRY
		msg.Serialize(*m_pArchiveOut_Client);
		m_pArchiveOut_Client->Flush();
	}
}
void CReglerDoc::SendMsgtoClient(CString str)
{
	if(m_pArchiveOut_Server !=NULL)
	{
		CMsg msg;
		msg.m_strText=str;
		msg.m_msgList_Server.AddTail(str);
		
		//TRY
		msg.Serialize(*m_pArchiveOut_Server);
		m_pArchiveOut_Server->Flush();
	}
}
void CReglerDoc:: ReceiveMsgfromServer()
{
	CMsg msg;
	//TRY
	msg.Serialize(*m_pArchiveIn_Client);
	while(!msg.m_msgList_Server.IsEmpty())
	{
		CString temp = msg.m_msgList_Server.RemoveHead();
		DisplayMsg(temp);
	}
}
void CReglerDoc::ReceiveMsgfromClient()
{
CMsg msg;
	//TRY
	msg.Serialize(*m_pArchiveIn_Server);
	while(!msg.m_msgList_Client.IsEmpty())
	{
		CString temp = msg.m_msgList_Client.RemoveHead();
		DisplayMsg(temp);
	}
}

Der Umfang des Quellcodes in diesem Beispielprogramm sollte Sie nicht verwirren. Als Ausgangspunkt für das gesamte Programm kommen lediglich nur drei Funktionen in Frage und diese sind:

CreglerDoc::OnNewDocument, CListeningSocket::OnAccept und CClientSocket::OnReceive. Die Funktion OnNewDocument der Dokumentklasse wird in einem SDI Programm beim Start des Programms aufgerufen. Daher wird hier zuerst das Dialog für die Bestimmung des Server- oder Clientbetriebes aufgerufen. Befindet sich das Programm im Serverbetrieb, wird mit den Anweisungen:

	
	 	m_pSocket = new CListeningSocket(this);
		if (m_pSocket->Create(701))
		m_pSocket->Listen();

ein CSocket-Objekt erzeugt, eine Portnummer generiert und das Socket auf Horchen gestellt. Nimmt nun ein Client Kontakt zum Server auf, wird die Methode CListeningSocket::OnAccept aufgerufen, die wiederum eine andere Methode aus dem Dokument aufruft. In dieser Methode wird der Kontaktwunsch des Clients akzeptiert und ein CSocket-Objekt und zwei Carchive-Objekte und ein CSocketFile-Objekte für den Datenaustausch auf der Serverseite erzeugt. Nun kehren wir zur ursprünglichen Funktion CReglerDoc::OnNewDokument zurück. Startet das Programm im Clientbetrieb, so wird die Methode CReglerDoc::ConnectSocket aufgerufen. Hierin werden auch das CSocket-Objekt, die beiden Carchive-Objekte und das CSocketFile-Objekt für den Datenaustausch auf der Clientseite erzeugt. Die Funktion CClientSocket::OnReceive wird sowohl für die Serverseite, als auch für den Clientbetrieb überschrieben. Im Serverbetrieb wird die Methode CReglerDoc::ReceiveMsgfromClient und im Clientbetrieb wird die Methode CReglerDoc::ReceiveMsgfromServer aufgerufen. Die Methoden CReglerDoc::SendMsgtoServer und CReglerDoc::SendMsgtoClient sind für das Senden der Daten zuständig. Die Methode CReglerDoc::DisplayMsg sorgt dafür, daß die Daten vom Dokument in die Ansicht transportiert werden. Und die Methode CReglerView::Message zeigt das Ergebnis auf dem Bildschirm. Die Methode CReglerView::Message sieht wie folgt aus:

void CReglerView::Message(LPCTSTR lpszMessage)
{

 	CString strTemp=lpszMessage;
	GetDlgItem(IDC_EDIT2)->SetWindowText(strTemp);
        value=atoi(strTemp);
        m_slider.SetPos(value);
	
}

Die int Variable value ist in der Headerdatei von CReglerView deklariert worden. Die Variable m_slider ist natürlich mit dem Schieberegler verbunden. Und IDC_EDIT2 ist das ID des Editfeldes in dem, der momentane Wert der Reglers gezeigt wird. Für die Vollständigkeit wird im folgenden noch die Funktion CReglerView::OnVScroll gezeigt:

void CReglerView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	// TODO: Code für die Behandlungsroutine für Nachrichten hier einfügen
	CReglerDoc* pDoc =GetDocument();
	ASSERT_VALID(pDoc);
	
	if(pScrollBar !=NULL)
	{
		CString str;
	 value=m_slider.GetPos();
        str.Format("%d",m_slider.GetPos());
        GetDlgItem(IDC_EDIT2)->SetWindowText(str);
        if(pDoc->IstClient)
        pDoc->SendMsgtoServer(str);
        if(pDoc->IstServer)
        pDoc->SendMsgtoClient(str);
	}
 
	CFormView::OnVScroll(nSBCode, nPos, pScrollBar);
}

 

 

ISAPI Servererweiterungen(ISA)

 

ISAPI(Internet Server Application Programming Interface) bildet eine Schnittstelle für die Server-Anwendungsprogrammierung und besteht aus einer Reihe von APIs, mit denn man Servererweiterungen und Serverfilter schreiben kann.Eine ISAPI Servererweiterung ist eine DLL, die von einem ISAPI Kompatiblen HTTP Server aufgerufen werden kann. Der Microsoft Internet Information Server (IIS) ist ein derartiger Server. IIS gehört zu dem Lieferumfang von Windows NT 4.0. Die Installation von IIS ist allerdings optional. Achten Sie darauf, daß das Verzeichnis, in dem sich die Servererweiterung befindet, eine Ausführungsberechtigung auf den Internet Server haben muß. Eine Internet Servererweiterung, auch ISA(Internet Server Application) genannt, kann von Browseranwendungen aufgerufen werden und verfügt über ähnliche Funktionen, wie eine Common Gateway Interface(CGI) Anwendung. Mit Hilfe von ISAs können sehr unterschiedliche Bereiche in der Internetprogrammierung erfaßt werden. Benutzer können im Internet Formulare ausfüllen und durch das Anklicken der "Abschicken" Taste die Daten zu einem Web Server senden, der wiederum die ISAs aufrufen um die abgeschickten Daten zu verarbeiten kann. Mit Hilfe von ISAs könnte man diverse Steuerungen über das Internet durchführen. Ein konkretes Beispiel wäre z.B. die Steuerung einer Webkamera. ISAs können als eine echte Alternative zu den CGI Anwendungen gesehen werden. Als DLL, schont eine ISA die Resourcen eines Web Servers erheblich. Sie wird nur einmal ausgeführt und wird nicht für jede Abfrage neu geladen und ist daher sehr schnell.

Die MFC bietet eine Reihe von Klassen mit denen ISAs programmiert werden können. Im folgenden werden die MFC Klassen CHttpServer und CHttpServerContext etwas näher erläutert.

 

Die MFC Klassen, die für die ISAPI Programmierung zur Verfügung stehen, haben keine Basisklassen und sind nicht wie gewöhnlich von CObject abgeleitet worden. Um eine ISA mit Hilfe von MFC programmieren zu können, muß man zuerst ein CHttpServer-Objekt erstellen. Bei einer Clientabfrage erstellt das CHttpServer-Objekt wiederum ein CHttpServerContext-Objekt. Letzteres stellt dem CHttpServer-Objekt alle nötigen Werkzeuge zur Verfügung, mit dem die Daten der Clientabfrage bearbeitet werden können. Die Anwendung der Member-Funktionen dieser beiden Klassen werden Sie im folgenden Beispielprogramm namens anmld besser verstehen.

 

Das Beispielprogramm zu dem Thema ISAs heißt anmld. Das Beispiel beinhaltet eine Servererweiterung und ein HTML-Formular. Sie können die beiden Dateien auf den Microsoft Internet Information Server(IIS) in geeigneten Verzeichnissen speichern. Nehmen wir an, Sie sind ein Experte in Visual C++ und bieten Lehrgänge im Internet an. Interessenten können das Formular ausfüllen und sich für den Lehrgang anmelden. Wenn ein User im Formular die "Anmelden" Taste drückt, wird eine Formularabfrage gestartet, die die DLL bzw. die Servererweiterung aufruft. Die Servererweiterung ließt die Eingaben aus dem Formular und erstellt für den User dynamisch eine HTML-Seite und bestätigt den Eingang der Anmeldung. Der Datenaustausch findet über einen besonderen Mechanismus, den man als Analysezuordnung nennt. In einer Analysezuordnung ordnet ein von CHttpServer abgeleitetes Objekt Client-Anforderungen den zugehörigen Member-Funktionen zu. Die Analysezuordnung besteht aus insgesamt fünf Makros, die wie folgt definiert sind:

BEGIN_PARS_MAP: Dieses Makro startet die Definition der Analysezuordnung.

ON_PARSE_COMMAND: Identifiziert einen Befehl. Die Klasse der Member-Funktion und eine Liste von Parametertypen bilden die Parameter dieses Makros.

ON_PARSE_COMMAND_PARAMS: Definiert die Parameterliste des Befehls und muß unmittelbar auf ON_PARSE_COMMAND folgen. Die vom Client erhaltenen Argumente gelten als Parameter dieses Makros.

DEFAULT_PARSE_COMMAND: Falls für die Ausführung der Servererweiterung keine weiteren Befehle definiert worden sind, wird dieses Makro ausgeführt.

END_PARSE_MAP: Beendet die Definition eines Analysezuordnungsmakros.

Der Ausgangspunkt des Beispielprogramms ist das HTML-Formular, dessen Quellcode wie folgt aussieht:


Abbildung
<html>
<head>
<title>Anmeldung zu einem Visual C++ Lehrgang</title>
</head>
<body bgcolor=#EEEEEE text=#000000 link=#AA5522 vlink=#772200 alink=#000000>
<P>&nbsp;</P>
<p><H2><center><font face="Arial" color="#005BA5">Anmeldeformular</font></center></H2></p>
<P>&nbsp;</P>
<p>Um sich für ein Visual C++ Lehrgang anmelden zu können, m&uuml;ssen Sie das folgende Formular ausf&uuml;llen und abschicken:</p>
<form action ="scripts/anmld.dll?AnmeldeDaten" method=POST>
<table>
<tr>
<td colspan=2><hr noshade size=1></td>
</tr><tr><td align=right>Ihr Name:</td><td><tt><input type=text size=50 name="name"></tt></td>
</tr><tr>
<td align=right>Ihre E-Mail-Adresse:</td><td><formdata><tt><input type=text size=50 name="email"></tt></formdata></td>
</tr><tr>
<td align=right>Ihr Alter:</td><td><tt><input type=text size=5 name="alter"></tt></td>
</tr><tr>
<td colspan=2><hr noshade size=1></td>
</tr><tr>
<td colspan=2><p><font color="#005BA5">Wie w&uuml;rden Sie Ihre vorhandenen Kenntnisse in Visual C++ einstufen?</font></p></td>
</tr><tr>
<td><input type=radio name="Beginner" value="skill" checked> Anf&auml;nger <br>
<input type=radio name="skill" value="Middle" > Mittel<br>
<input type=radio name="skill" value="Advanced" > Fortgeschritten <br>
</td>
</tr>
<tr>
<td colspan=2><hr noshade size=1></td>
</tr>
<tr>
<td colspan=2><p><font color="#005BA5">Im welchem Bereich m&ouml;chten Sie Ihre Kenntnisse vertiefen ? </font></p></td>
</tr>
<tr>
<td>
<input type=checkbox name="controls" value="Controls"checked> Windows Steuerelemente<br>
<input type=checkbox name="dockview" value="Docview"> Dokument und Ansicht <br>
<input type=checkbox name="grafik" value="Grafik"> Grafik <br>
<input type=checkbox name="internet" value="Internet"> Internet <br>
</td>
</tr><tr>
<td colspan=2><hr noshade size=1></td>
</tr><tr>
<P>&nbsp;</P>
<P>&nbsp;</P>
<td align=right><P>&nbsp;</P>
Formular:</td>
<td>
<P>&nbsp;</P>
<input type=submit value="Anmelden"> <input type=reset value="Abbrechen"> </td>
</tr></table>
</form>
 
</body>
</html>

Schauen wir uns die folgende Anweisung aus dem obigen HTML-Code genauer an:

<form action ="scripts/anmld.dll?AnmeldeDaten" method=POST>

Diese Anweisung besagt, daß die Servererweiterung, die die Formulardaten bearbeiten soll, anmld.dll heißt und sich in dem virtuellen Verzeichnis scripts befindet und die Funktion, an die die Daten in der DLL geleitet wird, AnmeldeDaten heißt. Im übrigen benutzen Sie für den Umgang mit Servererweiterungen immer die POST Methode anstelle von GET, weil es sicherer ist.

Bei der Erstellung einer Servererweiterung unterstützt Sie der AppWizard erheblich. Starten Sie im AppWizard den Assistenten für ISAPI-Erweiterungen , geben Sie Ihrem Projekt den Namen anmld und bestätigen Sie Ihre Aktion. Im Schritt eins des Assistenten für ISAPI-Erweiterungen markieren Sie bitte die Option " Server-Erweiterungsobjekt erzeugen" und bestätigen Sie Ihre Eingaben. AppWizard erstellt nun für Sie das Projekt.


Abbildung

Abbildung
Da die DLL sozusagen keine Oberfläche hat, ist auch das erstellte Projekt verglichen mit anderen Projekten, die wir bis jetzt behandelt haben, schlanker. Deklarieren Sie bitte in der Headerdatei von anmld.cpp, in der sich fast alles abspielt die Funktion AnmeldeDaten:

void AnmeldeDaten(CHttpServerContext* pCtxt, LPCTSTR pstrName, 
                  LPCTSTR pstrMail, long int absAlter, 
                  LPCTSTR pstrSkill , LPCTSTR pstrControls, LPCTSTR 
                  pstrDocview,LPCTSTR pstrGrafik,LPCTSTR pstrInternet);

Wie Sie Sehen, ist der erste Parameter der Funktion AnmeldeDaten ein Zeiger auf CHttpServerContext. Beim Aufruf von AnmeldeDaten übergibt die MFC einen Zeiger auf CHttpServerContext. Mit diesem Zeiger können Sie dann auf die Member-Funktionen von CHttpServerContext zugreifen. Die Typen der übrigen Parameter stimmen mit den Datentypen aus dem Formular überein. Die Implementierung der Funktion AnmeldeDaten sieht folgendermaßen aus:

 

void CAnmldExtension::AnmeldeDaten( CHttpServerContext* pCtxt, LPCTSTR 
                                    pstrName, LPCTSTR pstrMail,
				    long int absAlter, LPCTSTR pstrSkill,
				    LPCTSTR pstrControls, LPCTSTR pstrDocview, 
                                    LPCTSTR pstrGrafik, LPCTSTR pstrInternet  )

{
	StartContent(pCtxt);
	
        WriteTitle(pCtxt);
	
        if((strlen(pstrName)>0 )&& (strlen(pstrMail)>0))
	{
		*pCtxt<<"Ihre Daten sehen wie folgt aus:";
		*pCtxt<<"<p>Name: "<<pstrName;
		*pCtxt<<"<p>E-Mail: "<<pstrMail;
		*pCtxt<<"<p>Alter: "<<(long int) absAlter;
		*pCtxt<<"<p>Vorhandene Visual C++ Kenntnisse: "<<pstrSkill;
		*pCtxt<<"<p>Themenbereiche: "<<pstrControls<<" "<<pstrDocview
                <<" "<<pstrGrafik<<" "<<pstrInternet;
		*pCtxt<<"<p> Es ist ein geeigneter Platz für Sie frei!"
			" Drücken Sie bitte auf untere Taste, um die Reservierung zu 
                         vervollständigen.";
		
                *pCtxt<<"<form action=\"anmld.dll?ConfirmOrder\" method=POST>";
		*pCtxt<<"<p><input type=\"hidden\"name=\"name\"value=\""<<
			pstrName<<"\">";
		*pCtxt<<"<p><input type=\"submit\"value=\"Anmeldung bestätigen\">";
		*pCtxt<<"</form>";
		//Vielleicht Eintrag in einer Datenbank
	}
	else{
		*pCtxt<<" Sie haben vergessen, Ihren Namen oder E-Mail einzutragen."
			 " Vervollständigen Sie Ihre Eingaben und melden Sie sich neu an.";
	}
	EndContent(pCtxt);
}

Die Member-Funktion StartContent fügt in die dynamisch zu erstellende HTML-Seite für den Client die HTML-Tags <HTML> und <BODY> ein. Die Member-Funktion WriteTitle schreibt den Titel der zu erzeugende Seite für den Client. WriteTitle holt sich den Titel von der Member-Funktion GetTitle, die Sie mit ClassWizard erzeugen können. Der Zeiger *pCtxt schreibt die Daten mit Hilfe der überladenen Ausgabeoperatoren in die zu erzeugende HTML-Seite. Schauen wir uns folgende Anweisungen genauer an:

*pCtxt<<"<form action=\"anmld.dll?ConfirmOrder\" method=POST>";
*pCtxt<<"<p><input type=\"hidden\"name=\"name\"value=\""<<pstrName<<"\">";

Damit wird in der zu erzeugenden HTML-Seite eine weitere Formularabfrage gestartet, die eine Funktion namens ConfirmOrder aufruft, welche wir noch schreiben müssen. Damit auch im zweiten dynamisch erstellten Formular die Beziehung zum originalen Anmeldeformular erhalten bleibt, wird der Name aus dem Anmeldeformular versteckt weitergegeben.

Die Deklaration und Implementierung der Funktion ConfirmOrder sieht wie folgt aus:

void ConfirmOrder(CHttpServerContext* pCtxt,LPCSTR pstrName);

void CAnmldExtension::ConfirmOrder(CHttpServerContext* pCtxt,LPCSTR pstrName)
{
	StartContent(pCtxt);
        WriteTitle(pCtxt);
	*pCtxt<<"<p> Danke schön " <<pstrName<< ", für Ihr Interesse";
	*pCtxt<<"<p> Weitere Informationen über den Lehrgang erhalten "
		 "Sie von uns über Ihre E-Mail.";
	EndContent(pCtxt);
}

Nun fehlt noch der Eintrag in die Zuordnungsanalyse , die wie folgt ausschauen muß:

BEGIN_PARSE_MAP(CAnmldExtension, CHttpServer)
	// ZU ERLEDIGEN: Fügen Sie Ihr ON_PARSE_COMMAND() ein und 
	// ON_PARSE_COMMAND_PARAMS() hier, um Ihre Befehle einzubinden.
	// Beispiel:
	ON_PARSE_COMMAND(Default, CAnmldExtension, ITS_EMPTY)
	DEFAULT_PARSE_COMMAND(Default, CAnmldExtension)
ON_PARSE_COMMAND(ConfirmOrder, CAnmldExtension, ITS_PSTR)
	ON_PARSE_COMMAND_PARAMS("name") 
	
ON_PARSE_COMMAND(AnmeldeDaten, CAnmldExtension, ITS_PSTR 
ITS_PSTR ITS_I4 ITS_PSTR ITS_PSTR ITS_PSTR ITS_PSTR ITS_PSTR)
	ON_PARSE_COMMAND_PARAMS("name email alter skill controls=~ 
                                 docview=~ grafik=~ internet=~") 
END_PARSE_MAP(CAnmldExtension)

 

 

      Vorheriges Kapitel: ActiveX Steuerelemente       Inhaltsverzeichnis Inhaltsverzeichnis       Die Downloadseite Download       Kontakt Kontakt