64
Auszug aus VB .NET Objektorientiertes Programmieren in VB und Einstieg in die .NET-Klassenbibliothek von Andreas Kühnel Ein Service von Galileo Computing 960 S., 2002, geb., mit CD 49,90 Euro, ISBN 3-89842-129-5 Galileo Computing

VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

  • Upload
    dinhnga

  • View
    234

  • Download
    0

Embed Size (px)

Citation preview

Page 1: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

Auszug aus

VB .NET ObjektorientiertesProgrammieren in VB und Einstieg in die .NET-Klassenbibliothek

von Andreas Kühnel

Ein Service von

Galileo Computing960 S., 2002, geb., mit CD49,90 Euro, ISBN 3-89842-129-5

Galileo Computing

Page 2: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

TCP/IP-Programmierung 877

14 TCP/IP-Programmierung

In diesem Kapitel lernen Sie, wie zwei Rechner basierend auf der Socket-API miteinander in Verbindung treten und miteinander kom-munizieren. Anschließend werden wir die Kenntnisse dazu benutzen, um eine E-Mail an einen beliebigen Empfänger zu schicken.

14.1 Ein paar fundamentale Netzwerkgrundlagen

So langsam nähern wir uns dem Ende dieses Buches. Wir haben sehr viel gelernt,wissen nun (hoffentlich), objektorientiert zu programmieren, haben uns einigewesentliche Techniken angesehen und viele Klassen des .NET-Frameworks ken-nen gelernt. Nun sind wir in der Lage, Anwendungen mit visualisierter Oberflächezu entwickeln, können uns mit der neuen DatenbankzugriffstechnologieADO .NET auseinander setzen oder Applikationen für das Internet schreiben – dieMöglichkeiten und die Grenzen können derzeit noch gar nicht abgeschätzt wer-den.

Wir wollen zum Abschluss dieses Buches in die Fundgrube der .NET-Klassenbiblio-thek greifen und uns mit einem sehr interessanten Thema beschäftigen. Dabei solles nicht darauf ankommen, etwas programmiertechnisch gänzlich Neues zulernen, sondern auf den erworbenen Kenntnissen aufzusetzen, diese einzusetzenund dabei auch noch Spaß an einer lebhaften Thematik zu haben, die sogar zueinem über dieses Buch hinausgehenden Studium motiviert – der Programmie-rung auf Grundlage des Netzwerkprotokolls TCP/IP.

14.1.1 Das WinSock-API

Die Zeit der Einzelplatzrechner ist vorbei; Netzwerke führen nicht erst seit kurzemdie einzelnen Rechner einem größerem Organisationsverbund zu – sei es in loka-len Netzwerken oder im Internet. Dabei beschränken sich Netzwerke nicht nurauf Rechner, sondern binden auch andere Hardwarekomponenten in die Strukturein. Die Idee, die sich hinter den Netzwerken verbirgt, ist die gemeinsame Nut-zung von Ressourcen – sowohl informative wie beispielsweise Datenbanken alsauch technische wie Drucker oder Faxgeräte.

Die Ressourcen eines Netzwerks müssen miteinander kommunizieren. EinemDrucker muss gesagt werden, dass und in welchem Format er ein Dokument aus-geben soll. Greift man auf eine Dateninformation zu, muss genau angegeben wer-den, welche aktuell von Interesse ist. Um die Steuerung bzw. den Datenaustauschzwischen mehreren Partnern zu ermöglichen, muss die Kommunikation auf einer

VBNET-14.fm Seite 877 Mittwoch, 27. März 2002 5:17 17

aus: A. Kühnel: VB.NET. OOP in VB und Einstieg in die .NET-Klassenbibliothek

© 2001 Galileo Press GmbH

© 2002 Galileo Press GmbH

Page 3: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

878 TCP/IP-Programmierung

Basis erfolgen, die von allen am Austausch beteiligten verstanden wird. Diesegemeinsame Basis wird als Protokoll bezeichnet. Ein Protokoll beschreibt dieRegeln, die notwendig sind, um einen eindeutigen Verbindungsaufbau, Daten-austausch und letztendlich auch Verbindungsabbau zu gewährleisten.

In der Vergangenheit wurden verschiedene Protokolle von verschiedenen Institu-tionen und Unternehmen mit unterschiedlichen Fähigkeiten entwickelt. Aus die-sen kristallisierte sich am Ende eine Spezifikation heraus, die ihren Ursprung unterUNIX hatte und sich zum Standard im Internet etablierte: das Socket-Interface,das auch als Socket-API bezeichnet wird.

Als es 1992 darum ging, der Windows-Plattform den Weg ins Internet zu ebnen,griff Microsoft das Socket-Interface auf – nicht zuletzt aus dem Grund, um diebereits bestehenden Internet-Anwendungen aus dem UNIX-Bereich relativ prob-lemlos übernehmen zu können. Damals gab es bereits die Software, die Windowserst noch benötigte, denn bestehender Programmcode kann leichter portiertwerden, wenn dieselben Funktionen, Konstanten usw. zum Einsatz kommen.

Obwohl die Portierung auf Windows Anpassungen erforderlich machte undeinige Funktionen hinzugefügt werden mussten, blieb die grundlegende Strukturdes Socket-Interfaces erhalten. Aufgrund der notwendigen Anpassungen unterWindows spricht man hier nicht mehr von den Sockets, sondern von den Win-Socks oder dem WinSock-API.

14.1.2 TCP, UDP und IP

Das WinSock-API ist eine in seinem Kern standardisierte Funktionssammlung mitmehr als 40 Funktionen und somit die gängigen Internetprotokolle TCP/IP undUDP/IP abdeckt, die die Grundlage sämtlichen Datenaustausches bilden – sowohlim Internet als auch sehr häufig in lokalen Netzwerken. Andere Protokolle habenkeine wesentliche Bedeutung mehr und sind fast schon als Exoten anzusehen.

TCP/IP bzw. UDP/IP beschreiben jeweils zwei Protokolle, deren binärer Aufbausehr komplex ist und deren Aufgaben unterschiedlich definiert sind:

� IP (Internet Protocol) ist das Netzwerkprotokoll, das für den Transport derDaten von einem Sender zu einem Empfänger verantwortlich ist.

� TCP (Transmission Control Protocol) und UDP (User Datagram Protocol)beschreiben die Behandlung der Datenpakete, die bei der Sitzung zwischenden beiden Kommunikationspartnern ausgetauscht werden.

Man kann sich die Sockets-Funktionen als eine Art Kapselung des TCP/UDP/IP-Protokolls vorstellen. Durch Aufruf der entsprechenden Funktion des Sockets-APIkann ein Client einen bestimmten Server anrufen, der seinerseits den Client von

VBNET-14.fm Seite 878 Mittwoch, 27. März 2002 5:17 17

Page 4: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

Ein paar fundamentale Netzwerkgrundlagen 879

der erfolgreichen Kontaktierung unterrichtet. Letzteres übrigens nur, wenn dieVerbindung über TCP aufgebaut wird.

Dabei spielt es keine Rolle, ob zwei Anwendungen, die sich auf einem lokalenRechner befinden, die WinSock-Schnittstelle benutzen, um miteinander Datenauszutauschen, oder ob die Rechner durch eine große Entfernung getrennt sind.

Das IP-Protokoll

Der Datenaustausch zwischen zwei Kommunikationspartner im Netzwerk kannnur dann erfolgen, wenn beide über eine innerhalb des Netzwerks eindeutigeAdresse erreichbar sind. Das ist im täglichen Leben auch nicht anders, wenn Sieetwa einen Brief verschicken. Die Adresse in IP-Netzen wird durch eine 32-Bitlangen Zahl beschrieben, die als IP-Adresse bezeichnet wird. Eine typischeIP-Adresse wäre beispielsweise 192.169.100.12.

Das größte aller Netzwerke ist das Internet. Innerhalb des Internets müssennatürlich auch alle IP-Adressen eindeutig sein. Um dies sicher zu stellen, gibt eseine zentrale Institution zur Vergabe von Internetadressen und -namen: das Net-work Information Center (NIC). Obwohl rein theoretisch ungefähr 4 Milliarden IP-Adressen vergeben werden können, reicht das Kontingent bereits nicht mehr aus.In naher Zukunft wird deshalb ein neuer Adressierungsstandard eingeführt, IPv6(IP-Version 6), das sogar 128-Bit-Adressen vorsieht. Damit könnte jeder Erdbe-wohner eine eigene, statische IP-Adresse bekommen – ein verlockenderGedanke.

Domain-Namen

Obwohl manche Menschen ein sehr gutes Zahlengedächtnis haben und sich eineTelefonnummer für lange Zeit merken können, fällt das den meisten doch ziem-lich schwer. Das gilt gleichermaßen auch für die IP-Adressen, mit denen ein Com-puter umgeht. Um die Handhabung zu vereinfachen, wurde daher das DomainName System (DNS) eingeführt, das einer IP-Adresse einen Namen zuordnet.www.microsoft.com ist ein Beispiel dafür.

Das Domain Name System kommt uns auch in der Programmierung zugute. Wirkönnen entweder die IP-Adresse oder den bezeichnenden DNS-Namen verwen-den. Letzterer wird von einem Name-Server in die passende IP-Adresse übersetzt,bevor die Verbindung aufgebaut wird. Einem DNS-Namen kann immer nur eineIP-Adresse zugeordnet werden, während eine IP-Adresse durch mehrere symbo-lische Namen beschrieben werden kann.

VBNET-14.fm Seite 879 Mittwoch, 27. März 2002 5:17 17

Page 5: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

880 TCP/IP-Programmierung

Das TCP-Protokoll

TCP ist ein verbindungsorientiertes Protokoll, das auf Basis von IP eine sicherePunkt-zu-Punkt-Verbindung zwischen zwei Partnern ermöglicht. Demgegenüberist UDP ein verbindungsloses Protokoll. Bei einer TCP-Verbindung wird sicherge-stellt, dass keine Datenpakete verloren gehen und die richtige Reihenfolge derDatenpakete gewährleistet bleibt. Aus diesem Grund werden zur Absicherungund Nachverfolgung der Kommunikation Datenpakete zwischen den beidenbeteiligten Partnern hin- und her geschickt. Das kostet natürlich Leistung; dieeffektiven Datenübertragungsraten sind vergleichsweise niedrig.

Das UDP-Protokoll

UDP hat diesen Nachteil nicht und eignet sich insbesondere bei Verbindungen,die eine hohe Datenübertragungsrate erfordern. Dafür muss aber ein Preis gezahltwerden, denn es könnte ein Datenpaket im Netz verloren gehen oder die Paketenicht in der richtigen Reihenfolge eintreffen.

14.1.3 Das Client-Server-Prinzip

Internet-Software arbeitet nach dem Client-Server-Prinzip. Dahinter verbirgt sichder Ansatz, die Funktionalität in zwei Komponenten aufzuteilen:

� Der Client-Software, die von einem Anwender benutzt wird, um auf dieDienste eines Servers in Anspruch zu nehmen.

� Der Server-Software, die bestimmte Funktionalitäten, man spricht auch vonDiensten, den zugreifenden Clients bereitstellt.

Client und Server bauen eine Verbindung über TCP/IP bzw. UDP/IP auf. Die Kom-munikation erfolgt aber über Protokolle, die auf einer Schicht oberhalb von IPund TCP/UDP liegen. Ein Web-Browser und ein Web-Server sprechen die Spra-che des HTTP-Protokolls, ein Mail-Client unterhält sich mit einem Mail-Server viaSMTP. Es gibt noch eine große Zahl weiterer Protokolle, die alle einen bestimmtenKommunikationsablauf definieren.

Protokolle lassen sich grundsätzlich in zwei Gruppen aufteilen:

� Protokolle mit Binärstrukturen

� Protokolle auf ASCII-Basis

TCP und UDP sind Binärprotokolle; HTTP, SMTP, POP3, FTP wiederum usw. sindreine ASCII-Protokolle.

VBNET-14.fm Seite 880 Mittwoch, 27. März 2002 5:17 17

Page 6: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

Ein paar fundamentale Netzwerkgrundlagen 881

Abbildung 14.1 Auszug aus dem Protokoll-Stack

Die Tatsache, dass Protokolle ASCII-Zeichen als Grundlage einer Kommunikationfestgelegt haben, können sich Entwickler auf einfache Art und Weise zunutzemachen. Ist nämlich einmal die Verbindung zu einem Dienstserver über dieSockets aufgebaut, werden einfach nur noch ASCII-Sequenzen zwischen Clientund Server ausgetauscht.

Port-Nummern

Auf der bestehenden TCP/IP-Verbindung eines Hosts laufen meist mehrereunterschiedliche Serveranwendungen. Aus Erfahrung wissen Sie, dass sowohl einMail-Client als auch ein Web-Browser gleichzeitig laufen können. Beide basierennatürlich auf TCP/IP, benutzen demnach auch dieselbe IP-Adresse. Damit trittein Problem auf: Die eingehenden Daten müssen bei der richtigen Anwendunglanden. Um dies sicherstellen zu können, gibt es ein weiteres Adressierungs-merkmal: die Port-Nummer.

Mit jedem Serverprogramm ist eine bestimmte Port-Nummer verknüpft. Bei denStandardanwendungen sind das immer dieselben. Ein HTTP-Server läuft beispiels-weise immer an Port 80, ein SMTP-Server an Port 25. Abhängig von den Installa-tionen auf einem Rechner, sind viele Port-Nummern fest vergeben. Unter Win-dows 2000 finden Sie im Verzeichnis \winnt\system32\drivers\etc die Dateiservices, in der alle fest vergebenen Port-Nummern unter Angabe des Protokollsaufgeführt sind. Insgesamt stehen Port-Nummern im Bereich von 0 bis 65535 zurVerfügung.

IP

TCP UDP

HTTP SMTP FTP TFTP

VBNET-14.fm Seite 881 Mittwoch, 27. März 2002 5:17 17

Page 7: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

882 TCP/IP-Programmierung

14.1.4 Zusammenfassung

� Das Socket-API ist eine Schnittstelle mit ca. 40 Funktionen. Es ermöglicht dieKommunikation zweier Anwendungen, die entweder lokal oder auf einem ent-fernten Rechner installiert sind.

� IP ist das Netzwerkprotokoll, das für den Transport der Daten von einem Sen-der zu einem Empfänger verantwortlich ist. TCP und UDP beschreiben, wie diezwischen den beiden Kommunikationspartnern ausgetauschten Datenpaketebehandelt werden. TCP stellt sicher, dass alle Pakete richtig beim Empfängerankommen, während UDP das nicht gewährleisten kann, dafür aber eine imVergleich zu TCP höhere Datenübertragungsrate hat.

� Um sicher zu stellen, dass ein Datenpaket nicht nur beim richtigen Empfänger,sondern dort auch von einer bestimmten Anwendung entgegengenommenwird, ist jeder TCP/IP-Anwendung ein Port zugeordnet. Eine eindeutigeAdresse setzt sich immer aus einer 4 Byte grossen IP-Adresse und der Port-Nummer zusammen.

� TCP, UDP und IP sind Protokolle, deren Daten binär strukturiert sind. Die meis-ten der darauf aufsattelnden Protokolle bedienen sich eines einfachen ASCII-Befehlssatzes.

VBNET-14.fm Seite 882 Mittwoch, 27. März 2002 5:17 17

Page 8: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

Netzwerkprogrammierung mit dem .NET-Framework 883

14.2 Netzwerkprogrammierung mit dem .NET-Framework

Jetzt haben Sie die wichtigsten Begriffe kennen gelernt, mit denen wir im Folgen-den permanent zu tun haben. Wir können uns nach dieser theoretischen Einfüh-rung endlich wieder der Programmierung zuwenden, um selber die Kontrolleüber eine TCP/IP-Verbindung zu erhalten und sie zu nutzen. Die .NET-Klassen-bibliothek stellt dazu – wie kaum anders zu erwarten – zahlreiche Klassen zur Ver-fügung, die Funktionalitäten zur Netzwerkprogrammierung anbieten. Die wich-tigsten und grundlegendsten sind in den Namespaces System.Net undSystem.Net.Sockets zu finden. Daneben sind in System.Web und weiteren,untergeordneten Namespaces Klassen organisiert, die spezielleren Aufgaben die-nen.

14.2.1 Einfacher Verbindungsaufbau

Die gesamte Kommunikation zwischen einem Client und einem Server wird überSockets abgewickelt. Bevor es jedoch zu einem Datenaustausch kommt, musseine Verbindung zwischen diesen beiden Partnern aufgebaut werden. Im .NET-Framework sind einige Klassen zu finden, die prinzipiell dazu benutzt werdenkönnen. Die fundamentalste ist System.Net.Sockets.Socket, deren Methodendie Funktionen der WinSock-API kapseln und auf einfache Art und Weise zugäng-lich machen. Alle anderen Klassen des Namespaces System.Net setzen auf dieseKlasse auf.

Wir wollen uns nun an einem Beispiel ansehen, wie mittels der Socket-Klasseeine Verbindung zwischen zwei Kommunikationspartnern aufgebaut wird. Dazuentwickeln wir sowohl eine Server- als auch eine Clientanwendung.

Die Datenübertragung über eine Socketverbindung ähnelt einem Dateizugriff:

� Aufbau der Verbindung eines Clients zu einem bestimmten Endpunkt im Netz-werk

� Datenaustausch zwischen dem Client und dem Server

� Beenden der Verbindung

Um die Sockets zu programmieren, sind nicht unbedingt zwei Rechner in einemlokalen Netzwerk oder der Anschluss an das Internet notwendig. Sie können denAblauf der Kommunikation zwischen Client und Server auch auf einer Maschinetesten. Die einzige Bedingung, die daran geknüpft ist, ist die Installation des Pro-tokolls TCP/IP.

Die klare Unterscheidung zwischen Client und Server gilt streng genommen nurfür den Verbindungsaufbau. Wenn ein Client versucht, Kontakt zu einem Server

VBNET-14.fm Seite 883 Mittwoch, 27. März 2002 5:17 17

Page 9: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

884 TCP/IP-Programmierung

aufzunehmen, muss die Serveranwendung bereits gestartet sein. Der Server istunter einer bestimmten IP-Adresse erreichbar; doch wir haben oben gesehen,dass das nicht ganz ausreichend ist: Der Anwendung ist auch ein bestimmter Portzugeordnet, an dem die Anwendung lauscht, um das Verbindungsgesuch des Cli-ents entgegen zu nehmen. Ist die Verbindung aufgebaut, ist die Rollenverteilungzwischen Client und Server nicht mehr eindeutig gegeben. Der ursprünglicheClient kann dem ursprünglichen Server ebenso Daten zukommen lassen wieumgekehrt. Mit anderen Worten: Nach dem Aufbau der Verbindung sind beideabstrakt gesehen in derselben Position und haben grundsätzlich dieselben Fähig-keiten. Geregelt wird die Kommunikation nur noch durch ein Kommunikations-protokoll – wenn ein solches definiert ist.

Die Serveranwendung

Eine Socketverbindung ist die Punkt-zu-Punkt-Verbindung von zwei Anwendun-gen, an derem sowohl server- als auch clientseitigen Ende der Verbindungskanalzum Partner durch eine Instanz der Klasse Socket repräsentiert wird.

Um eine Socket-Instanz zu erhalten, steht nur ein Konstruktor zur Verfügung:

Die Parameter ermöglichen, die unterschiedlichsten Sockets bereitzustellen. Alledrei Typen beschrieben Enumerationen, aus denen wir die richtige Auswahl tref-fen müssen. Beabsichtigen wir, auf Basis von TCP/IP einen Kommunikationskanalöffnen zu wollen, müssen wir uns wie folgt ein Socket-Objekt besorgen:

Damit haben wir einen TCP/IP-Socket beschrieben, dem im Moment weder einekonkrete IP-Adresse der Maschine noch eine Port-Nummer zugeteilt ist.

Ein Socket beschreibt einen Kanal, an dessen Enden sich zwei durch eine IP-Adresse eindeutig identifizierbare Kommunikationspartner befinden. Das Socketeiner Serveranwendung kann beim Start noch nicht wissen, von welchem Clientes kontaktiert wird, aber es muss sich über eine IP-Adresse und der Port-Nummereindeutig identifizieren lassen. Mit der Bind-Methode eines Sockets werden diesebeiden Daten dem Socket bekannt gegeben. Dazu muss man als Argument dieReferenz auf ein Objekt vom Typ EndPoint übergeben:

Public Sub New(AddressFamily, SocketType, ProtocolType)

Dim server As New Socket(AddressFamily.InterNetwork, _ SocketType.Stream, ProtocolType.IP)

Public Sub Bind(EndPoint)

VBNET-14.fm Seite 884 Mittwoch, 27. März 2002 5:17 17

Page 10: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

Netzwerkprogrammierung mit dem .NET-Framework 885

Die Klasse EndPoint ist abstrakt definiert und hat derzeit nur eine Subklasse:IPEndPoint. Ein Blick in die Dokumentation verrät uns, dass ein Objekt des TypsIPEndPoint die Eigenschaften Address und Port offen legt – genau die Informati-onen, die wir zur endgültigen Beschreibung unseres Sockets noch benötigen.

Da wir nun wissen, dass wir ein Objekt vom Typ IPEndPoint benötigen, sehen wiruns die Definition des Konstruktors dieser Klasse an:

Dem ersten Parameter wird in Form einer Referenz vom Typ IPAddress die IP-Adresse übergeben, dem zweiten Parameter als Integer die Port-Nummer. DerTyp des zweiten Parameters macht uns sicherlich kein Problem, nur der ersteerzwingt ein weiteres Abtauchen in die .NET-Klassenbibliothek.

Halten wir an dieser Stelle noch einmal fest: Wir haben uns zum Ziel gesetzt, eineServeranwendung zu entwickeln, der wir ein Socket zuordnen wollen, das Client-anfragen entgegen nimmt. Der Rechner, auf dem die Anwendung installiert wird,hat einen Namen und eine IP-Adresse im Netzwerk, die wir dem Socket mitteilenmüssen. Wir stehen jetzt vor zwei Alternativen:

� Wir zwingen beim Start des Serverprogramms den Anwender dazu, zuerstmanuell die IP-Adresse anzugeben und werten die Eingabe aus, oder

� wir ermitteln mittels Programmcode automatisch die IP-Adresse.

Die manuelle Eingabe ist natürlich wenig komfortabel und sollte daher normaler-weise ausscheiden. Widmen wir uns daher der zweiten Alternative.

Die einem Rechner zugeordnete IP-Adresse kann entweder statisch oder dyna-misch sein. Ist sie statisch, können wir sie noch relativ einfach durch ein Objektvom Typ IPAddress beschreiben. Dazu rufen wir die statische Methode Parse derKlasse IPAddress auf und übergeben die IP-Adresse als String. Der Rückgabewertder Methode ist die Referenz auf ein IPAddress-Objekt. Nehmen wir an, dassdem Serverrechner die Adresse 150.100.108.12 zugeteilt ist und die Serveranwen-dung mit dem Port 930 verknüpft werden soll, dann würde der komplette Codezur Anbindung des Sockets an diese Informationen wie folgt lauten:

Public Sub New(IPAddress, Integer)

Dim server As New Socket(AddressFamily.InterNetwork, _ SocketType.Stream, ProtocolType.IP)Dim ipAddress As IPAddress = ipAddress.Parse("150.100.108.12")Dim localEP As New IPEndPoint(ipAddress, 930)server.Bind(localEP)

VBNET-14.fm Seite 885 Mittwoch, 27. März 2002 5:17 17

Page 11: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

886 TCP/IP-Programmierung

Flexibel ist diese Lösung nicht, denn die IP-Adresse wird als unveränderlich ange-nommen und in den Programmcode kompiliert. Da IP-Adressen häufig dynamischvergeben werden, spiegelt das Codefragment nur unzureichend die Tatsachen desAlltags wider und dürfte nur in den seltensten Fällen den Anforderungen genü-gen.

Eine optimale Softwarelösung berücksichtigt eine dynamische Adresszuordnung.Dieser Ansatz ist komplexer als der vorherige und setzt sich aus mehreren Schrit-ten zusammen:

� Bestimmung des Rechnernamens, auf dem die Serveranwendung läuft;

� Da einem Rechner mehrere IP-Adressen zugeordnet sein können, muss mansich die Liste aller IP-Adressen besorgen: Meistens enthält diese Liste nur eineeinzige IP-Adresse, die wir für das weitere Vorgehen nutzen können.

� Die IP-Adresse wird mit der Port-Nummer verknüpft und anschließend an dasSocket gebunden.

Um den aktuellen Rechnernamen und die der Maschine zugeordneten IP-Adres-sen zu erfahren, bietet das .NET-Framework wieder eine eigene Klasse an: Sys-tem.Net.Dns. Da alle Methoden dieser Klasse statisch sind, benötigen wir keineInstanz.

Mit

erfahren wir den Namen des Rechners, der als String zurückgeliefert wird. Nunmüssen wir den Rückgabewert noch in die IP-Adressen auflösen. Das leistet dieMethode Resolve der Klasse Dns, die als Parameter einen String mit dem Namendes Rechners erwartet:

Die gesamte Anweisung zur Auflösung in die IP-Adressen lautet demnach wiefolgt:

Durch den Rückgabewert vom Typ IPHostEntry wird die oben erwähnte Auflis-tung beschrieben, die alle der Maschine zugeordneten IP-Adressen enthält. Damitsind wir nahezu am Ziel, wir müssen jetzt nur noch die Eigenschaft AddressListder Referenz auf das IPHostEntry-Objekt auswerten, die einen Array des TypsIPAddress beschreibt:

Dns.GetHostName()

Public Shared Function Resolve(String) As IPHostEntry

Dim ipHost As IPHostEntry = Dns.Resolve(Dns.GetHostName())

VBNET-14.fm Seite 886 Mittwoch, 27. März 2002 5:17 17

Page 12: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

Netzwerkprogrammierung mit dem .NET-Framework 887

Ist nur eine IP-Adresse mit dem Rechner verknüpft, ist diese unter dem Index 0anzusprechen. Diese wird nun als Objekt der Methode Bind des Sockets über-geben.

Das hört sich bis hierher alles ziemlich kompliziert an. Sicherlich ist unbestreitbar,dass der Weg zum Ziel über viele Klassen führt. Es ist aber nicht ungewöhnlich,sich innerhalb einer Klassenbibliothek die Informationen schrittweise zu besorgenund die Einzelschritte am Ende zu einem Komplex zusammenzufassen. Der Vor-teil liegt allerdings auch auf der Hand: Uns bieten sich mannigfaltige Möglichkei-ten, abseits des »normalen« Weges aus dem Geflecht der Klassen und ihrerMethoden die Lösung für eine sehr spezialisierte Anwendung zu finden.

Fassen wir nun noch den gesamten Code zusammen, um ein Socket basierend aufeiner dynamischen IP-Adresse an einen bestimmten Port zu binden:

Die Aufgabe des von uns konfigurierten Sockets der Serveranwendung ist, amPort 930 auf eingehende Clientanfragen zu lauschen. Wir hätten natürlich aucheine andere Port-Nummer wählen können, solange diese noch nicht von eineranderen Anwendung oder einer anderen Verbindung reserviert oder belegt wird.

Ein Socket konfiguriert zu haben, bedeutet noch nicht, dass der Socket aktiviertist – er verharrt noch immer im Ruhezustand. Erst durch Aufruf der MethodeListen auf die Socket-Referenz wird das Socket geöffnet und kann auf einge-hende Clientanrufe reagieren:

Es ist möglich, dass mehrere Clientanrufe quasigleichzeitig am geöffneten Portauflaufen. Zu einem gegebenen Zeitpunkt kann ein Socket aber nur ein Anrufbearbeiten. Daher werden alle weiteren Gesuche in eine Warteschlange gestellt.Die Größe der Warteschlange beschreibt der Parameter der Listen-Methode. Inunseren Codefragment lassen wir zehn gleichzeitige Anrufe zu, die der Reihe nachaus der Warteschlange geholt werden, wenn ein Verbindungsgesuch bearbeitetist. Ist die Warteschlange allerdings voll und versucht ein weiterer, in unserem

Dim ipAdress As IPAddress = ipHostInfo.AddressList(0)

Dim server As New Socket(AddressFamily.InterNetwork, _ SocketType.Stream, ProtocolType.IP)Dim ipHostInfo As IPHostEntry = Dns.Resolve(Dns.GetHostName())Dim ipAdress As IPAddress = ipHostInfo.AddressList(0)Dim localEP As New IPEndPoint(ipAdress, 930)server.Bind(localEP)

server.Listen(10)

VBNET-14.fm Seite 887 Mittwoch, 27. März 2002 5:17 17

Page 13: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

888 TCP/IP-Programmierung

Beispiel elfter Client das Socket in Anspruch zu nehmen, gibt der Server dem Cli-ent eine Fehlermeldung zurück und teilt ihm dadurch mit, dass er ausgelastet ist.

Wenn ein Verbindungsgesuch eintrifft, wird auf das lauschende Socket dieMethode Accept aufgerufen, die ein neues Socket-Objekt zurückgibt.

Es welchem Grund wird aber eine weitere Socket-Instanz erstellt?

Auf einem Rechner mit TCP/IP-Unterstützung können gleichzeitig mehrere Ver-bindungen mit Clients abgewickelt werden, sowohl innerhalb einer einzigenInternet-Anwendung als auch im Rahmen mehrerer, parallel laufender. Ein Socketbeschreibt den Endpunkt einer eindeutig definierten Client-Server-Verbindung.Wenn innerhalb einer Anwendung mehrere verschiedene Clients bedient wer-den, muss es demnach für jede aktive Verbindung ein Socket geben, das sowohllokale Informationen als auch die Angabe der IP-Adresse und der Portnummerder entfernten (remote) Gegenstelle enthält. Erst damit ist es möglich, ein IP-Datenpaket zu einer eindeutigen Zieladresse zu schicken.

Das Schließen eines Sockets

Ist die Kommunikation zwischen einem Client und einem Server beendet, solltedas jeweilige Socket mit der Methode Close geschlossen werden, um die vomSocket beanspruchten Ressourcen freizugeben.

Es könnte sein, dass ein Socket geschlossen wird, während noch Daten gesendetoder empfangen werden. Daher ist es empfehlenswert, vor dem Aufruf von Closemit der Methode ShutDown einen eingeleiteten Vorgang korrekt abzuschließenund nachfolgend eingehende Verbindungsgesuche nicht mehr zuzulassen. Dazuwird der Methode ShutDown eine Konstante aus der Enumeration SocketShut-Down übergeben:

� SocketShutDown.Send

� SocketShutDown.Receive

� SocketShutDown.Both

Public Function Accept() As Socket

Der Methode Accept kommt nicht nur die Aufgabe zu, bei einem Verbin-dungsgesuch ein neues Socket zu erzeugen, über das zukünftig die gesamteKommunikation zwischen den beiden Partner abgewickelt wird. Beinahegenauso wichtig ist die Fähigkeit von Accept, den aktuellen Thread des Ser-vers in einen Wartezustand zu versetzen. Erst wenn ein Client das lauschendeSocket anspricht, wird der Thread wieder aktiviert.

VBNET-14.fm Seite 888 Mittwoch, 27. März 2002 5:17 17

Page 14: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

Netzwerkprogrammierung mit dem .NET-Framework 889

Meistens wird es sich anbieten, den Member Both anzugeben. ShutDown bewirktnicht das Schließen des Sockets, sondern hat nur Einfluss auf das Kommunika-tionsverhalten – Close ist daher immer anzugeben.

Der vollständige Servercode

Wir wollen nun eine komplette funktionsfähige Serveranwendung entwickeln,die in der Lage ist, uns über ein erfolgreiches Verbindungsgesuch in Kenntnis zusetzen. Den dazu passenden Client werden wir im nächsten Abschnitt entwickeln.Schauen wir uns zuerst den Code an:

'----------------------------------------------------'Codebeispiel:...\Beispielcode\Kapitel_14\TCPServer_1'----------------------------------------------------Imports System.Net.SocketsImports System.NetModule Module1 Sub Main() Dim server As New Socket(AddressFamily.InterNetwork, _ SocketType.Stream, ProtocolType.IP) Dim clientSocket As Socket Dim ipHostInfo As IPHostEntry = Dns.Resolve(Dns.GetHostName()) Dim ipAdress As IPAddress = ipHostInfo.AddressList(0) Dim localEP As New IPEndPoint(ipAdress, 930) server.Bind(localEP) Try 'Socket aktivieren und auf Clientanruf warten server.Listen(1) Console.WriteLine("Ich warte auf einen Client...") 'Clientanfrage akzeptieren und einen clientspezifischen 'Socket anlegen clientSocket = server.Accept() Console.WriteLine(".....Verbindungsgesuch des Clients gelungen.") 'Ausgabe der Verbindungsdaten zum Client Dim port As Int32 = CType(clientSocket.RemoteEndPoint, _ IPEndPoint).Port Dim tcpip As String = CType(clientSocket.RemoteEndPoint, _ IPEndPoint).Address.ToString() Console.WriteLine("Remote Daten: {0}/Port {1}", tcpip, port) port = CType(clientSocket.LocalEndPoint, IPEndPoint).Port tcpip = CType(clientSocket.LocalEndPoint, _ IPEndPoint).Address.ToString() Console.WriteLine("Lokale Daten: {0}/Port {1}", tcpip, port) Catch e As Exception

VBNET-14.fm Seite 889 Mittwoch, 27. März 2002 5:17 17

Page 15: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

890 TCP/IP-Programmierung

Unsere TCP/IP-Serveranwendung ist noch verhältnismäßig dumm – aber sie funk-tioniert bereits, wie Sie später noch sehen werden. Sie kann nicht mehr als genauein Verbindungsgesuch entgegennehmen und die Daten der Gegenstelle an derKonsole ausgeben. Außerdem ist sie nur in der Lage, das Verbindungsgesucheines Clients entgegenzunehmen.

Zur Ausgabe der Verbindungsdaten werden die Eigenschaften LocalEndPoint undRemoteEndPoint des Verbindungssockets ausgewertet. Diese sind zunächst in denTyp IPEndPoint zu konvertieren, dessen Eigenschaften Address und Port diegewünschten Informationen liefern.

Der gesamte Code zur Verbindungsaufnahme wird innerhalb einer Ausnahmebe-handlung ausgeführt, um bei etwaig auftretenden Laufzeitfehlern die Fortsetzungdes Programms zu garantieren.

Die Clientanwendung

Damit ein TCP/IP-Client die Verbindung zu einem ihm bekannten Server aufneh-men kann, muss der Server bereits gestartet sein und der Dienst am festgelegtenPort lauschen.

Der Client ersucht den Server um eine Verbindung, indem zuerst ebenfalls einObjekt vom Typ Socket erstellt wird:

Dieser Socket wird dazu benutzt, sich bei einem ganz bestimmten Port eines ganzbestimmten Servers als verbindungswillig anzumelden. Nach der Initialisierungdes Sockets kann sofort die Methode Connect des Sockets aufgerufen werden, derals Parameter dieselben Informationen übergeben werden, wie der Bind-Methode aufseiten des Servers.

Console.WriteLine(e.ToString()) Finally clientSocket.Close() End Try Console.ReadLine() End SubEnd Module

port = CType(clientSocket.LocalEndPoint, IPEndPoint).Porttcpip = CType(clientSocket.LocalEndPoint, _ IPEndPoint).Address.ToString()

Dim server As New Socket(AddressFamily.InterNetwork, _ SocketType.Stream, ProtocolType.Tcp)

VBNET-14.fm Seite 890 Mittwoch, 27. März 2002 5:17 17

Page 16: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

Netzwerkprogrammierung mit dem .NET-Framework 891

Die Daten der EndPoint-Referenz besorgen wir uns auf ähnliche Weise, wieschon zuvor beim Server:

Der Methode Resolve übergeben wir einen Domain-Namen. Im einfachsten Fallist das der Name eines Rechners, im Beispielcode oben WSAKL. Bauen Sie dieVerbindung zu einem Computer im Internet auf, werden Sie an dieser Stelle einenDomain-Namen übergeben. Über die Adressliste der IPHostEntry-Referenzerhalten wir wiederum eine IP-Adresse, diesmal die des Servers, die wir unterErgänzung der Angabe des zu kontaktierenden Server-Ports zur Initialisierung desIPEndPoint-Objekts benutzen.

Nun sind wir in der Lage, mit

unsere Verbindungsgesuch abzusetzen. Connect arbeitet ähnlich wie Accept undversetzt den ausführenden Thread in den Wartezustand. Wird das Verbindungs-gesuch vom Server entgegengenommen und bestätigt, wird der Code in der Zeilefortgesetzt, die Connect folgt. Ansonsten wird ein Fehler ausgelöst – entwederweil unter der übergebenen IP-Adresse kein Server im Netz gefunden wird oderweil unter der angegebenen Port-Nummer kein Dienst läuft.

Sehen wir uns nun wieder den gesamten Code des Clients an, der nur noch umdie Ausgabe der entfernten Endpunktdaten an der Konsole ergänzt ist.

Public Sub Connect(EndPoint)

Dim ipHost As IPHostEntry = Dns.Resolve("WSAKL")Dim serverAddress As IPAddress = ipHost.AddressList(0)Dim EP As New IPEndPoint(serverAddress, 930)

server.Connect(EP)

'----------------------------------------------------'Codebeispiel:...\Beispielcode\Kapitel_14\TCPClient_1'----------------------------------------------------Imports System.Net.SocketsImports System.Net

Module Module1 Sub Main() Console.Write("Zur Verbindungsaufnahme <ENTER> drücken: ") Console.ReadLine() Dim server As New Socket(AddressFamily.InterNetwork, _ SocketType.Stream, ProtocolType.Tcp) 'Serverendpunkt beschreiben Dim ipHost As IPHostEntry = Dns.Resolve("WSAKL")

VBNET-14.fm Seite 891 Mittwoch, 27. März 2002 5:17 17

Page 17: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

892 TCP/IP-Programmierung

Sie können nun beide Anwendungen testen, und brauchen dazu nicht einmal eineigenes Netzwerk. Die einzige Änderung, die Sie höchstwahrscheinlich vorneh-men müssen, ist die Übergabe des Rechnernamens an die Methode Resolve imClientcode. Tragen Sie hier den Namen Ihres Rechners ein.

Starten Sie zuerst den Server und danach die Clientanwendung. Die Clientanwen-dung fordert den Anwender an der Konsole dazu auf, durch Drücken der (¢)-Taste die Verbindung zum laufenden Server aufzubauen. Haben Sie keinen Fehlergemacht, werden Sie in den beiden Konsolenfenstern die jeweiligen Angaben derGegenstelle erhalten. Beobachten Sie dabei auch, wie die Accept-Methode desServersockets wartet, bis sich der Client anmeldet.

14.2.2 Der Datenaustausch zwischen Client und Server

Ist die Verbindung zwischen zwei Partnern aufgebaut, kann einer der beidenbeginnen, Daten an den anderen zu senden. Dabei verwischen oft die klarenAbgrenzungen zwischen einem Client und einem Server. Grundsätzlich sind beidein der Lage, Nachrichten zu senden oder zu empfangen.

Gesendet wird mit der überladenen Send-Methode. Eine Begrenzung der Größe desDatenstroms, der via TCP geschickt wird, gibt es nicht, da der zu versendendeDatenstrom automatisch auf mehrere Pakete verteilt wird – falls dies erforderlich ist.

Dim serverAddress As IPAddress = ipHost.AddressList(0) Dim EP As New IPEndPoint(serverAddress, 930) Try 'Verbindung zum TCP-Server aufnehmen server.Connect(EP) Dim port As Int32 = CType(server.RemoteEndPoint, _ IPEndPoint).Port Dim tcpip As String = CType(server.RemoteEndPoint, _ IPEndPoint).Address.ToString() 'Ausgabe der Daten des Server-IP-Punkts Console.WriteLine("Verbindung zu {0}/Port {1} ist aufgebaut.", _ tcpip, port) Catch e As Exception Console.WriteLine(e.ToString()) Finally server.Close() End Try Console.ReadLine() End SubEnd Module

Public Function Send(Byte()) As Integer

VBNET-14.fm Seite 892 Mittwoch, 27. März 2002 5:17 17

Page 18: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

Netzwerkprogrammierung mit dem .NET-Framework 893

Empfangene Daten lassen sich im Gegenzug mit der Methode Receive in Emp-fang nehmen.

Die eingehenden Daten werden in einen Puffer geschrieben. Beim Aufruf derReceive-Methode muss bereits festgelegt werden, wie groß der Puffer ist, obwohlman noch keine Kenntnis davon hat, wie viel Bytes die Datenquelle verschickt.Receive wartet, bis der Puffer gefüllt ist und liefert dann die Zeichen ab. Die Zei-chen, die der Puffer nicht aufnehmen konnte, werden erst beim nächsten Aufrufvon Receive zurückgeliefert.

Wir haben ein paar Seiten vorher die Methode Accept kennen gelernt und dabeigesehen, wie sie den laufenden Thread solange blockiert, bis das Verbindungsge-such einer Gegenstelle eintrifft. Sehr ähnlich arbeitet die Receive-Methode, diedas Socket solange blockiert, bis von der Gegenstelle Daten eintreffen. Solangeein Socket blockiert, verweigert er jeglichen weiteren Dienst, was unter Umstän-den nicht wünschenswert ist. Wenn man beispielsweise nur sporadisch wissenmöchte, ob der Partner Daten geschickt hat, verhindert das blockierende Socket,selbst Daten zu schicken.

Um das standardmäßige Blocken aufzuheben, bietet sich die Eigenschaft Blockingdes Sockets an. Unterbindet man mit

das Blockieren und ruft die Receive-Methode auf, trifft diese auf einen leeren Puf-fer und löst eine Exception aus. Das Programm kann die Programmausführungdanach wie gewohnt fortsetzen, beispielsweise um Daten an die Gegenstelle zuschicken.

Umwandeln einer Zeichenfolge in ein Byte-Array

Die Methoden Send und Receive senden und verschicken die Daten in Form einesByte-Arrays. Bevor wir uns dem ersten Beispiel einer Datenübermittlung widmen,müssen wir noch klären, wie wir eine ASCII-Zeichenfolge in ein Byte-Array schrei-ben bzw. aus einem Byte-Array die Originaldaten zurückerhalten.

Hier unterstützt uns die Klasse Encoding aus dem Namespace System.Text. Mit

legen wir zuerst den Zeichencode fest. Encoding stellt uns dazu alle erforderli-chen Eigenschaften zur Verfügung, die durchweg statisch definiert sind. Der

Public Function Receive(Byte()) as Integer

mySocket.Blocking = False

Encoding.<Zeichencode>

VBNET-14.fm Seite 893 Mittwoch, 27. März 2002 5:17 17

Page 19: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

894 TCP/IP-Programmierung

Rückgabetyp ist ein konkretes Encoding-Objekt, auf das wir Methoden aufrufenkönnen, um in das gewünschte Format zu konvertieren: Mit GetBytes konvertie-ren wir die Zeichenfolge in ein Byte-Array, mit GetString ein Byte-Array zurück ineine Zeichenfolge.

Beide Methoden sind überladen. Während sich GetBytes in der gezeigten Vari-ante sofort anbietet, würde GetString den gesamten Puffer einlesen (die Größedes Puffers müssen Sie vor dem Einlesen der zu empfangenden Daten festlegen).Da unter Umständen weniger Daten gesendet werden, als der Puffer aufzuneh-men vermag, können wir das Auslesen auf die tatsächlich empfangenen Dateneinschränken, wenn wir uns der folgenden Variante der überladenen GetString-Methode bedienen:

Dem zweiten Parameter übergibt man die Indexposition aus dem Byte-Array, abder umgewandelt werden soll. Normalerweise wird man ab der ersten Positionlesen wollen, daher dürfte diesem Parameter meist die Zahl 0 übergeben werden.Dem dritten Parameter teilen wir mit, wie viele Byte wir aus dem Array lesen wol-len. Jetzt hilft uns der Rückgabewert der Receive-Methode weiter, der uns diekorrekte Anzahl der übermittelten Bytes mitteilt.

Die Server- und Clientanwendung

Wir wollen uns nun, aufbauend auf dem Code der Beispiele TCPServer_1 undTCPClient_1, einen primitiven Datenaustausch ansehen. Beide Partner senden nurjeweils eine Zeichenfolge, die an der Konsole der Gegenstelle ausgegeben wird.

Public Function GetBytes(String) As Byte()Public Function GetString(Byte()) As String

Public Function GetString(Byte(), Int32, Int32) As String

'----------------------------------------------------'Codebeispiel:...\Beispielcode\Kapitel_14\TCPServer_2'----------------------------------------------------Imports System.Net.SocketsImports System.NetImports System.Text

Module Module1 Sub Main() Dim bytes(1024) As Byte Dim countBytesFromClient As Int32 Dim dataFromClient As String Dim clientSocket As Socket

VBNET-14.fm Seite 894 Mittwoch, 27. März 2002 5:17 17

Page 20: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

Netzwerkprogrammierung mit dem .NET-Framework 895

Sehen wir uns jetzt noch den Code des Clients an:

'Socket initialisieren Dim server As New Socket(AddressFamily.InterNetwork, _ SocketType.Stream, ProtocolType.IP) Dim ipHostInfo As IPHostEntry = Dns.Resolve(Dns.GetHostName()) Dim ipAdress As IPAddress = ipHostInfo.AddressList(0) Dim localEP As New IPEndPoint(ipAdress, 930) 'Daten an das Socket binden server.Bind(localEP) Try server.Listen(1) Console.WriteLine("Ich warte auf einen Client...") 'Clientanfrage akzeptieren clientSocket = server.Accept() Console.WriteLine("Verbindungsgesuch des Clients gelungen.") 'dem Client eine Mitteilung schicken Dim msg() As Byte = _ Encoding.ASCII.GetBytes("Hallo mein Freund.") clientSocket.Send(msg) 'vom Client eine Mitteilung entgegen nehmen countBytesFromClient = clientSocket.Receive(bytes) Console.WriteLine(Encoding.ASCII.GetString(bytes, 0, _ countBytesFromClient)) Catch e As Exception Console.WriteLine(e.ToString()) Finally clientSocket.Shutdown(SocketShutdown.Both) clientSocket.Close() End Try Console.ReadLine() End SubEnd Module

'----------------------------------------------------'Codebeispiel:...\Beispielcode\Kapitel_14\TCPClient_2'----------------------------------------------------Imports System.Net.SocketsImports System.NetImports System.TextModule Module1 Sub Main() Dim bytes(1024) As Byte Dim countBytesFromServer As Int32 Console.Write("Zur Verbindungsaufnahme <ENTER> drücken: ") Console.ReadLine()

VBNET-14.fm Seite 895 Mittwoch, 27. März 2002 5:17 17

Page 21: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

896 TCP/IP-Programmierung

Sie können die beiden Anwendungen testen, indem Sie wieder zuerst die TCP-Server- und anschließend die TCP-Clientanwendung starten. Jede Anwendungöffnet wieder ein eigenes Konsolenfenster und gibt die von der Gegenstelle emp-fangene Zeichenfolge aus.

14.2.3 Kommunikation zwischen zwei TCP-Partnern

So ganz verschafft das vorhergehende Beispiel noch keine Befriedigung. Wir sindzwar in der Lage, Nachrichten zu verschicken oder zu empfangen, aber eine Kom-munikation im eigentlichen Sinn ist noch nicht möglich. Wollten wir mehrereNachrichten schicken, müsste man immer wieder abwechselnd Send und Receiveaufrufen.

Dim server As New Socket(AddressFamily.InterNetwork, _ SocketType.Stream, ProtocolType.Tcp) 'Serverendpunkt beschreiben Dim ipHost As IPHostEntry = Dns.Resolve("WSAKL") Dim serverAddress As IPAddress = ipHost.AddressList(0) Dim EP As New IPEndPoint(serverAddress, 930) Try 'Verbindung aufnehmen server.Connect(EP) Dim port As Int32 = CType(server.RemoteEndPoint, _ IPEndPoint).Port Dim tcpip As String = CType(server.RemoteEndPoint, _ IPEndPoint).Address.ToString() Console.WriteLine("Verbindung zu {0}/Port {1} ist aufgebaut.",_ tcpip, port) 'es wird solange blockiert, bis die Antwort des Servers eintrifft countBytesFromServer = server.Receive(bytes) Console.WriteLine(Encoding.ASCII.GetString _ (bytes, 0, count BytesFromServer)) 'dem Server eine Mitteilung schicken Dim message() As Byte = Encoding.ASCII.GetBytes ("Ein prima Tag heute.") server.Send(message) Catch e As Exception Console.WriteLine(e.ToString()) Finally server.Shutdown(SocketShutdown.Both) server.Close() End Try Console.ReadLine() End SubEnd Module

VBNET-14.fm Seite 896 Mittwoch, 27. März 2002 5:17 17

Page 22: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

Netzwerkprogrammierung mit dem .NET-Framework 897

Wir wollen deshalb die beiden Anwendungen TCPServer und TCPClient in derWeise ergänzen, dass sie den folgenden Anforderungen genügen:

� Der TCP-Server empfängt beliebig viele Nachrichten vom Client und gibt dieseals Echo an den Client zurück.

� Der TCP-Client übermittelt dem TCP-Server Daten in beliebiger Abfolge, ohnedabei abhängig vom Empfang der vom Server gesendeten Daten zu sein.

Sehen wir uns zuerst den Teil des Codefragments an, der der ersten Anforderunggenügt:

Da der Server grundsätzlich auf jede eingegangene Nachricht des Clients reagie-ren soll, können die mit Receive empfangenen Zeichen direkt mit Send an denClient zurückgeschickt werden. Weil der TCP-Server nicht weiß, wie viele Nach-richten ihm die Gegenstelle schickt, wird der Empfang und das Senden in einerDo-While-Schleife ausgeführt. Die Schleife wird genau dann verlassen, wenn derClient die Verbindung aufgibt und dadurch einen Fehler auslöst, der von der Aus-nahmebehandlung abgefangen wird.

Etwas komplexer ist es, den TCP-Client zu programmieren. Das Problem, dasgelöst werden muss, ist die Blockage des Clients durch die Methode Receive.

Das beliebige Absenden von Zeichen – wie oben gefordert – ist nur möglich,wenn die beiden Methoden Send und Receive voneinander entkoppelt werden.Es bietet sich hier geradezu an, einen zweiten Thread zu starten, dem die Aufgabezukommt, die Servernachrichten zu empfangen. Dazu müssen wir eine Prozedurschreiben, die vom Thread ausgeführt wird. Der Name der Prozedur soll GetSer-verData lauten.

'Clientanfrage akzeptierenclientSocket = server.Accept()'weitere AnweisungenDo While True 'vom Client eine Mitteilung entgegen nehmen countBytesFromClient = clientSocket.Receive(bytes) Dim str As String = Encoding.ASCII.GetString(bytes, 0, _ countBytesFromClient) Dim message As String = "Serverantwort: " 'dem Client eine Mitteilung schicken Dim msg() As Byte = Encoding.ASCII.GetBytes(message & str) clientSocket.Send(msg)Loop'weitere Anweisungen

VBNET-14.fm Seite 897 Mittwoch, 27. März 2002 5:17 17

Page 23: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

898 TCP/IP-Programmierung

In der Prozedur Main muss jetzt ein Delegate vom Typ DelegateStart deklariertwerden, dem bei der Initialisierung die Adresse von GetServerData übergebenwird:

Wird das Verbindungsgesuch durch den Aufruf von Connect vom TCP-Serverakzeptiert, kann man die Klasse Thread unter Übergabe des Delegates instanzie-ren und anschließend den Thread starten.

Ab diesem Moment läuft sowohl der Vordergrund- als auch parallel dazu der Hin-tergrundthread, der innerhalb der Schleife ununterbrochen alle von der Gegen-stelle eingehenden Daten entgegennimmt, während im Vordergrundthread dieDaten versendet werden. Um dem Anwender die Möglichkeit zu geben, beliebigoft Nachrichten zu senden, wird, wie auch schon im Server, eine Schleife durch-laufen. Der Anwender kann die Schleife und somit auch die Verbindung zurGegenstelle aufgeben, indem er nach Aufforderung ein Zeichen eingibt, dasungleich (W) ist.

Nachfolgend ist der gesamte Code der inzwischen dritten Version unseres TCP-Clients und des TCP-Servers wiedergegeben.

Public Sub GetServerData() Do While True Dim bytes(1024) As Byte Dim countBytesFromServer As Int32 'es wird solange blockiert, bis die Antwort des Servers eintrifft countBytesFromServer = server.Receive(bytes) Console.WriteLine(Encoding.ASCII.GetString(bytes, 0, _ countBytesFromServer)) LoopEnd Sub

Dim del As ThreadStartdel = New ThreadStart(AddressOf GetServerData)

server.Connect(EP)'Anweisungen'Thread instanzierenDim GetDataThread As New Thread(del)'Thread startenGetDataThread.Start()

Anmerkung Selbstverständlich könnte man auch im TCP-Server in einemeigenen Thread die Receive-Methode behandeln.

VBNET-14.fm Seite 898 Mittwoch, 27. März 2002 5:17 17

Page 24: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

Netzwerkprogrammierung mit dem .NET-Framework 899

'----------------------------------------------------'Codebeispiel:...\Beispielcode\Kapitel_14\TCPClient_3'----------------------------------------------------Imports System.Net.SocketsImports System.NetImports System.TextImports System.ThreadingModule Module1 Dim server As Socket Sub Main() 'neuen Thread erzeugen Dim del As ThreadStart del = New ThreadStart(AddressOf GetServerData) Console.Write("Zur Verbindungsaufnahme <ENTER> drücken: ") Console.ReadLine() server = New Socket(AddressFamily.InterNetwork, _ SocketType.Stream, ProtocolType.Tcp) 'Serverendpunkt beschreiben Dim ipHost As IPHostEntry = Dns.Resolve("WSAKL") Dim serverAddress As IPAddress = ipHost.AddressList(0) Dim EP As New IPEndPoint(serverAddress, 930) Try 'Verbindung aufnehmen server.Connect(EP) Dim port As Int32 = CType(server.RemoteEndPoint, _ IPEnd-Point).Port Dim tcpip As String = CType(server.RemoteEndPoint, _ IPEndPoint).Address.ToString() Console.WriteLine("Verbindung zu {0}/Port {1} ist aufgebaut.",_ tcpip, port) 'Thread instanzieren Dim GetDataThread As New Thread(del) 'Thread starten GetDataThread.Start() Do While True 'dem Server eine Mitteilung schicken Console.WriteLine("Was möchten Sie dem Server mitteilen?") Dim strMessage As String = Console.ReadLine Dim message() As Byte = _ Encoding.ASCII.GetBytes(strMessage) server.Send(message) Dim ausgabe As String = "Wenn Sie eine weitere Mitteilung " ausgabe &= "schicken wollen, drücken Sie bitte 'W'" Console.WriteLine(ausgabe) If Console.ReadLine() <> "W" Then Exit Do

VBNET-14.fm Seite 899 Mittwoch, 27. März 2002 5:17 17

Page 25: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

900 TCP/IP-Programmierung

Beachten Sie, dass es unter mehreren Umständen zur Auslösung einer Exceptionkommen kann. Unabhängig davon sollten Sie auf jeden Fall sicherstellen, dasSocket wieder ordentlich zu schließen. Am besten eignet sich dazu der Finally-Zweig, der unabhängig vom Ablauf des Programms in jedem Fall ausgeführt wird.

Da die Ausnahmeauslösung unterschiedliche Ursachen haben kann, muss mandamit rechnen, dass das Socket möglicherweise schon geöffnet ist. Kann keineVerbindung zum TCP-Server aufgebaut werden, wird das Socket jedoch nicht inAnspruch genommen und bleibt geschlossen. Auf ein geschlossenes Socket Closeaufzurufen, verursacht eine Ausnahme. Um dies nicht innerhalb der Ausnahme-behandlung in Kauf nehmen zu müssen, prüfen wir mit der Eigenschaft Connecteddes Sockets, in welchem Zustand es sich befindet, und schließen es nur dann,wenn es bereits die Verbindung zum TCP-Server hält.

Loop GetDataThread.Abort() Catch e As ThreadAbortException Catch e As Exception Console.WriteLine(e.ToString()) Finally If server.Connected Then server.Shutdown(SocketShutdown.Both) server.Close() End If Console.ReadLine() End Try End Sub

'diese Methode wird in einem eigenen Thread ausgeführt Public Sub GetServerData() Do While True Dim bytes(1024) As Byte Dim countBytesFromServer As Int32 'solange blockieren, bis die Antwort des Servers eintrifft countBytesFromServer = server.Receive(bytes) Console.WriteLine(Encoding.ASCII.GetString(bytes, 0, _ countBytesFromServer)) Loop End SubEnd Module

VBNET-14.fm Seite 900 Mittwoch, 27. März 2002 5:17 17

Page 26: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

Netzwerkprogrammierung mit dem .NET-Framework 901

'----------------------------------------------------'Codebeispiel:...\Beispielcode\Kapitel_14\TCPServer_3'----------------------------------------------------Imports System.Net.SocketsImports System.NetImports System.TextModule Module1 Sub Main() Dim bytes(1024) As Byte Dim countBytesFromClient As Int32 Dim dataFromClient As String Dim clientSocket As Socket 'Socket initialisieren Dim server As New Socket(AddressFamily.InterNetwork, _ SocketType.Stream, ProtocolType.IP) Dim ipHostInfo As IPHostEntry = _ Dns.Resolve(Dns.GetHostName()) Dim ipAdress As IPAddress = ipHostInfo.AddressList(0) Dim localEP As New IPEndPoint(ipAdress, 930) 'Daten an das Socket binden server.Bind(localEP) 'Socket aktivieren und auf Clientanruf warten Try server.Listen(1) Console.WriteLine("Ich warte auf einen Client...") 'Clientanfrage akzeptieren clientSocket = server.Accept() Console.WriteLine("Verbindungsgesuch des Clients gelungen.") Dim port As Int32 = CType(clientSocket.RemoteEndPoint, _ IPEndPoint).Port Dim tcpip As String = CType(clientSocket.RemoteEndPoint, _ IPEndPoint).Address.ToString() Console.WriteLine("Verbindung zu {0}/Port {1} ist aufgebaut.",_ tcpip, port) Do While True 'vom Client eine Mitteilung entgegen nehmen countBytesFromClient = clientSocket.Receive(bytes) Dim str As String = Encoding.ASCII.GetString(bytes, 0, _ countBytesFromClient) Dim message As String = "Serverantwort: " 'dem Client eine Mitteilung schicken Dim msg() As Byte = Encoding.ASCII.GetBytes(message & str) clientSocket.Send(msg) Loop Catch e As Exception

VBNET-14.fm Seite 901 Mittwoch, 27. März 2002 5:17 17

Page 27: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

902 TCP/IP-Programmierung

Console.WriteLine(e.ToString()) Finally clientSocket.Shutdown(SocketShutdown.Both) clientSocket.Close() End Try Console.WriteLine("WWW") Console.ReadLine() End SubEnd Module

VBNET-14.fm Seite 902 Mittwoch, 27. März 2002 5:17 17

Page 28: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

Netzwerkprogrammierung mit dem .NET-Framework 903

14.2.4 Zusammenfassung

� Unter .NET repräsentiert die Instanz der Klasse Socket eine Verbindung zwi-schen zwei Kommunikationspartners auf Basis der Socket-API. Die KlasseSocket stellt nur einen Konstruktor bereit, dem bei der Instanzierung die pas-senden Parameter übergeben werden.

� Ein Server muss ein Socket bereitstellen, das von einem Client bei der Kontakt-aufnahme angesprochen werden kann. Dazu bindet sich das Socket an die IP-Adresse des Servers und an einem für den Dienst reservierten Port. Die Bin-dung erfolgt über die Bind-Methode des Sockets. Das Schalten in denBetriebsbereiten Zustand erfolgt durch anschließenden Aufruf von Listen.

� Bei einem laufenden TCP/IP-Dienst können mehrere Clientanfragen gleichzei-tig eingehen. Da zu einem gegebenen Zeitpunkt ein Socket nur einen Clientbedienen kann, werden alle anderen Anrufe in eine Warteschlange gestellt. Diemaximale Größe der Warteschlange wird durch das der Listen-Methode über-gebene Argument festgelegt.

� Mit der Methode Accept wartet ein Socket auf einen eingehenden Clientanruf.Solange wird der aktuelle Thread suspendiert. Die Aktivierung erfolgt erstdurch die Verbindungsaufnahme des Clients.

� Ein TCP/IP-Client benötigt ebenfalls eine Socket-Instanz, ruft aber nur dieMethode Connect auf und übergibt dabei ein IPEndPoint-Objekt mit den Ver-bindungsdaten, dessen Dienst der Client in Anspruch nehmen möchte.

� Connect versetzt den ausführenden Thread des Client solange in den Wartezu-stand, bis der Server sich beim Client zurückmeldet oder eine Exception ausge-löst wird, da der Server unter den angenommenen Verbindungsdaten nicht zuerreichen ist.

� Nach dem erfolgreichen Aufbau der Verbindung sind keine klare Grenzen zwi-schen Client und Server mehr gesetzt. Beide können über die Methoden Sendder Gegenstelle Daten schicken oder diese mit Receive in Empfang nehmen.Auch die Receive-Methode suspendiert den wartenden Thread und lauschtsolange am Socket, bis über die Verbindung Daten eingehen.

Um eine beliebig ablaufende Kommunikation sicher zu stellen, sollte die beidenMethoden Send und Receive in zwei Threads ausgeführt werden.

VBNET-14.fm Seite 903 Mittwoch, 27. März 2002 5:17 17

Page 29: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

904 TCP/IP-Programmierung

14.3 E-Mails verschicken

Sie wissen nun, wie man die Sockets dazu einsetzt, um in einem Netzwerk miteinem Partner zu kommunizieren: Man muss nur die Verbindung zu einembestimmten Server aufbauen und kann anschließend sofort mit dem Versendender Daten beginnen.

Sehr ähnlich arbeiten auch die meisten Low-Level-Protokolle, mit denen wir unsbestimmte Dienste im Internet sichern: FTP, Telnet, POP3, SMTP, HTTP, usw.Einen Dienst in Anspruch zu nehmen, bedeutet aber auch, dass wir die Gegen-stelle zuerst mit vordefinierten Befehlen beschicken müssen, um den Dienst inder von uns gewünschten Weise anzustoßen. Diese Befehle sind bei den meistenüblichen Protokollen reine ASCII-Befehle, deren Syntax und Abfolge in einerDokumentation festgehalten ist, die als RFC (Request For Comments) bezeichnetwird.

Beabsichtigen wir, einen bestimmten Dienst im Internet in Anspruch zu nehmen,brauchen wir uns nur um die RFC des entsprechenden Protokolls zu bemühenund können dann sofort anfangen – denn die Grundlagen für die Verbindungsauf-nahme sind bereits im vorhergehenden Abschnitt gelegt worden und unterschei-den sich in keinster Weise.

Das hört sich nicht nur sehr einfach an, es ist tatsächlich auch einfach. Sie werdendazu in diesem Abschnitt exemplarisch lernen, wie man mit dem SMTP (SimpleMail Transfer Protocol) eine Mail auf die Reise schickt, und diese – falls uns keinFehler unterläuft – sogar beim Empfänger ankommt. Sie werden hinsichtlich des.NET-Frameworks nur sehr wenig Neues hinzulernen, denn das Handwerkszeugzur Realisierung halten Sie bereits vollständig in den Händen – es fehlt Ihnen nurnoch das erforderliche technologische Know-how.

14.3.1 Einleitung

Das gesamte E-Mail-System ist ein Komplex, der sich aus mehreren Protokollenzusammensetzt und sich in einem Punkt von den anderen Internet-Anwendun-gen unterscheidet: Während andere Dienste wie beispielsweise das Web einedirekte Verbindung zwischen Sender und Empfänger voraussetzen, muss derRechner des Empfängers einer Mail nicht zur selben Zeit online sein wie der desAbsenders. Die abgesetzte Mail wird zunächst an den Mail-Router des eigenenInternet-Providers weitergeleitet, der seinerseits die Mail zum nächsten Routerweiterleitet. Dieser Prozess setzt sich solange fort, bis die Mail an den Routergelangt, der für den Mailempfänger zuständig ist.

Der Weiterleitung dient das Protokoll SMTP. Es dient aber nicht nur dem Absen-der einer Nachricht dazu, die Mail abzuschicken, sondern wird auch von den ver-

VBNET-14.fm Seite 904 Mittwoch, 27. März 2002 5:17 17

Page 30: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

E-Mails verschicken 905

schiedenen Routern untereinander zur Weiterleitung benutzt. Nur eine Bedin-gung ist mit SMTP verbunden: Sie müssen ein Postfach bei dem Mail-Serverhaben, der als Erster die Mail empfängt.

Ein Router interessiert sich nicht dafür, welchen Inhalt die Mail hat. Er benötigtnur die Informationen, um die Mail weiterzuleiten, damit sie am Ende eines mehroder weniger langen Laufwegs richtig zugestellt werden kann. Der Aufbau unddie Struktur der Mail ist für die Router unwichtig. Gelangt die Mail in das Postfachdes Empfängers, haben die Weitergabeinformationen, mit denen die Routerarbeiten, für den Empfänger nur selten einen informativen Charakter.

Jede Mail setzt sich auch aus einer Befehlssequenz für die Weiterleitung und einerweiteren Befehlssequenz, die den Inhalt und die Struktur einer E-Mail beschreibt,zusammen. Der Transport einer E-Mail wird im RFC 821 beschrieben, die Struktureiner Mail im RFC 822. Beide zusammen bilden das Protokoll SMTP.

Abbildung 14.2 Der Weg einer Mail vom Sender zum Empfänger

14.3.2 Die Anforderungen an das Mail-Programm

Für das Mail-Programm, dessen Entwicklung nachfolgend Schritt für Schrittbeschrieben wird, wollen wir nicht von unserer sturen Linie abweichen. Wirhaben bisher ohne visualisierte Oberflächen gearbeitet und werden es auch wei-terhin nicht tun. Unser Programm kann sich daher auch nicht mit MS-Outlook

Router

Sender schickt eine Mail via TCP/IP

Empfänger holt die Mail ab

Die Mail wird solangeweitergeleitet, bis sieden Mail-Server desEmpfängers erreicht

VBNET-14.fm Seite 905 Mittwoch, 27. März 2002 5:17 17

Page 31: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

906 TCP/IP-Programmierung

und ähnlichen Mailclients messen, es hat nicht so viele Features und ist auch nichtso komfortabel, aber wir wollen auch keine kleinen Brötchen backen und könnennachher mit stolz geschwellter Brust im Ton der Überzeugung sagen, es funktio-niert (wenigstens).

Der wesentlichste Teil des Programmcodes, der für das Versenden einer Mail ver-antwortlich ist, soll in einer Klasse definiert werden, deren Name ConsoleMail lau-tet. Damit stünde es uns frei, die Klasse auch in anderen Anwendungen als primi-tiven Client einzusetzen. Obwohl in der Klasse verhältnismäßig viel Funktionalitätsteckt, soll eine Mail alleine durch den Aufruf der Instanzmethode SendMail abge-schickt werden.

Damit eine Mail nicht nur abgeschickt, sondern am Ende ihres möglicherweiselangen Weges durch das Netz auch den richtigen Empfänger erreicht, sind vieleInformationen sowohl allgemeiner als auch spezifischer Art notwendig. Zum Bei-spiel müssen Sie sich über Ihr eigenes Postfach bei Ihrem Provider identifizieren –dies wird sich von Mail zu Mail nicht ändern. Im Gegensatz dazu sind die Mail-empfänger und natürlich auch die textuellen Inhalte vermutlich nicht immer die-selben. Bevor eine Mail vertrauensvoll an den Mail-Server übergeben wird, mussder Benutzer alle erforderlichen Informationen an der Konsole angeben.

Damit ihm diese Arbeit ein wenig erleichtert wird, wollen wir die statischenInformationen der Klasse ConsoleMail nur beim ersten Start der Anwendung abfra-gen und danach per Serialisierung in einer Datei speichern. Ein zweiter Programm-aufruf deserialisiert diese Daten und unterstützt so den Anwender. Wir wollen mitder Serialisierung sogar so weit gehen, dass sie ohne eine Aktion des Anwendersautomatisch im Hintergrund abläuft, also innerhalb der Klasse implementiert wird.

14.3.3 Der Transport einer E-Mail

In der Regel sind es verschiedene Mail-Server, die eine Mail passiert, bis sie end-gültig beim Empfänger landet. Es spielt dabei keine Rolle, auf welcher Hardware-architektur der Mail-Server aufsetzt, denn die Sprache und die Befehle, mit denendie Server kommunizieren, ist festgelegt: das SMTP-Protokoll. Mit SMTP könnenMails nur gesendet werden, dass Abholen einer Mail wird durch andere Proto-kolle beschrieben: POP3 bzw. IMAP4. Wie die meisten anderen Internet-Proto-kolle, gehört auch SMTP zu der Gruppe der ASCII-Protokolle. Die Kommunika-tion läuft also im Klartext und somit lesbar ab.

Das Prinzip, nach dem mit einem SMTP-Server kommuniziert wird, kann man sichwie ein Tennisspiel vorstellen, bei dem die beiden Spieler die sich im Datenaus-tausch befindlichen Partner darstellen und der Ball der Datenstrom ist. Wie derBall von einem Spieler zum anderen gespielt wird (auch der Begriff »Netz« spielt

VBNET-14.fm Seite 906 Mittwoch, 27. März 2002 5:17 17

Page 32: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

E-Mails verschicken 907

hier rein zufällig ein entscheidende Rolle), so schickt der Client seine Daten anden Server ab und bekommt dafür eine Antwort – natürlich wieder in Form vonDaten. Ist die Antwort des Servers beim Client eingetroffen, darf dieser die nächs-ten Daten schicken. Wie die Daten aussehen müssen, damit der Server etwas mitihnen anfangen kann, werden Sie gleich noch erfahren.

Damit ist schon die allgemeine Struktur eines Mail-Clients beschrieben: Es wirdmit Send ein vordefiniertes Kommando abgesetzt, dem eine ebenso vordefinierteServerantwort folgt, die mit Receive empfangen wird. Dieses Spielchen setzt sichsolange fort, bis die Mail erfolgreich abgesetzt ist oder ein Fehler auftritt, bei-spielsweise, weil ein falscher Befehl abgesetzt wird oder die Verbindung durchäußere Umstände unterbrochen wird.

Die Struktur der Klasse ConsoleMail

Bevor wir uns mit den Details der Implementierung der Klasse ConsoleMailbeschäftigen, wollen wir das Gerüst der Klasse erstellen und die Instanzvariablensowie den Konstruktor bereits vollständig definieren. Damit treten auch keineErklärungsnotstände auf, wenn aus den Methoden heraus die Feldinhalte ausge-wertet werden.

<Serializable()> Class ConsoleMail 'allgemeine Felder <NonSerialized()> Private server As Socket <NonSerialized()> Private puffer(1024) As Byte <NonSerialized()> Private count As Int32 <NonSerialized()> Private svrAnswer As String

'SMTP-spezifische Felder Private mailServer As String Private mailFrom As String Private senderName As String <NonSerialized()> Private mailTo As String <NonSerialized()> Private betreff As String <NonSerialized()> Private mailText As String

'Konstruktor Public Sub New() Dim binFormatter As New BinaryFormatter() Dim fs As FileStream 'Initialisierung der SMTP-spezifischen Felder If Not File.Exists(Path.GetTempPath & "\MailOptions.dat") Then '--------------------------------------------------------- 'dieser Block wird nur ausgeführt, wenn die Klasse auf der 'aktuellen Maschine zum ersten Mal instanziiert wird '---------------------------------------------------------

VBNET-14.fm Seite 907 Mittwoch, 27. März 2002 5:17 17

Page 33: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

908 TCP/IP-Programmierung

Einige Eingaben des Anwenders werden »getrimmt«, d. h, dass eventuell amAnfang oder am Ende der Eingabe stehende Leerzeichen abgeschnitten werden,da Sie zu einer Fehlinterpretation beim Empfänger, also dem SMTP-Server, führenkönnten.

Die Felder lassen sich in zwei Gruppen einteilen:

� Allgemeine Felder, die nur zur Unterstützung des Programmcodes dienen;

� SMTP-spezische Felder, die für das Versenden der Mail mit SMTP notwendigsind.

'IP-Adresse oder Domain-Name des eigenen Mail-Servers Console.Write("Mail-Server: ") mailServer = Console.ReadLine().Trim() 'die Mail-Adresse des Absenders angeben Console.Write("Ihre E-Mail-Adresse: ") mailFrom = Console.ReadLine().Trim() 'den Namen des Absenders angeben Console.Write("Ihr Name: ") senderName = Console.ReadLine().Trim() 'Serialisierung der vorher angegebenen Daten fs = New FileStream(Path.GetTempPath & _ "\MailOptions.dat", FileMode.Create) binFormatter.Serialize(fs, Me) fs.Close() Else 'Deserialisierung der SMT-spezifischen Daten fs = New FileStream("C:\MyObject.dat", FileMode.Open) Dim tempObj As ConsoleMail = _ CType(binFormatter.Deserialize(fs), ConsoleMail) mailServer = tempObj.mailServer mailFrom = tempObj.mailFrom senderName = tempObj.senderName tempObj = Nothing End If 'den Empfänger der Mail eingeben Console.Write("E-Mail-Empfänger: ") mailTo = Console.ReadLine().Trim() 'die Betreff-Zeile festlegen Console.Write("Betreff: ") betreff = Console.ReadLine() 'Angabe der eigentlichen Nachricht Console.Write("Text: ") mailText = Console.ReadLine() End SubEnd Class

VBNET-14.fm Seite 908 Mittwoch, 27. März 2002 5:17 17

Page 34: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

E-Mails verschicken 909

Die Initialisierung der Felder der letzten Gruppe erfolgt bereits im Konstruktor.Daher ist schon an dieser Stelle eine kurze Erklärung der Bedeutung angebracht:

mailServer enthält die IP-Adresse oder den Domain-Name des Mail-Servers, beidem Sie Ihr Postfach angemeldet haben. Er ist die erste Station, über der die Mailläuft.

mailFrom enthält die Angabe Ihrer eigenen Mailadresse, die auf dem im FeldmailServer eingetragenen Mail-Server verwaltet wird.

senderName ist Ihr ausgeschriebener Name. Der Inhalt dieses Feldes wird späterim Mailclient des Empfängers als Absender der Mail angezeigt.

mailTo enthält die E-Mail-Adresse des Empfängers.

betreff repräsentiert den Inhalt der in den Mails obligatorischen, aber nicht zwin-gend notwendigen Betreff-Zeile.

mailText enthält schließlich die Nachricht an den Empfänger.

Ein Teil der Felder soll laut Aufgabenstellung serialisiert werden. Die Klassendefi-nition ist aus diesem Grund mit dem Serializable-Attribut verknüpft. Da der Seri-alisierungsprozess standardmäßig aller Felder unabhängig vom Zugriffsmodifizie-rer in den Prozess einbezieht, können wir einige Felder mit dem AttributNonSerialized ausschließen. Die verbleibenden, es sind deren nur drei, werdenmit

in die Datei MailOptions.dat des benutzerspezifischen, temporären Verzeichnissesgeschrieben.

Bei der Instanzierung der Klasse ConsoleMail wird der parameterlose Konstruktoraufgerufen, der alle SMTP-spezifischen Felder initialisiert. Dazu wird der Anwen-der zu entsprechenden Eingaben an der Konsole aufgefordert. Beim ersten Startder Anwendung wird es auch noch keine zu deserialisierenden Daten geben – diePrüfung, ob die Datei MailOptions.dat bereits existiert, fällt also negativ aus. Folg-lich wird der Anwender auch zur Eingabe der Daten aufgefordert, die bei einemwiederholten Start wieder verwendet werden können. Dabei handelt es sich umdie Daten, mit denen der Anwender die Adresse seines Mail-Server, seine E-Mail-Adresse und seinen Namen bekannt gibt.

Der wiederholte Start der Anwendung führt sofort in den Else-Zweig der If-Bedingung und deserialisiert die gespeicherten Objektdaten.

fs = New FileStream(Path.GetTempPath & _ "\MailOptions.dat", FileMode.Create)

VBNET-14.fm Seite 909 Mittwoch, 27. März 2002 5:17 17

Page 35: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

910 TCP/IP-Programmierung

Nach Beendigung der gesamten If-Anweisung verlangt die Laufzeit des Pro-gramms noch nach den mailspezifischen Angaben. Kehrt der Konstruktor zu sei-nem Aufrufer zurück, ist die Klasse bereits mit allen Daten versorgt, um die Mailauf den Weg zu bringen. Aber bis dahin müssen wir noch einiges an Implemen-tierungsarbeit leisten. Bevor wir damit anfangen, sollten wir noch einen Blick aufden Aufrufer werfen.

Mehr Code wird nicht hinzukommen, denn die Kontrolle des Programmablaufswird der Methode SendEMail übergeben, die wir allerdings noch nicht implemen-tiert haben.

Die Methode SendEMail

Die einzige Methode, die in der Klasse ConsoleMail öffentlich deklariert ist, istSendEMail. Sie übernimmt die Aufgabe, zuerst das Socket zu initialisieren unddann über die Methode OpenConnection die Verbindung aufzubauen. OpenCon-nection liefert True, wenn die Gegenstelle antwortet und signalisiert damit, dassder Mail-Server sich zur Entgegennahme der SMTP-Befehle bereit erklärt hat. Fürdiesen Teil ist wiederum eine eigene Methode vorgesehen – SMTPCommands

Sicherlich könnten wir auch den gesamten Code in der Methode SendEMail im-plementieren. Dagegen spricht ein Grundprinzip des objektorientierten Ansatzes,nach dem die Struktur des Programmcodes in kleinere, überschaubare und in sichschlüssige Fragmente aufgeteilt werden sollte – Modularisierung ist hier das Stich-wort.

Sub Main() Console.Write("Zur Verbindungsaufnahme <ENTER> drücken: ") Console.ReadLine() Dim myMail As New ConsoleMail() myMail.SendEMail()End Sub

Public Sub SendEMail() server = New Socket(AddressFamily.InterNetwork, _ SocketType.Stream, ProtocolType.Tcp) 'Verbindung aufnehmen If OpenConnection() Then SMTPCommands() End If Console.ReadLine()End Sub

VBNET-14.fm Seite 910 Mittwoch, 27. März 2002 5:17 17

Page 36: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

E-Mails verschicken 911

Die Verbindungsaufnahme

Beabsichtigen wir eine Mail zu versenden, müssen wir zunächst eine Verbindungzum Mail-Server unseres Internet-Providers aufbauen. Das Prinzip ist dabei das-selbe, das wir in diesem Kapitel schon in mehreren Beispielen gesehen haben.Den SMTP-Dienstes kontaktieren wir standardmäßig am Port 25. Zur Verbin-dungsaufnahme benötigen wir auch außerdem bekanntermaßen die IP-Adresse,die wir aber in der Regel nicht kennen, sondern nur den Domain-Namen unseresSMTP-Servers. Deshalb muss der Domain-Name in eine IP-Adresse umgesetztwerden.

Nach der erfolgreichen Verbindungsaufnahme mit Connect wird sich der Serverzurückmelden. Wir müssen diese Antwort entgegennehmen, denn sie ist für unsder Anstoss, anschließend die SMTP-Kommandos abzusetzen.

Der Abwicklung der Verbindungsaufnahme dient die Prozedur OpenConnection.Sie liefert einen booleschen Rückgabewert der True ist, wenn das Verbindungsge-such vom SMTP-Server positiv bestätigt wird. Wir lassen uns das mit den dazuge-hörigen Verbindungsdaten an der Konsole anzeigen.

Sehen wir uns nun die Methode OpenConnection an, die privat deklariert ist, weilsie nur aus dem Objekt heraus aufgerufen werden soll.

Private Function OpenConnection() As Boolean OpenConnection = False Try 'Serverendpunkt beschreiben Dim ipHost As IPHostEntry = Dns.Resolve("mailto.btx.dtag.de") Dim serverAddress As IPAddress = ipHost.AddressList(0) Dim EP As New IPEndPoint(serverAddress, 25) server.Connect(EP) Dim port As Int32 = CType(server.RemoteEndPoint, IPEndPoint).Port Dim tcpip As String = _ CType(server.RemoteEndPoint, IPEndPoint).Address.ToString() Console.WriteLine("Verbindung zu {0}/Port {1} ist aufgebaut.", _ tcpip, port) 'Serverantwort empfangen count = server.Receive(puffer) svrAnswer = Encoding.ASCII.GetString(puffer, 0, count) Console.WriteLine(svrAnswer) If svrAnswer.Substring(0, 3) = "220" Then OpenConnection = True Else Throw New SocketException() End If

VBNET-14.fm Seite 911 Mittwoch, 27. März 2002 5:17 17

Page 37: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

912 TCP/IP-Programmierung

Die Struktur der Verbindungsaufnahme ähnelt der, die Sie bereits in den vergan-genen Beispielen benutzt haben, orientiert sich allerdings an den besonderenGegebenheiten eines SMTP-Servers. Zuerst konstruieren wir aus dem Domain-Namen und der Port-Nummer ein Objekt vom Typ IPEndPoint, das wir derMethode Connect übergeben:

Reagiert der von adressierte Server, werten wir dessen Verbindungsdaten rein ausNeugier aus. Im Gegensatz zu den Beispielprogrammen, die wir bisher entwickelthaben, erklärt sich der Server nicht nur zur Kommunikation bereit, sondern sen-det sofort eine Nachricht an den akzeptierten Client, die wir mit

entgegennehmen und auswerten müssen. Stellen Sie beispielsweise die Verbin-dung zum Mail-Server von T-Online her, wird die Antwort wie folgt lauten:

Tatsächlich liefert uns die Antwort eine wesentliche Information, die wir auswer-ten können: Es sind die ersten drei Zeichen, die eine dreistellige Zahl beschreiben.Der darauf folgende Text ist serverspezifisch und bedeutungslos. Lauten die ers-ten drei Zeichen 220, erklärt sich der von uns adressierte SMTP-Server zu einerweiteren Kommunikation bereit.

Bei einer falschen IP-Adresse oder einer ungültigen Portangabe wird eine Aus-nahme ausgelöst, die wir natürlich abfangen. Die Annahme, dass alleine der Emp-fang einer Serverantwort ausreichend ist, um daraus die Bereitschaft zu einemweiteren Austausch von SMTP-Befehlen zu erkennen, ist falsch. Geben wir näm-lich einen auflösbaren Domain-Namen und einen vom Server geöffneten, belie-bigen Port an, wird eine andere Antwort eingehen. Das können wir sehr einfach

Catch e As SocketException Console.WriteLine("Der SMTP-Server reagiert nicht.") server.Close() Catch e As Exception Console.WriteLine(e.ToString()) server.Close() End TryEnd Function

Dim ipHost As IPHostEntry = Dns.Resolve(mailServer)Dim serverAddress As IPAddress = ipHost.AddressList(0)Dim EP As New IPEndPoint(serverAddress, 25)server.Connect(EP)

count = server.Receive(puffer)

220 fwd00.sul.t-online.com T-Online ESMTP receiver fsmtpd ready

VBNET-14.fm Seite 912 Mittwoch, 27. März 2002 5:17 17

Page 38: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

E-Mails verschicken 913

testen, indem wir anstatt des Ports 25 die Port-Nummer 110 eintragen. Der Port110 wird vom POP3-Protokoll reserviert und dient dem Abruf eines Postfachs. DerServer wird ebenfalls eine Antwort schicken, die allerdings mit +OK eingeleitetwird – vorausgesetzt natürlich, dass der Server am angegebenen Port lauscht.

Unser Programmcode muss daher zwei Anforderungen genügen:

1. Er muss die Antwort des Servers abwarten. Reagiert der adressierte Server nichtinnerhalb einer vordefinierten Zeitspanne, wird eine Exception ausgelöst.

2. Die eingehende Antwort wird extrahiert, um sicherzustellen, dass die erstendrei Ziffern 220 lauten. Der verbleibende Rest der eingehenden Serverantworthat keine Bedeutung.

Wir erfüllen die genannten Punkte mit einer Ausnahmebehandlung und durch dieÜberprüfung der vom Server eingehenden Zeichenfolge:

Weicht der Antwortcode von 220 ab, lösen wir eine Ausnahme aus und schließendas Socket.

Das SMTP-Protokoll

Ist das Socket richtig konfiguriert, können wir die im RFC 821 dokumentiertenBefehle im ASCII-Format absetzen. Der SMTP-Server wird jeden einzelnen Befehlmit einem Antwortcode quittieren, aus dem der Erfolg oder auch das Scheiterndes abgesetzen Befehls interpretiert werden kann. Wir müssen daher immer war-ten, wie der Server reagiert, bevor wir den Folgebefehl absetzen.

Der vom Mail-Server unterstützte Sprachschatz lässt sich in zwei Gruppen eintei-len:

� Den SMTP-Standard, der von jedem Mail-Server unterstützt werden muss;

� einen optinalen, erweiterten Befehlssatz.

Der erweiterte Befehlssatz ist im RFC 1651 dokumentiert. Will eine Client-Soft-ware auf Befehle dieses Befehlssatzes zurückgreifen, muss diese sich vorab darü-ber vergewissern, ob dieser Befehlssatz auch vom kontaktierten Mail-Serverunterstützt wird. Wir werden unsere Mailclient-Software nachfolgend nur auf dieStandardbefehle des RFC 821 zugreifen lassen, die in der folgenden Tabelle aufge-führt sind.

If svrAnswer.Substring(0, 3) = "220" Then ...

VBNET-14.fm Seite 913 Mittwoch, 27. März 2002 5:17 17

Page 39: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

914 TCP/IP-Programmierung

Obwohl in der Praxis in den beiden Befehlen MAIL FROM und RCPT TO Senderund Empfänger der Mail eingesetzt werden, kommt diesen beiden Kommandoseine weitergehende Aufgabe zu. In beiden kann ein Pfad definiert werden, deneine Mail vom Sender zum Emfänger nehmen soll. In Netzwerken, die mit demInternet nur durch einen Knotenrechner verbunden sind, kommt diese Auslegungder Parameter Bedeutung zu.

Beachten Sie, dass jeder Befehl mit einem Zeilenumbruch endet, der dem Mail-Ser-ver mitgeteilt werden muss und in der Tabelle mit <CRLF> gekennzeichnet ist. Ameinfachsten erreichen wir das, indem wir im Allgemeinteil den Namespace Micro-soft.VisualBasic.ControlChars importieren, der die Konstante CrLf definiert.

Hinsichtlich der Befehlssyntax ist auch noch eine zweite Anmerkung wichtig: Diespitzen Klammern um Absender und Empfänger sind Bestandteil des jeweiligenBefehls. Wir müssen, um beispielsweise den Empfänger der Mail bekannt zugeben, dem Server die folgende Zeichenfolge übergeben:

Die Serverantwort

Die numerischen Antwortcodes des Servers werden als mindestens dreiziffrigeASCII-Zeichenfolge zurückgeliefert. Wir haben dieses Verhalten schon beim Ver-bindungsgesuch zum SMTP-Server gesehen. In ähnlicher Form äußert sich derMail-Server nach jedem Befehl, den der Client sendet. Die Antwortcodes bestäti-gen den Befehl oder beschreiben die Ablehnung einer Operation, wobei bereitsdie erste Ziffer dem Zweck dient, den Antwortcode einer bestimmten Gruppezuzuordnen. Dabei gelten die in der folgenden Tabelle aufgelisteten Zuordnungen.

Befehl Beschreibung

HELO Domain-Name <CRLF> Anmelden beim Mail-Server (hier wird der Domain-Name des Mail-Servers des Mail-Absenders angegeben).

MAIL FROM:<Absender> <CRLF> Absender der Mail (E-Mail-Adresse).

RCPT TO:<Empfänger> <CRLF> E-Mail-Adresse des Empfängers der Mail.

DATA <CRLF> Übertragung der Nachricht.

QUIT <CRLF> Abmeldung vom Mail-Server.

RSET <CRLF> Abbruch einer laufenden Übertragung und Reset der Ver-bindung .

NOOP <CRLF> Eine Null-Operation, die ein OK von der Gegenstelle pro-vozieren soll.

Tabelle 14.1 Die Standardbefehle des SMTP

Dim mailTo As String = "RCPT TO:<[email protected]>" & CrLf

VBNET-14.fm Seite 914 Mittwoch, 27. März 2002 5:17 17

Page 40: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

E-Mails verschicken 915

In der Tabelle 14.3 sind alle Antwortcodes des SMTP-Protokolls einschließlich derverursachenden Faktoren vollständig aufgeführt.

Startziffer Beschreibung

2 Befehl wurde positiv angenommen.

3 SMTP-Server wartet auf weitere Daten.

4 Aktion wurde verweigert.

5 Fehler.

Tabelle 14.2 Kategorisierung der Antwortcodes

Code Meldung Beschreibung

211 System status, or system help reply

Begleittext enthält den aktuellen Status des SMTP-Servers.

214 Help message Begleittext enthält Hilfsinformationen.

220 <domain> Service ready Der SMTP-Server steht bereit, um Kommandos vom Client entgegenzunehmen.

221 <domain> Service closing transmission channel

Der SMPT-Server beendet die Verbindung.

250 Requested mail action okay, completed

Befehl erfolgreich ausgeführt.

251 User not local; will forward to <forward-path>

Anwender ist nicht auf dem angesprochenen Mail-Server registriert, seine Mail-Adresse jedoch bekannt. Die Nachricht wird vom Mail-Server automatisch weitergeleitet.

354 Start mail input; end with <CRLF>.<CRLF>

Antwort auf den DATA-Befehl. Mail-Server erwartet Nachrichtentext mit dem Abschluss durch <CRLF>.<CRLF>.

421 <domain> Service not available,

closing transmission channel

Der SMTP-Server beendet die Verbindung, weil er den SMTP-Client aufgrund der angegebenen E-Mail-Adresse abweist.

450 Requested mail action not taken:

mailbox unavailable

Befehl kann nicht ausgeführt werden, weil das genannte Postfach unbekannt ist.

451 Requested action aborted: local error in processing

Befehlsausführung wg. Fehler aufseiten des SMTP-Servers abgebrochen.

Tabelle 14.3 Antwortcodes eines SMTP-Servers

VBNET-14.fm Seite 915 Mittwoch, 27. März 2002 5:17 17

Page 41: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

916 TCP/IP-Programmierung

Absetzen der SMTP-Kommandos

Wir wissen nun, wie die Kommunikation zwischen einem SMTP-Client und einemSMTP-Server abläuft: Wir senden ein Kommando und der Server schickt uns denAntwortcode. Das wiederholt sich solange, bis der letzte Befehl QUIT abgesetztwird – vorausgesetzt, es tritt kein Fehler auf. Schematisch folgt daraus der nach-stehende Ablauf:

452 Requested action not taken: insufficient system storage

Der Mail-Server kann eine Nachricht nicht ent-gegennehmen, weil nicht mehr genug freier Speicherplatz zur Verfügung steht (Überlastung des Mail-Servers durch nicht abgeholte Mails).

500 Syntax error, command unrecognized

Syntax-Fehler in der Kommandozeile, Befehl nicht erkannt.

501 Syntax error in parameters or arguments

Syntax-Fehler bei den Parameter eines Kom-mandos.

502 Command not implemented Das angegebene Kommando steht auf dem Mail-Server nicht zur Verfügung.

503 Bad sequence of commands Kommandos wurden in der falschen Reihen-folge abgesetzt.

504 Command parameter not implemented

Einer der beim Kommando angegebenen Para-meter wird nicht unterstützt.

550 Requested action not taken: mailbox unavailable

Kommando kann nicht ausgeführt werden, weil das angegebene Postfach unbekannt ist.

551 User not local; please try <forward-path>

Der angegebene Anwender wird nicht auf dem kontaktierten Mail-Server geführt. Es ist jedoch eine passende Mail-Adresse bekannt, die im Rahmen der Antwort genannt wird.

552 Requested mail action aborted: exceeded storage allocation

Nachrichtenübertragung wurde abgebrochen, weil die Nachricht den noch verfügbaren Spei-cherplatz auf dem Server überschreitet.

553 Requested action not taken: mailbox name not allowed

Kommando konnte wegen unerlaubten Post-fachnamen nicht ausgeführt werden.

554 Transaction failed Übertragung gescheitert

Dim mySocket As SocketmySocket.Send <HELO> mySocket.Receive <Servercode>mySocket.Send <MAIL FROM>mySocket.Receive <Servercode>

Code Meldung Beschreibung

Tabelle 14.3 Antwortcodes eines SMTP-Servers (Forts.)

VBNET-14.fm Seite 916 Mittwoch, 27. März 2002 5:17 17

Page 42: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

E-Mails verschicken 917

Die Befehlssequenz wird innerhalb der Prozedur SMTPCommands ausgeführt, diein SendEMail aufgerufen wird, nachdem die Verbindung zum SMTP-Server mit

erfolgreich aufgebaut werden konnte.

Widmen wir uns nun der Prozedur SMTPCommands, die noch einer kleinen Ein-schränkung unterliegt: Die zu versendende Mail muss nach den Richtlinien desRFC 822 verpackt werden. Dazu dient eine weitere private Methode, die ausSMTPCommands heraus aufgerufen wird: CreateMail. Das Thema, wie eine Nach-richt zu codieren ist, haben wir noch nicht erörtert und wird später folgen.Ansonsten ist SMTPCommands mit Ausnahme der genannten Unterprozedurbereits voll funktionsfähig.

Es ist interessant, das »Gespräch« der beiden Stellen miteinander zu verfolgen.Deshalb wird sowohl das vom Client abgesetzte Kommando als auch die gesamteAntwort des Servers an der Konsole angezeigt.

mySocket.Send <RCPT TO>mySocket.Receive <Servercode>mySocket.Send <DATA>mySocket.Receive <Servercode>mySocket.Send <QUIT>mySocket.Receive <Servercode>

'Verbindung aufnehmenIf OpenConnection() Then SMTPCommans()End If

Private Sub SMTPCommands() Dim message As String Try 'HELO – Kommando message = "HELO " & Mail-Server SendCommand(message) count = server.Receive(puffer) If Not DoServerAnswer() Then Exit Sub 'MAIL FROM – Kommando message = "MAIL FROM: <" & mailFrom & ">" SendCommand(message) count = server.Receive(puffer) If Not DoServerAnswer() Then Exit Sub 'RCPT TO – Kommando message = "RCPT TO: <" & mailTo & ">" SendCommand(message)

VBNET-14.fm Seite 917 Mittwoch, 27. März 2002 5:17 17

Page 43: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

918 TCP/IP-Programmierung

Insgesamt enthält die Prozedur relativ viel Programmcode, der bei einer genaue-ren Betrachtung aber in viele kleine, ähnlich oder sogar gleich strukturierte Passa-gen zerfällt, die nur mit unterschiedlichen Parametern arbeiten. Jeder dieserAbschnitte schickt einen Befehl an den verbundenen SMTP-Server und wartet aufdessen Antwort. Ziehen wir uns beispielhaft den Teil heraus, mit dem wir uns alsInhaber eines Postfach auf diesem Server identifizieren.

count = server.Receive(puffer) If Not DoServerAnswer() Then Exit Sub 'DATA – Kommando message = "DATA" SendCommand(message) count = server.Receive(puffer) If Not DoServerAnswer() Then Exit Sub 'die Mail verschicken Console.WriteLine("Client: Ich schicke die Mail") message = ConstructMail() SendCommand(message) count = server.Receive(puffer) If Not DoServerAnswer() Then Exit Sub 'QUIT – Kommando message = "QUIT" SendCommand(message) count = server.Receive(puffer) DoServerAnswer() If server.Connected Then server.Shutdown(SocketShutdown.Both) server.Close() End If Catch e As Exception Console.WriteLine(e.ToString()) End TryEnd Sub

'MAIL FROM – Kommandomessage = "MAIL FROM: <" & mailFrom & ">"'Kommando an den Server schickenSendCommand(message)'auf die Antwort des SMTP-Servers warten. Diese wird'in die Variable puffer geschriebencount = server.Receive(puffer)'Auswerten der ServerantwortIf Not DoServerAnswer() Then Exit Sub

VBNET-14.fm Seite 918 Mittwoch, 27. März 2002 5:17 17

Page 44: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

E-Mails verschicken 919

Zuerst wird in der Variablen message der String konstruiert, der im zweiten Schrittder Gegenstelle übermittelt wird. Der Server erwartet, dass jeder Befehl miteinem Zeichenumbruch abgeschlossen wird. Die Variable message enthält nochnicht diese Abschlusskennzeichnung. Wir hängen sie erst bei der Übermittlungan, wie gleich noch zu sehen sein wird.

Die Prozedur SendCommand erwartet als Parameter die Befehlszeichenfolge, sen-det diese unter Anhängung des Zeilenumbruchs an den Mail-Server, gibt denübermittelten String an der Konsole aus und kehrt zum Aufrufer zurück, der jetztnur noch darauf wartet, dass der Server eine Reaktion zeigt.

Zur Auswertung der Serverantwort bietet sich wiederum eine benutzerdefinierteProzedur an, die einen booleschen Wert zurückliefert. Der Name dieser Prozedurlautet DoServerAnswer. Ihr Rückgabewert ist True, wenn der Mail-Server mit einemZifferncode geantwortet hat, der einem OK entspricht. Verursacht das abgesetzteKommando einen Fehler, wird die Prozedur SendMail mit Exit Sub verlassen.

Auf dieselbe Weise wird auch mit allen anderen SMTP-Befehlen verfahren. Nurnach der Ankündigung, mit QUIT die Kommunikation zu beenden, spielt die Ant-wort des Servers keine ausschlaggebende Rolle mehr, da die Mail bereits auf demWeg ist. Wir sollten deshalb nur noch prüfen, ob das Socket die Verbindung nochaufrecht erhält. Dazu wird die Eigenschaft Connected des Socket-Objekts ausge-wertet. Wird die Verbindung noch aufrecht erhalten, können wir sie schließen.

Natürlich müssen wir nun noch einen Blick auf die beiden Prozeduren werfen, dieaus SendMail heraus aufgerufen werden. Beiden ist gemein, an der Konsole darü-ber zu informieren, welche Aktion gerade ausgeführt wird bzw. welches Ergebnisals Antwort auf die Aktion übermittelt wird.

In DoServerAnswer soll es uns genügen, nur die erste Ziffer der Serverantwort aus-zuwerten, da sich daraus bereits alles Wesentliche interpretieren lässt. Im Falleeines Fehlers kann das Socket ordnungsgemäß geschlossen werden und könnte– wenn wir den gesamten Code des Mailens in eine Schleife schreiben würden –erneut zum Verbindungsaufbau benutzt werden.

'QUIT – Kommandomessage = "QUIT"SendCommand(message)count = server.Receive(puffer)svrAnswer = Encoding.ASCII.GetString(puffer, 0, count)Console.WriteLine("Server: " & svrAnswer)If server.Connected Then server.Shutdown(SocketShutdown.Both) server.Close()End If

VBNET-14.fm Seite 919 Mittwoch, 27. März 2002 5:17 17

Page 45: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

920 TCP/IP-Programmierung

Der Methode SendCommand kommt nur die Aufgabe zu, eine Zeichenfolge, dieals Parameter beim Aufruf übergeben wird, in ein Byte-Array umzuwandeln, dasvom Mail-Server empfangenen wird.

14.3.4 Struktur einer E-Mail-Nachricht

Den Vorgang, eine Mail von einem Absender zu einem Empfänger zu schicken,kann man mit dem Versenden eines Briefes vergleichen. Die eigentliche Nachrichtsteckt in einem Briefumschlag und geht die am Versand beteiligten Personennichts an. Auf dem Briefumschlag sind die Adressen von Sender und Empfängerangegeben, damit der Brief entweder den zugedachten Empfänger erreicht oder –falls unzustellbar – an den Absender zurückgegeben werden kann. Die Dienstleis-tung des Transports übernimmt eine Institution wie die Post oder vielleicht einprivates Kurierunternehmen.

Bezogen auf das Versenden einer E-Mail übernimmt SMTP die Position desDienstleisters und der Nachrichtenkopf die des Briefumschlags, der nun zumThema wird.

Private Function DoServerAnswer() As Boolean DoServerAnswer = True svrAnswer = Encoding.ASCII.GetString(puffer, 0, count) Console.WriteLine("Server: " & svrAnswer) 'ist die erste Ziffer der Antwort 4 oder 5, hat der 'SMTP-Server einen Fehler entdeckt 'das clientseitige Socket kann dann geschlossen werden If svrAnswer.Substring(0, 1) > 3 Then DoServerAnswer = False server.Shutdown(SocketShutdown.Both) server.Close() Console.WriteLine("Die Verbindung wird geschlossen.") End IfEnd Function

Private Sub SendCommand(ByVal message As String) Dim command() As Byte 'abzusetzenden SMTP-Befehl an der Konsole ausgeben Console.WriteLine("Client: " & message) 'dem Befehl einen Zeichenumbruch anhängen und in ein 'Byte-Array umwandeln command = Encoding.ASCII.GetBytes(message & CrLf) 'Befehl übermitteln server.Send(command)End Sub

VBNET-14.fm Seite 920 Mittwoch, 27. März 2002 5:17 17

Page 46: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

E-Mails verschicken 921

Abbildung 14.3 Struktur einer E-Mail

Eine Mail, die über das Internet verschickt wird, besteht immer aus einem Nach-richtenkopf und dem Nachrichtenkörper. Dem Nachrichtenkopf kommt die Auf-gabe zu, den Mail-Routern Informationen im ASCII-Format für das Weiterleitenvon Nachrichten mitzuteilen. Die Mail-Router werten die Informationen imNachrichtenheader aus, können ihn sogar noch ergänzen, um später den Weg derMail in den Weiten des Internet verfolgen zu können. Damit der Nachrichtenkopffür die Mail-Router verständlich ist, ist dessen Syntax wieder eindeutig definiert.Es ist das schon erwähnte RFC 822.

Der Nachrichtenkörper geht nur den Sender und Empfänger etwas an – es han-delt sich hierbei um die eigentliche Mail. Das bedeutet aber nicht, dass es nichtauch hier Vereinbarungen gibt, an die sich Sender und Empfänger zu haltenhaben. Von diesen kann man natürlich abweichen, solange sich der Sender undder Empfänger über ein gemeinsames Format geeinigt haben. Hält man sich andie Konventionen, dürfen in einer Mail nur die Zeichen 0 bis 127 des ASCII-Codesverwendet werden. Für Mails, die neben ASCII-Zeichen auch andere Datenstruk-turen transportieren, ist der MIME-Standard (Multipurpose Internet Mail Exten-sions) festgelegt.

Der Nachrichtenkopf

Der Nachrichtenkopf setzt sich aus Feldern zusammen, die einer festgelegtenSyntax folgen müssen. Einige Felder bestimmen den minimalen Nachrichtenkopf,andere Felder sind optional. Wir wollen uns hier nur auf eine Auswahl beschrän-ken, die in der folgenden Tabelle enthalten sind. Die drei zuerst aufgeführten Fel-der FROM, TO und DATE bilden zusammen den Minimalkopf.

Nachrichtenkopf

Nachrichtenkörper

SMTP

Transport via SMTP

E-Mail

VBNET-14.fm Seite 921 Mittwoch, 27. März 2002 5:17 17

Page 47: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

922 TCP/IP-Programmierung

Die drei Felder FROM, TO, CC und BCC sind syntaktisch sehr ähnlich aufgebautund erinnern an die Kommandos von SMTP. Hinter der Feldangabe wird in spit-zen Klammern die E-Mail-Adresse angegeben, der im Klartext der Name des Post-fachbesitzers vorangestellt werden kann, z.B.

Ein ausgeschriebener Adressatenname erscheint beispielsweise unter MS-Out-look im Posteingang in der Spalte Von.

Mit SUBJECT wird optional der Inhalt der Betreffzeile als Zeichenfolge in denNachrichtenkopf übernommen; mit X-MAILER dürfen Sie Ihrem Mailprogrammsogar einen Namen geben, der beim Empfänger sichtliches Erstaunen auslösenkann, wenn das Eigenschaftsfenster der eingegangenen E-Mail geöffnet wird.

Nun sind wir fast dazu in der Lage, Nachrichtenkörper und Nachrichtenkörper zueiner Zeichenfolge zusammenzusetzen, die wir vollwertig senden können. DreiInformationen sind aber in diesem Zusammenhang wichtig:

� Hinter jeder Feldangabe ist ein Zeilenumbruch notwendig.

� Der Nachrichtenkörper wird vom Nachrichtenkopf durch eine Leerzeilegetrennt. Dadurch erkennt der Mail-Router, dass hier ein Bereich anfängt, derfür Sender und Empfänger privat ist.

� Die gesamte Nachricht wird nach dem SMTP-Kommando DATA abgesetzt –beide Partner wissen das. In ähnlicher Weise muss auch das Ende einer Mailgekennzeichnet werden, damit SMTP deren Ende erkennt und dem Absender

Feld Beschreibung

FROM: E-Mail-Adresse des E-Mail-Senders

TO: E-Mail-Adresse des Nachrichtenempfängers

DATE: Datum und Uhrzeit

CC: E-Mail-Adressen der zusätzlichen Empfänger dieser Nachricht.

BCC: E-Mail-Adressen der zusätzlichen Empfänger dieser Nachricht. Die hier ange-geben Empfänger werden allerdings nicht den unter TO und CC angegebenen E-Mail-Empfängern bekannt gegeben.

SUBJECT: Inhalt der Betreff-Zeile

X-MAILER: Name des Mailclients, mit dem die übermittelte Mail verfasst worden ist.

Tabelle 14.4 Felder des Nachrichtenkopfs einer E-Mail nach RFC 822

FROM: Bodo Ballermann <[email protected]>TO: Willi <[email protected]>BCC: Hanni <[email protected]>

VBNET-14.fm Seite 922 Mittwoch, 27. März 2002 5:17 17

Page 48: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

E-Mails verschicken 923

eine fehlerfreie Übermittlung signalisiert. Das RFC definiert dazu eine Zeichen-kombination, die an das Ende der Nachricht angehängt wird: den durch Zeilen-umbrüche eingeschlossenen Punkt, also <CRLF>.<CRLF>.

Tatsächlich könnte diese Kombination theoretisch auch innerhalb der Mail auftre-ten und zu einer Missinterpretation führen. Der SMTP-Client untersucht daherden Mailinhalt auf diese Kombination und fügt, falls er fündig wird, innerhalb derbeiden Zeilenumbrüche einen weiteren Punkt hinzu. Wir werden in unserenMail-Client diesen Sonderfall nicht berücksichtigen.

Wir können nun Nachrichtenkopf und Nachrichtenkörper zu der eigentlich zuversendenden E-Mail in der Prozedur ConstructMail zusammenfügen, die nachdem SMTP-Kommando DATA aufgerufen wird.

Fast haben wir unser Ziel erreicht. Es fehlt nur noch das DATE-Feld, das sich alsetwas komplizierter erweist, da es einem von dem RFC 822 exakt beschriebenenMuster folgen muss und beispielsweise wie folgt aussieht:

Zur Bildung der Zeit- und Datumsangabe kann zuerst optional der Wochentaggenannt werden. Das sich anschließende Datum muss von der Wochentagangabedurch ein Komma getrennt sein. Die Angabe des Wochentags benutzt die erstendrei Buchstaben des entsprechenden englischen Wochentagnamens, also Sun,Mon, Tue, Wed, Thu, Fri oder Sat.

Ähnlich wird auch die Angabe des Monats gestaltet. Auch hier dienen die erstendrei Buchstaben des englischen Monatsnamens (Jan, Feb, Mar usw.). Vor demMonat stehen 1 oder 2 Ziffern für den Monatstag, danach 2 oder 4 für das Jahr.

Public Function ConstructMail() As String Dim strMail As String 'Nachrichtenkopf strMail = "FROM: " & senderName & "<" & mailFrom & ">" & CrLf strMail &= "TO: <" & mailTo & ">" & CrLf strMail &= "DATE: " & MakeDate() & CrLf strMail &= "SUBJECT: " & betreff & CrLf strMail &= "X-MAILER: Tollsoft-MailV1.0" & CrLf 'Nachrichtenkörper strMail &= CrLf & mailText 'Nachrichtenabschluss strMail &= CrLf & "." & CrLf Return strMailEnd Function

DATE: Sun, 10 Feb 02 20:15:22 +0100

VBNET-14.fm Seite 923 Mittwoch, 27. März 2002 5:17 17

Page 49: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

924 TCP/IP-Programmierung

Nach dem Datum folgt die Uhrzeit. Stunden und Minuten werden jeweils durchzwei Ziffern beschrieben, die durch einen Doppelpunkt getrennt sind. Optionalist die Angabe der Sekunden, ebenfalls durch einen Doppelpunkt von den Minu-ten getrennt.

Den Abschluss des DATE-Feldes bildet die Angabe einer Zeitzone, entweder alsKürzel oder als numerische Angabe der Zeitdifferenz zur GMT. Die mitteleuropä-ische Zeit, die einer Stunde vor GMT liegt, wird mit +0100 beschrieben.

Public Function MakeDate() As String Dim getDate As String 'Wochentag bestimmen Select Case DateTime.Now.DayOfWeek Case DayOfWeek.Sunday : getDate = "Sun, " Case DayOfWeek.Monday : getDate = "Mon, " Case DayOfWeek.Tuesday : getDate = "Tue, " Case DayOfWeek.Wednesday : getDate = "Wed, " Case DayOfWeek.Thursday : getDate = "Thu, " Case DayOfWeek.Friday : getDate = "Fri, " Case DayOfWeek.Saturday : getDate = "Sat, " End Select 'den Tag festlegen getDate &= DateTime.Now.Day & " " 'Monat und Jahr Select Case DateTime.Now.Month Case 1 : getDate &= "Jan " & DateTime.Now.Year Case 2 : getDate &= "Feb " & DateTime.Now.Year Case 3 : getDate &= "Mar " & DateTime.Now.Year Case 4 : getDate &= "Apr " & DateTime.Now.Year Case 5 : getDate &= "May " & DateTime.Now.Year Case 6 : getDate &= "Jun " & DateTime.Now.Year Case 7 : getDate &= "Jul " & DateTime.Now.Year Case 8 : getDate &= "Aug " & DateTime.Now.Year Case 9 : getDate &= "Sep " & DateTime.Now.Year Case 10 : getDate &= "Oct " & DateTime.Now.Year Case 11 : getDate &= "Nov " & DateTime.Now.Year Case 12 : getDate &= "Dec " & DateTime.Now.Year End Select 'Zeit sowie Zeitzone getDate &= " " & DateTime.Now.ToShortTimeString & " +0100" Return getDateEnd Function

Anmerkung Den Code zu dem Beispiel MailClient finden Sie auf der Buch-CDunter \Beispielcode\Kapitel_14\MailClient.

VBNET-14.fm Seite 924 Mittwoch, 27. März 2002 5:17 17

Page 50: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

Ein »anderer« MailClient 925

14.4 Ein »anderer« MailClient

Damit sind wir nahezu am Ende dieses Buches angelangt. Das letzte Beispiel Mail-Client beweist, mit VB .NET sind wir in der Lage, auch für die Konsole durchausfunktionelle Anwendungen zu schreiben. Natürlich nehmen wir Einschränkungenin Kauf, denn unser Programm ist zugegebenermaßen nicht sehr komfortabel undein ganz wesentliches Feature, das alle Mailanwendungen auszeichnet, fehlt unsnoch: Wir sind nämlich nicht in der Lage, einer Nachricht Dateien anzuhängen.

Um das zu realisieren, wurde der MIME-Standard ins Leben gerufen, der dengrundsätzlichen Aufbau eines Nachrichtenkörpers beschreibt, sodass er von Sen-der und Empfänger gleichermaßen verstanden werden kann. Wenn Sie dieseFähigkeit noch programmieren wollen, müssen Sie auf die RFC's 1521 und 1522zurückgreifen und sich mit ihnen vertraut machen.

Wie intensiv haben Sie sich mit dem letzten Beispiel beschäftigt? Beantworten Siesich ernsthaft die Frage, ob Sie nur den Code verfolgt oder auch einen Blick in die.NET-Klassenbibliothek geworfen haben. Denn, so befriedigend unser Ergebnisam Ende auch sein mag, wir können unter Zuhilfenahme der Klassenbibliothekeine Mail versenden – und brauchen dazu nur zwei Zeilen Programmcode!

Dieser Code reicht tatsächlich aus – man mag es im ersten Moment kaum glau-ben. Weder das Absetzen der SMTP-Kommandos noch die Definition eines Nach-richtenkopfes ist notwendig, denn die Klasse SmtpMail übernimmt diese Arbeitfür uns. Eigentlich sollte uns die Einfachheit nicht erstaunen, da die von uns ent-wickelte Klasse ConsoleMail ein ähnliches Verhalten zeigt; auch wenn wir einwenig weiter gegangen sind und einige Daten serialisieren können.

Um den obigen Code ausführen zu können, müssen wir zwei Dinge im Vorfelderledigen:

� Die Klasse SmtpMail ist in keiner Datei enthalten, auf die standardmäßig ver-wiesen wird. Daher muss im Projektmappen-Explorer zuerst über Verweise dieDatei System.Web.dll eingebunden werden.

� Wir müssen bzw. sollten mit

den Namespace der Klasse importieren.

SmtpMail.SmtpServer = "mailto.t-online.de"SmtpMail.Send("[email protected]", "[email protected]", _ "Beurteilung", "Visual Basic ist spitze!")

Imports System.Web.Mail

VBNET-14.fm Seite 925 Mittwoch, 27. März 2002 5:17 17

Page 51: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

926 TCP/IP-Programmierung

Die Klasse veröffentlicht nur die Eigenschaft SmtpServer und die überladeneMethode Send.

Die Klassendefinition schreibt vor, die Adresse des Mailservers der EigenschaftSmtpServer zu übergeben, obwohl die Architekten diese auch zu einem Parame-ter der Send-Methode hätten machen können. Dann wäre das Versenden einerMail im Minimalfall sogar zu einer einzeiligen Anweisung geworden.

Die Parameter der Send-Methode nehmen die folgenden Angaben entgegen:

� 1. Parameter: E-Mail-Adresse des Absenders

� 2. Parameter: E-Mail-Adresse des Empfängers

� 3. Parameter: Inhalt der Betreff-Zeichenfolge

� 4. Parameter: der Nachrichtenkörper

Alle Klassenmitglieder von SmtpMail sind statisch deklariert und benötigen daherkein konkretes Objekt, obwohl die Klasse einen Konstruktor bereitstellt.

Sehen wir uns nun eine Version der überladenen Send-Methode an:

Als Argument wird die Referenz auf ein Objekt vom Typ MailMessage erwartet,der ebenfalls zum Namespace System.Web.Mail gehört.

Auffällig ist zunächst, dass diese Klasse nur Eigenschaften, aber keine Methodenimplementiert. Die verschiedenen Eigenschaften bieten Möglichkeiten, die sogarweit über das hinausgehen, was wir im vorhergehenden Abschnitt in unsererKlasse ConsoleMail implementiert haben, beispielsweise Cc, um eine Kopie derMail an andere Empfänger zu schicken, und Priority zur Kennzeichnung der Pri-orität einer Nachricht.

Der Clou der Klasse MailMessage steckt aber in der Eigenschaft Attachments, derSie eine Liste von Dateien übergeben, die als Anhang der Mail mit versendet werden.

Sie werden gleich sehen, wie genial einfach es ist, nicht nur eine einfache Text-nachricht, sondern auch gleichzeitig beliebig viele Dateien, Bilder usw. an denEmpfänger zu schicken. So erfreulich dieses Klassenfeature auch ist, die Doku-mentation zur Eigenschaft Attachments ist ausgesprochen schwach, denn sie ist

Public Shared Property SmtpServer As StringPublic Shared Sub Send (String, String, String, String)

Public Shared Sub Send(MailMessage)

Public ReadOnly Property Attachments As IList

VBNET-14.fm Seite 926 Mittwoch, 27. März 2002 5:17 17

Page 52: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

Ein »anderer« MailClient 927

unvollständig und es fehlt ein kleines Beispiel, an dem die Arbeitsweise der Eigen-schaft verdeutlicht wird. Wir sollten allerdings nicht verständnislos den Kopfschütteln, sondern Toleranz üben und uns vor Augen halten, dass wir eine völligneue Klassenbibliothek vorliegen haben.

Der Stein des Anstoßes ist die Anmerkung in der Dokumentation, die besagt, dassein Attachment durch den Aufruf von

an eine Mail gehängt werden kann. Die Eigenschaft repräsentiert die Referenz aufeine IList-Auflistung. Dabei könnte es sich beispielsweise um ein Objekt derKlasse ArrayList handeln. Das stimmt schon sehr gut mit der Aussage in derDokumentation überein, durch Aufruf von Add ein Element zu einem Mail-Attachment zu erklären.

Nehmen wir an, Sie möchten eine Datei als Anhang zu einer Mail verschicken. Siewissen nur, dass Sie die Methode Add aufrufen müssen, um die Datei zu einemverwalteten Element der Collection zu machen. Vielleicht erinnern Sie sich sogarauch noch, dass der Parameter von IList.Add vom Typ Object ist. Was nun? Siestehen vor dem Problem nach einem Weg zu suchen, um die Datei als Objekt dar-zustellen. Doch welche Möglichkeiten gibt es? Hier werden Sie auf ziemlich bru-tale Art alleine gelassen; die Dokumentation gibt keine Auskunft darüber. StoßenSie nicht per Zufall auf die richtige Lösung, bleiben Ihnen die Möglichkeiten derAttachments-Eigenschaft verschlossen.

Die Lösung ist ebenfalls im Namespace System.Web.Mail zu finden: es ist diedritte Klasse dieses Namespaces – MailAttachment. Sehen wir uns zunächst an,wie einer Mail endgültig ein Anhang beigefügt wird:

Sollen mehrere Dateien versendet werden, muss für jede Datei ein Aufruf erfolgen.

Ein Objekt der Klasse MailAttachment beschreibt eine Datei als Mailanhang.Dazu genügt es, nur den Konstruktor aufzurufen, dem als Argument ein Stringmit der Pfadangabe übergeben wird:

Nun haben wir alle Informationen zusammen, die wir benötigen, um eine Mailmit Anhängen via SMTP einem Empfänger zukommen zu lassen, ohne uns mitden einzelnen SMTP-Kommandos auseinander setzen zu müssen. Sehen wir unsdaher jetzt ein funktionsfähiges Codefragment an:

Message.Attachments.Add()

<MailMessage-Objekt>.Attachments.Add(<MailAttachment-Objekt>)

Public Sub New(String)

VBNET-14.fm Seite 927 Mittwoch, 27. März 2002 5:17 17

Page 53: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

928 TCP/IP-Programmierung

Zuerst wird die Klasse MailMessage instanziiert. Es soll genügen, die wichtigstenEigenschaften festzulegen, um die Nachricht ordnungsgemäß dem Empfängerzuzustellen. Dazu muss mit SmtpServer der Mail-Server, auf dem das Postfach desAbsenders geführt wird, bekannt gegeben werden sowie die Absender- und Emp-fängeradresse mit To und From. Die Nachricht, die dem Empfänger übermitteltwird, weisen wir als Zeichenfolge der Eigenschaft Body zu. Anschließend rufen wirzweimal die Methode Add des von Attachments referenzierten Auflistungsobjektsauf und erstellen implizit eine Referenz auf ein MailAttachment-Objekt. Eineimplizite Referenz ist vollkommen ausreichend, da wir im weiteren Verlauf nichtmehr auf dieses Objekt zugreifen werden.

Zum Schluss übergeben wir das fertig konstruierte MailMessage-Objekt der sta-tischen Methode Send der Klasse SmtpMail und – sind fertig. Vorausgesetzt, alleAngaben sind korrekt und wir sind mit dem Internet verbunden, wird die Nach-richt einschließlich der Anhänge dem Empfänger zugestellt.

14.4.1 Fazit

Sie werden mir zustimmen, einfacher geht es nicht mehr, eine Nachricht zu ver-senden. Wir sind nicht genötigt, viel Zeit zu investieren, um das angestrebte Zielzu erreichen und müssen uns nicht zwangsläufig mit den technologischen Detailsund Hintergründen der RFC's beschäftigen – das .NET-Framework bietet uns einefertige und komplette Lösung für die meisten Anforderungen in der Praxis an undlässt dennoch den Weg über die fundamentalen Sockets offen.

'MailMessage-Klasse instanzierenDim myMail As New MailMessage()'Adresse des Mail-ServersSmtpMail.SmtpServer = "smtp.oal.de"'AbsenderadressemyMail.To = "[email protected]"'EmpfängeradressemyMail.From = "[email protected]"'BetreffmyMail.Subject = "Mein Urteil"'NachrichtentextmyMail.Body = "VB ist super!"'zwei DateianhängemyMail.Attachments.Add(New MailAttachment("C:\logo.jpg"))myMail.Attachments.Add(New MailAttachment("C:\MyObject.dat"))'Mail sendenSmtpMail.Send(myMail)

VBNET-14.fm Seite 928 Mittwoch, 27. März 2002 5:17 17

Page 54: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

Ein »anderer« MailClient 929

Diese Aussage gilt nicht nur für das Versenden einer Mail, sondern auch für vieleandere Thematiken, mit denen wir uns aus Platzgründen in diesem Buch nichtauseinander setzen konnten. Das Problem, vor dem Sie immer wieder stehenwerden, ist aber dasselbe und wurde Ihnen in diesem Kapitel beispielhaft vorAugen geführt: Sie müssen die passende Klasse oder die Klassen finden, die diegewünschten Fähigkeiten bereitstellen. Es wird eine längere Zeit dauern, bis maneinen guten Überblick über die .NET-Klassenbibliothek bekommt, selbst wennman täglich damit arbeitet. Je mehr man sich aber damit beschäftigt, umso effizi-enter wird die Arbeit sein, umso stabiler der Programmcode. Trotz mancherSchwachstellen, die ein neues Produkt mit einer so komplexen Technologie wiedas .NET-Framework unweigerlich haben muss (und auch haben darf), eine Aus-sage kann auch schon in der Startphase des Frameworks getroffen werden: Mit.NET zu programmieren macht Spaß und Visual Baisc.NET ist spitze!

VBNET-14.fm Seite 929 Mittwoch, 27. März 2002 5:17 17

Page 55: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

Inhalt 5

Inhalt

Vorwort 15

1 Das .NET-Konzept 17

1.1 Ein Wort zu diesem Buch 17

1.2 Das Entwicklerdilemma 221.2.1 Das .NET-Konzept 231.2.2 Einarbeitungszeit 24

1.3 Das Sprachenkonzept 261.3.1 NET-Anwendungsentwicklung 261.3.2 Die Common Language Specification 271.3.3 Das Common Type System 29

1.4 Das .NET-Framework 311.4.1 Die Common Language Runtime 311.4.2 Die .NET-Klassenbibliothek 32

1.5 Assemblies 36

2 Die Entwicklungsumgebung 39

2.1 Anmerkungen 39

2.2 Hard- und Softwareanforderungen 39

2.3 Die Installation 41

2.4 Die Entwicklungsumgebung des VS .NET 452.4.1 Die VB .NET-Vorlagetypen 462.4.2 Die Oberfläche der Entwicklungsumgebung 482.4.3 Der Code-Editor (Text-Editor) 482.4.4 Der Projektmappen-Explorer 512.4.5 Die Klassenansicht 512.4.6 Das Eigenschaftsfenster 522.4.7 Die Werkzeugsammlung (Toolbox) 532.4.8 Der Server-Explorer 542.4.9 »Dynamische Hilfe« und »Suchen« 552.4.10 Das Fenster »Inhalt« 56

2.5 Das Entwickeln einer Konsolenanwendung 572.5.1 Zum Abschluss 60

3 Grundlagen der Sprachsyntax 63

3.1 Konsolenanwendungen 633.1.1 Allgemeine Anmerkungen 633.1.2 Der Projekttyp Konsolenanwendung 64

Page 56: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

6 Inhalt

3.1.3 Die Vorlage »Konsolenanwendung« 653.1.4 Zusammenfassung 68

3.2 Variablen und Datentypen 693.2.1 Explizite und implizite Variablendeklaration 693.2.2 Die Variablendeklaration 773.2.3 Die nativen Datentypen 813.2.4 Sichtbarkeit und Lebensdauer 933.2.5 Module in der Entwicklungsumgebung 983.2.6 Initialisierung von Variablen 1043.2.7 Datentypkonvertierung 1053.2.8 Typkennzeichen 1113.2.9 Konstanten 1133.2.10 Ein- und Ausgabemethoden der Klasse Console 1133.2.11 Zusammenfassung 119

3.3 Datenfelder (Arrays) 1213.3.1 Eindimensionale Arrays 1213.3.2 Mehrdimensionale Arrays 1253.3.3 Ändern der Arraykapazität 1263.3.4 Initialisierung der Arrayelemente 1283.3.5 Bestimmung der Arrayobergrenze 1303.3.6 Zusammenfassung 132

3.4 Operatoren 1333.4.1 Arithmetische Operatoren 1333.4.2 Relationale Operatoren 1373.4.3 Logische Operatoren 1393.4.4 Zuweisungsoperatoren 1433.4.5 Verkettungsoperatoren 1453.4.6 Operatorprioritäten 1453.4.7 Bitweise Operationen 1463.4.8 Zusammenfassung 151

3.5 Kontrollstrukturen 1523.5.1 Die If-Anweisung 1523.5.2 Select-Case-Anweisung 1563.5.3 Einzeilige Entscheidungsanweisungen 1593.5.4 Zusammenfassung 164

3.6 Programmschleifen 1653.6.1 Die For…Next-Schleife 1653.6.2 Do-Schleifen 1693.6.3 Die While-Schleife 1733.6.4 Variablendeklaration in Anweisungsblöcken 1743.6.5 Zusammenfassung 175

3.7 Funktionen und Prozeduren 1763.7.1 Prozeduren 1773.7.2 Funktionen 1803.7.3 Prozedur- und Funktionsaufrufe 184

Page 57: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

Inhalt 7

3.7.4 Die Parameterliste 1853.7.5 Zusammenfassung 204

4 Klassen und Objekte (Teil 1) 205

4.1 Einführung in die Objektorientierung 2054.1.1 Klassen und Objekte 2064.1.2 Das objektorientierte Paradigma 2084.1.3 Vorteile der objektorientierten Programmierung 2124.1.4 Zusammenfassung 213

4.2 Die Klassendefinition 2144.2.1 Zugriffsmodifizierer einer Klasse 2164.2.2 Projektmappen-Explorer und Klassenansicht 220

4.3 Die Deklaration von Objektvariablen 2234.3.1 Frühe und späte Bindung 2254.3.2 Lebensdauer und Sichtbarkeit von Objektvariablen 2264.3.3 Zerstören einer Objektreferenz 2284.3.4 Mehrere Referenzen auf ein Objekt 2294.3.5 Den Typ einer Objektreferenz ermitteln 2304.3.6 Typvergleiche von Objektreferenzen 2314.3.7 Referenzvergleiche 2334.3.8 Das Clonen von Objekten 2394.3.9 Deklaration von Objekt-Arrays 2424.3.10 Zusammenfassung 245

4.4 Objekteigenschaften 2474.4.1 Datenkapselung 2484.4.2 Ergänzung der Klasse Circle 2544.4.3 Lese- und schreibgeschützte Eigenschaften 2564.4.4 Die Parameterliste einer Property-Prozedur 2584.4.5 Standardeigenschaften 2594.4.6 Das With...End With-Statement 261

4.5 Objektmethoden 2634.5.1 Methodenüberladung 2644.5.2 Aufruf überladener Methoden mit impliziter Konvertierung 2664.5.3 Objektreferenzen als Übergabeparameter 2684.5.4 Der Methodenzugriff auf private Daten 2694.5.5 Methode oder Eigenschaft? 2714.5.6 Das Schlüsselwort »Me« 2724.5.7 Die Trennung von Daten und Code 2744.5.8 Der aktuelle Stand der Klasse Circle 2754.5.9 Zusammenfassung 277

4.6 Konstruktoren und Destruktoren 2784.6.1 Konstruktoren 2784.6.2 Der Destruktor – Der Finalizer 2824.6.3 Der aktuelle Stand der Klasse Circle 2924.6.4 Zusammenfassung 294

Page 58: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

8 Inhalt

5 Klassen und Objekte (Teil 2) 295

5.1 Statische Klassenkomponenten 2955.1.1 Zugriff auf statische Komponenten 2975.1.2 Statische Klassenvariablen 2985.1.3 Klassenspezifische Methoden 3005.1.4 Statische Methoden in der Klasse Circle 3025.1.5 Statische Klasseninitialisierer 3065.1.6 Konstanten als Sonderform der Klassenvariablen 3075.1.7 Standardmodule als Sonderform von Klassen 3085.1.8 Der aktuelle Stand der Klasse Circle 3095.1.9 Zusammenfassung 313

5.2 Ereignisse eines Objekts 3145.2.1 Ergänzung eines Ereignisses in der Klasse Circle 3155.2.2 Die Behandlung eines Ereignisses im Ereignisempfänger 3175.2.3 Ereignisse mit Übergabeparametern 3215.2.4 Die Handles-Klausel 3235.2.5 Zusammenfassung 328

5.3 Strukturen – eine Sonderform der Klassen 3295.3.1 Die Definition einer Struktur 3295.3.2 Variablen vom Typ einer Struktur 3325.3.3 Die anwendungsübergreifende Sichtbarkeit 3355.3.4 Unterscheidungsmerkmale Klasse – Struktur 3365.3.5 Zusammenfassung 338

5.4 Enum-Auflistungen 3395.4.1 Wertzuweisung an Enum-Mitglieder 340

5.5 Referenz- und Wertetypen 3415.5.1 Die Boxing-Konvertierung 3435.5.2 Die Unboxing-Konvertierung 3455.5.3 Zusammenfassung 346

5.6 Namensbereiche (Namespaces) 3475.6.1 Zugriff auf Namespaces 3495.6.2 Das Imports-Statement 3505.6.3 Namespaces und Aliasnamen 3535.6.4 Standardmäßig importierte Namespaces 3535.6.5 Das Erstellen von Namespaces 3555.6.6 Eingebettete Namespaces 3565.6.7 Der aktuelle Stand der Klasse Circle 3585.6.8 Zusammenfassung 362

6 Vererbung und Polymorphie 363

6.1 Die Grundlagen der Vererbung 3636.1.1 Die Ableitung einer Klasse 3656.1.2 Klassen, die nicht vererben können 369

6.2 Konstruktoren in Subklassen 370

Page 59: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

Inhalt 9

6.2.1 Allgemeines 3706.2.2 Die Konstruktoren der Klasse GraphicCircle 3706.2.3 Der Zugriffsmodifizierer Protected 3716.2.4 Konstruktorverkettung 3726.2.5 Die Konstruktoren der Klasse GraphicCircle 3776.2.6 Finalizer-Verkettung 3776.2.7 Alle Zugriffsmodifizierer auf einen Blick 3796.2.8 Zusammenfassung 381

6.3 Methodenergänzung in einer Subklasse 3826.3.1 Die Windows-Testanwendung des CircleApplication-Projekts 3836.3.2 Erläuterung des vorläufigen Programmcodes der Draw-Methode 3876.3.3 Die endgültige Implementierung der Draw-Methode 392

6.4 Die Methoden einer abgeleiteten Klasse 3946.4.1 Das Überschreiben von Basisklassenmethoden mit Overloads 3946.4.2 Überladen der Basisklassenmethoden 3986.4.3 Das Schlüsselwort Shadows 3996.4.4 Die Vererbung statischer Mitglieder 401

6.5 Aggregation 4036.5.1 Beispielanwendung Aggregation 406

6.6 Typumwandlung von Objektvariablen 4116.6.1 Die implizite Typumwandlung von Objektreferenzen 4116.6.2 Die explizite Typumwandlung von Objektreferenzen 4136.6.3 Zusammenfassung 417

6.7 Abstrakte Klassen und Methoden 4186.7.1 Objektbestimmung mittels Polymorphie 4216.7.2 Polymorphe Methoden 4256.7.3 Das Schlüsselwort "MyClass" für Sonderfälle 427

6.8 Die Erweiterung Klassenhierarchie der CircleApplication 4306.8.1 Zusammenfassung 445

7 Schnittstellen und Delegates 447

7.1 Einführung in die Schnittstellen 4477.1.1 Schnittstellendeklaration 4487.1.2 Schnittstellenimplementierung 4497.1.3 Abstrakte Klassen vs. Schnittstellen 4587.1.4 Zusammenfassung 467

7.2 Delegates 4687.2.1 Problembeschreibung 4687.2.2 Ein erster Lösungsansatz 4697.2.3 Einfache Delegates 4717.2.4 Delegates als flexible Lösung 4757.2.5 Multicast-Delegates 4787.2.6 Allgemeine Anmerkungen zu Delegates 4807.2.7 Delegates zur synchronen und asynchronen Benachrichtigung 483

Page 60: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

10 Inhalt

7.2.8 Delegates und Events 4887.2.9 Der Sonderfall eines Events in einer Struktur 4947.2.10 EventArgs und EventHandler 4947.2.11 Zusammenfassung 500

8 Fehlerbehandlung 501

8.1 Allgemeines 501

8.2 Laufzeitfehler behandeln 503

8.3 Unstrukturierte Fehlerbehandlung 5068.3.1 Das Err-Objekt 5078.3.2 Weitere Möglichkeiten 5128.3.3 Zusammenfassung 514

8.4 Strukturierte Fehlerbehandlung 5158.4.1 Try-Catch-Anweisung 5158.4.2 Die Finally-Anweisung 5208.4.3 Das Weiterleiten von Ausnahmen 5218.4.4 Die Hierarchie der Exceptions 5288.4.5 Benutzerdefinierte Exceptions 5328.4.6 Die benutzerdefinierte Filterung der Ausnahmebehandlung 5368.4.7 Zusammenfassung 538

9 Multithreading 539

9.1 Prozesse, Anwendungsdomänen und Threads 5399.1.1 Multitasking und virtueller Speicherraum 5399.1.2 Multithreading 5429.1.3 Threadzustände und Prioritäten 5449.1.4 Einsatz von mehreren Threads 5459.1.5 Anwendungsdomänen 5469.1.6 Zusammenfassung 550

9.2 Die Entwicklung einer Multithread-Anwendung 5519.2.1 Die Klasse System.Threading.Thread 5559.2.2 Threadpools 5679.2.3 Zusammenfassung 568

9.3 Die Synchronisation von Threads 5699.3.1 Unsynchronisierte Threads 5699.3.2 Der Monitor zur Synchronisation 571

9.4 Asynchrone Aufrufe 5829.4.1 Eine kleine Einführung 5829.4.2 Asynchroner Methodenaufruf 5839.4.3 Das Ende des asynchronen Aufrufs 5869.4.4 Asynchroner Aufruf mit Rückgabewerten 5889.4.5 Eine Klasse mit asynchronen Methodenaufrufen 5919.4.6 Zusammenfassung 596

Page 61: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

Inhalt 11

10 Fundamentale Klassen des .NET-Frameworks 597

10.1 Allgemeines 597

10.2 Die Klasse Object 59910.2.1 Der Konstruktor 59910.2.2 Die Methoden 59910.2.3 Zusammenfassung 611

10.3 Die Klassen der Wertetypen 61210.3.1 Die nativen Datentypen 61310.3.2 Allgemeine Informationen 61610.3.3 Ausgabeformatierung 61810.3.4 Zusammenfassung 622

10.4 Die String-Klasse 62310.4.1 Das Erzeugen eines Strings 62310.4.2 Unveränderliche String-Objekte 62510.4.3 Die Eigenschaften eines String-Objekts 62610.4.4 Die Methoden der Klasse String 62810.4.5 Zusammenfassung der Klasse String 643

10.5 Die Klasse StringBuilder 64510.5.1 Die Konstruktoren der Klasse StringBuilder 64610.5.2 Die Eigenschaften der Klasse StringBuilder 64610.5.3 Die Methoden der Klasse StringBuilder 64710.5.4 Allgemeine Anmerkungen 65010.5.5 Zusammenfassung 651

10.6 Die Klasse Char 652

10.7 Die Klasse DateTime 65310.7.1 Die Konstruktoren der Klasse DateTime 65410.7.2 Die Eigenschaften der Klasse DateTime 65610.7.3 Die Methoden der Klasse DateTime 658

10.8 Die Klasse TimeSpan 66210.8.1 Zusammenfassung 666

10.9 Die Klasse Array 66710.9.1 Das Erzeugen eines Array-Objekts 66810.9.2 Die Eigenschaften eines Array-Objekts 66910.9.3 Die Methoden der Klasse Array 67010.9.4 Zusammenfassung 681

10.10 Objektauflistungen (Collections) 68210.10.1 Die Schnittstelle IList 68610.10.2 Die Schnittstelle IDictionary 69610.10.3 Die Klassen Queue und Stack 70810.10.4 Objektauflistungen im Überblick 71110.10.5 Zusammenfassung 713

Page 62: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

12 Inhalt

11 Dateien und Streams 715

11.1 Allgemeine Einführung 715

11.2 Dateien und Verzeichnisse 71711.2.1 Die Klasse File 71711.2.2 Die Klasse System.IO.FileInfo 72711.2.3 Die Klassen Directory und DirectoryInfo 73011.2.4 Die Klasse System.IO.Path 73511.2.5 Zusammenfassung 739

11.3 Die Stream-Klassen 74011.3.1 Die Klasse Stream 74111.3.2 Die abgeleiteten Stream-Klassen 74311.3.3 Die Klasse FileStream 74411.3.4 Zusammenfassung 754

11.4 Die Reader- und Writer-Klassen 75511.4.1 Die Klassen TextReader und TextWriter 75611.4.2 Die Klasse StreamWriter 75711.4.3 Die Klasse StreamReader 76211.4.4 Die Klassen StringWriter und StringReader 76611.4.5 Die Klassen BinaryReader und BinaryWriter 76711.4.6 Komplexe binäre Dateien 77111.4.7 Zusammenfassung 779

12 Serialisierung und Attribute 781

12.1 Einführung in die Serialisierung 78112.1.1 Einfache Serialisierung 78212.1.2 Benutzergesteuerte Serialisierung 79212.1.3 Beispiel einer benutzergesteuerten Serialisierung 79512.1.4 Zusammenfassung 799

12.2 Attribute 80012.2.1 Was ist ein Attribut? 80012.2.2 Beispiel eines benutzerdefinierten Attributs 80212.2.3 Fortsetzung des UserSerializationAttribut-Beispiels 80912.2.4 Zusammenfassung 816

13 Assemblies und Verteilung 817

13.1 Die COM-Technologie 81713.1.1 Allgemeines 81713.1.2 Überblick über die COM-Technologie 81913.1.3 Die Registrierung von COM-Komponenten 82113.1.4 Die Problematik mit COM 824

13.2 Das Konzept der Assemblies 826

13.3 Der Inhalt einer Assembly 829

Page 63: VB.NET - TCP/IP-Programmierung (Galileo Computing)download.microsoft.com/.../3898421295_14_TCP-IP_Galileo_Computin… · 878 TCP/IP-Programmierung Basis erfolgen, die von allen am

Inhalt 13

13.3.1 Die Struktur einer Assembly 82913.3.2 Manifest und Metadaten 831

13.4 Einzeldatei-Assemblies 837

13.5 Mehrdateien-Assemblies 84013.5.1 Manifest und MSIL-Code in einer Datei 84113.5.2 Zusammenfassung der Schritte 85013.5.3 Das Manifest in einer separaten Datei 850

13.6 Das Verteilen von Assemblies 85313.6.1 Gemeinsame genutzte Assemblies 85413.6.2 Das Erstellen einer globalen Assembly 85713.6.3 Eine gemeinsam benutzte Assembly in einer Anwendung 86413.6.4 Versionierung 864

13.7 Globale Assemblies im praktischen Einsatz 86613.7.1 Die Entwicklung einer globalen Assembly 86613.7.2 Der Endanwender von MeiersClientApp 86813.7.3 Ein zweiter .NET-Anwendungsentwickler 86813.7.4 Die überarbeitete Version einer globalen Assembly 86813.7.5 Zwei gleichnamige, versionsverschiedene Komponenten auf einem

Rechner 87013.7.6 Konfiguration einer Anwendung 87113.7.7 Die Attribute oldVersion und newVersion 87413.7.8 Die Entwicklung von Konfigurationsdateien 87513.7.9 Die Beispiele auf der CD 875

14 TCP/IP-Programmierung 877

14.1 Ein paar fundamentale Netzwerkgrundlagen 87714.1.1 Das WinSock-API 87714.1.2 TCP, UDP und IP 87814.1.3 Das Client-Server-Prinzip 88014.1.4 Zusammenfassung 882

14.2 Netzwerkprogrammierung mit dem .NET-Framework 88314.2.1 Einfacher Verbindungsaufbau 88314.2.2 Der Datenaustausch zwischen Client und Server 89214.2.3 Kommunikation zwischen zwei TCP-Partnern 89614.2.4 Zusammenfassung 903

14.3 E-Mails verschicken 90414.3.1 Einleitung 90414.3.2 Die Anforderungen an das Mail-Programm 90514.3.3 Der Transport einer E-Mail 90614.3.4 Struktur einer E-Mail-Nachricht 920

14.4 Ein »anderer« MailClient 92514.4.1 Fazit 928