Das Winsock-Control verwenden

Intro

Wenn man mich bisher gefragt hat, wie man mit dem Winsock-Control umgeht habe ich immer geantwortet "Suche in der MSDN nach Chat". Doch mittlerweile wurde mir gesagt, dass diese Samples da nicht unbedingt einfach zu verstehen seien. Auch wird die Möglichkeit, mehrere Benutzer gleichzeitig zu "verarbeiten" nur am Rande erwähnt. Einen Chat, der mehrere Benutzer zulässt werde ich in der nächsten Folge zeigen.

Formular zusammenstellen

Als erstes solltest du das Formular zusammenstellen. Um die umfangreichen Erklärungen zu deren Aufbau sparen zu können, biete ich das Formular zum Download an. Die Form in der ZIP-File enthält noch keinerlei Code.

Listen und Connect. Hu?

Um mit dem Winsock-Control umgehen zu können ist das Verständnis zweier Methoden wichtig. Es geht um die Methoden Connect und Listen.
Die Methode Listen wird verwendet, um als Server zu fungieren. Die Methode Connect um zu einem Server zu verbinden. Ich verwende dafür normalerweise die folgende Vorstellung: Ein Webserver wird beim Programmstart ein Listen im Code enthalten. Während ein Browser bei jedem Zugriff auf eine Website einen Connect ausführt. Im Multi-User-Chat, den ich in der nächsten Folge zeigen werde, sieht das dann so aus, dass ein Benutzer als Server fungiert. Diese Programminstanz wird die Methode Listen verwenden. Alle User, die nun an dem Chat teilnehmen wollen, werden einen Connect ausführen müssen.

Senden von Daten

Nachdem eine Verbindung besteht (dazu nachher mehr) können Daten ausgetauscht werden. Die geschieht sehr einfach mit der Methode SendData, welcher die zu sendenden Daten als String übergeben werden. Allerdings gibt es hier einige Tücken zu beachten. Wenn diese Funktion in einer Schleife x-mal nacheinander ausgeführt wird, kommt es zu Fehlern. Denn beim Senden der Daten kann (und wird es im Allgemeinen) zu Verzögerungen kommen. Deshalb arbeite ich mit einer Funktion, welche eine Pause einlegt, bis die Daten wirklich gesendet sind. Diese Funktion sieht folgendermassen aus: Private Sub Senden(strData As String) blnSendCompleted = False wsckChat.SendData strData Do Until blnSendCompleted DoEvents Loop End Sub Erklärungen: Die Variable blnSendCompleted ist im Formular-Kopf als Boolean definiert. Diese wird in der Sub wsckChat_SendComplete auf True gesetzt. Das Event SendComplete des Winsock-Controls wird ausgelöst, wenn die Daten fertig gesendet sind. Das ist die beste mir bekannte Möglichkeit, für das Senden von Daten.

Nachrichten anzeigen

In diesem Beispiel wird für das Anzeigen von Nachrichten eine Multiline-TextBox verwendet. Das ist zwar keine sehr komfortable Lösung, aber auch nicht wirklich schlecht. Und schliesslich soll es ja nur ein Beispiel sein ;-) Auch das Anzeigen von Nachrichten wurde in eine Sub ausgelagert: Private Sub AddMessage(strMessage As String, strSender As String) txtChat.Text = txtChat.Text & _ "(" & Trim(strSender) & ") " & _ strMessage & vbCrLf txtChat.SelStart = Len(txtChat.Text) End Sub Erklärungen: Dieser Code ist nicht sonderlich aufregend. Als erstes wird der neue Text inklusive Absender in der TextBox ans Ende geschrieben. Danach wird noch sichergestellt, dass auch das Ende der TextBox sichtbar ist.

Endlich: Die Methode Listen verwenden

Nun sind wir soweit. Wenn der Benutzer auf den Button cmdListen klickt, soll das Programm soweit vorbereitet werden, dass ein andere Benutzer connecten kann. Stell dir das aber nicht zu schwierig vor. Der folgende Code ist nötig: Private Sub cmdListen_Click() strNick = Left(txtName & Space(50), 50) wsckChat.LocalPort = 12123 wsckChat.Listen End Sub Erklärungen: Die erste Zeile hat nur die Aufgabe, in der Variable strNick den Nick zu speichern, der im Chat verwendet wird.
Für den Chat absolut nötig, und deshalb auch interessant, sind nur die beiden letzten Zeilen. Die Eigenschaft LocalPort legt fest, auf welchem Port das Programm laufen soll. Die Ports sind nötig, um verschiedenen Netzwerk-Programme auf einem Rechner aufs mal laufen lassen zu können. Oder hast du schon mal ernsthaft überlegt, wie das Betriebssystem denn weiss, dass die reinkommenden Daten jetzt für dem Browser und nicht dem Mail-Client sind? Das geschieht durch die verschiedenen Ports (Beim Web-Server z.B. meistens der Port 80)
Die Methode Listen sagt schliesslich, dass das Programm jetzt bitte auf eine reinkommende Verbindung warten soll.

Wow, es nimmt einer Kontakt auf. Aber was jetzt?

Wenn wir jetzt die Methode Listen aufgerufen haben, kann man noch nicht chatten. Schliesslich muss erst ein andere Rechner Kontakt aufnehmen. Das geschieht über die Methode Connect (kommt weiter unten). Wenn nun ein Client Kontakt aufnimmt, feuert das Winsock-Control das Event ConnectionRequest. Der Code sieht folgendermassen aus: Private Sub wsckChat_ConnectionRequest(ByVal requestID As Long) If wsckChat.State = sckListening Then wsckChat.Close wsckChat.Accept requestID MsgBox "Es wurde eine Verbindung aufgenommen" End If End Sub Erklärungen: Die IF-Abfrage dient dazu, Fehler zu verhindern. Hinweise dazu findest du im kommentierten Quellcode.
Wichtig ist die Methode Accept. Mit der wird eine reinkommende Verbindung angenommen.

Was nützt mir ein Server ohne Client?

Die Antwort ist "Rein gar nix". Deshalb wollen wir uns mal an den Client-Part machen. Da kommt die oft erwähnte Methode Connect zum Einsatz. Private Sub cmdConnect_Click() Dim strRemoteComputer As String strNick = Left(txtName & Space(50), 50) strRemoteComputer = InputBox("RemoteHost:") If Trim(strRemoteComputer) = "" Then Exit Sub wsckChat.RemotePort = 12123 wsckChat.RemoteHost = strRemoteComputer wsckChat.Connect End Sub Erklärungen: Um mit einem anderen PC zu connecten, muss natürlich dessen IP bekannt sein. Die IP wird über die InputBox eingegeben. Die Eigenschaft RemoteHost gibt an, zu welchem Rechner verbunden werden soll. Danach muss dem Winsock-Control bekanntgegeben werden, auf welchem Port es verbinden soll. Die Eigenschaft RemotePort ist dafür zuständig. Der Wert, welcher hier verwendet wird, muss gleich sein, wie der Wert LocalPort, bei Listen.
Nachdem diese beiden Eigenschaften gesetzt wurden, wird die Methode Connect aufgerufen. (Anmerkung: Die beiden Werten könnten auch direkt der Methode übergeben werden. Das habe ich hier aber aus Lernzwecken nicht so gelöst)
Das Programm ist jetzt soweit, dass zwei Rechner miteinander Kontakt aufnehmen können. Allerdings werden noch keine Nachrichten gesendet bzw. ausgewertet. Das folgt in den beiden nächsten Abschnitten.

Nachrichten senden

Am Anfang dieses Tutorials habe ich die Sub Senden vorgestellt. Diese soll nun verwendet werden. Folgender Code ist für den Button cmdSend nötig. Private Sub cmdSend_Click() Dim strMessage As String strMessage = strNick & _ txtNachricht.Text Senden strMessage AddMessage txtNachricht.Text, strNick End Sub Erklärungen: In der Sub wird erst die Nachricht zusammengebaut. Danach wird sie versendet, und zuletzt wird der Text noch im Chat-Fenster angezeigt. Übers Netzwerk gesendet werden immer der Nick und die Nachricht.

Die Nachrichten empfangen

Die Nachrichten werden zwar versendet, aber sie werden noch nicht angezeigt. So bringt uns der Chat natürlich nicht sehr viel. Aber mit dem folgenden Code wird alles gut... ;-) Private Sub wsckChat_DataArrival(ByVal bytesTotal As Long) Dim strData As String Dim strNick As String Dim strMessage As String wsckChat.GetData strData strNick = Trim(Mid(strData, 1, 50)) strMessage = Mid(strData, 51) AddMessage strMessage, strNick End Sub Erklärungen: Das Event DataArrival wird immer ausgelöst, wenn Daten reinkommen. Hier müssen die Daten entgegengenommen und verarbeitet werden.
Ins Programm rein bekommen wir die Daten mit der Methode GetData. Dieser wird eine Variable übergeben, in welcher die Daten gespeichert werden sollen. Dank dem Code strNick = Left(txtName & Space(50), 50) weiter oben ist sichergestellt, dass der Nick genau 50 Zeichen lang ist. Deshalb können wir aus den reingekommenen Daten die ersten 50 Zeichen abschneiden um den Nick zu erhalten. Ab Zeichen 51 ist dann die gesendete Nachricht gespeichert. Deshalb die beiden Mid-Funktionen.
Mit einem Aufruf unserer Sub AddMessage wird die Nachricht ins Chat-Fenster eingefügt.
Jetzt kannst du den Chat starten! Bereits können zwei Personen miteinander chatten. Allerdings ist der Chat sehr primitiv.

Verbindung schliessen

Nach dem Motto "Einen hab' ich noch" zeige ich zum Schluss, wie die Verbindung geschlossen wird: Private Sub cmdCancel_Click() If wsckChat.State <> sckClosed Then wsckChat.Close End Sub Erklärungen: Über die Eigenschaft State können wir den Status des Winsock-Controls abfragen. Der Wert sckClosed bedeutet soviel wie "Die Verbindung ist total geschlossen". Durch die IF-Abfrage wird also festgestellt, ob die Verbindung noch geöffnet ist. Wenn ja, dann wird sie mit dem Aufruf der Methode Close geschlossen.

Downloads

Zu diesem Artikel stehen zwei Downloads zur Verfügung:
Formular: Das fertige Formular. Ohne Code
Ganzes Projekt: Das gesamte Projekt. Mehr Code, als hier erklärt.

Schlusswort und Ausblick

Ich hoffe, dieser Artikel hat dir gezeigt, wie einfach die Programmierung mit dem Winsock-Control ist. Falls du Fragen zu diesem Tutorial oder zur Winsock-Programmierung im Allgemeinen hast, so schreib mir doch einfach eine Mail.