Intro
In der letzten Folge habe ich
gezeigt, wie Du mit dem Winsock-Control einen einfachen Chat
programmieren kannst. In dieser Folge gehe ich ein wenig
weiter. Dieses mal ist ein Chat an der Reihe, in dem mehrere
Personen zusammen chatten können.
Wenn Du den vorigen Artikel nicht gelesen hast, würde ich das
empfehlen.
Formular zusammenstellen
Auch dieses mal habe ich das Formular wieder zum Download bereit gestellt.
Ein paar Erklärungen
Bei dem Chat, den ich hier vorstelle, ist ein Chat-Server
nötig. Dessen IP muss jedem Chatter bekannt sein. Danach
können alle Benutzer zu diesem Server verbinden, um
miteinander zu chatten. Der Server muss also die verschiedenen
Benutzer verwalten.
Nun kann aber mit dem Winsock-Control nur eine Verbindung
geöffnet werden. Wie ist es denn möglich, mehrere
Benutzer auf den Server zu lassen? Die Antwort ist einfach:
Mit Hilfe eines Steuerelementfeldes (Array). Zur Laufzeit
werden dynamisch Winsock-Controls hinzugeladen. Mit jedem
geladenen Control kann genau eine Verbindung geöffnet
sein. Aber der Reihe nach...
Der Nachrichtenaufbau
Bei unserem ersten Beispiel war der Aufbau der Nachrichten so einfach wie es nur geht. Erst 50 Zeichen für den Namen und dahinter kam die Nachricht. Das reicht für dieses Beispiel nicht mehr. Denn jetzt identifizieren sich die Clients bei dem Server. Deshalb wird das erste Zeichen der Datei dazu verwendet, den Nachrichten-Typ zu kennzeichnen. Wenn dieses erste Zeichen eine Null ist, handelt es sich um eine Nachricht eines Benutzers. (z.B. "Hallo Hansi") Steht jedoch eine Eins am Anfang, so handelt es sich um eine Identifikation. Bei dieser werden 51 Zeichen übermittelt. (Die Eins und der Name des Benutzers)
Nachrichten durch die Leitungen senden
Das Versenden von Nachrichten übernimmt wieder die
Prozedur Senden, an der wir jedoch leichte Änderungen
vornehmen müssen, da die Winsock-Controls neu mit einem Index
versehen sind. Sehen wir uns die Sub mal an:
Sub Senden(strData As String, Optional Index As Integer = 0)
blnSendCompleted = False
If wsckChat(Index).State = sckConnected Then
wsckChat(Index).SendData strData
Do Until blnSendComplted
DoEvents
Loop
End If
End Sub
Erklärungen: Geändert hat sich, dass neu ein Parameter Index
übergeben wird. Dieser ist jedoch optional, muss also nicht
zwingend Angegeben werden, wodurch die Sub abwärtskompatibel
wird. Standardmässig wird der Index 0 verwendet.
Nachrichten an die Clients senden
Ein Client kann nur an den Server senden, nicht aber an den Benutzer direkt. Deshalb muss der Server
die reinkommenden Nachrichten an alle Clients weiterleiten. Dies geschieht mit der folgenden Methode ForwardMsg.
Sub ForwardMsg(strData As String, intExcludeIndex As Integer)
Dim intCounter As Integer
If wsckChat.Count > 1 Then
For intCounter = 1 To wsckChat.Count - 1
If intCounter <> intExcludeIndex Then
Senden strData, intCounter
End If
Next
End If
End Sub
Erklärungen: Diese Funktion durchläuft alle geladenen
Winsock-Controls. Für jedes Control wird geprüft, ob
eine Verbindung offen ist. Ist dies der Fall, ruft das
Programm die Funktion Senden auf, um die Nachricht zu
verschicken.
Wieder im Spiel: Die Methode Listen
Auch in diesem Beispiel verwenden wir wieder die Methode
Liste, die wir bereits im erste Teil verwendet haben. (Wenn
dir das nichts sagt, dann ab zum ersten
Teil ;-)
Sub cmdListen_Click()
strNick = Left(txtName & Space(50), 50)
blnServer = True
wsckChat(0).LocalPort = 12123
wsckChat(0).Listen
End Sub
Erklärungen: Der Code sieht wieder sehr ähnlich aus. Da aber
unser Winsock-Control jetzt ein Array ist, müssen wir
einen Index verwenden. Das Haupt-Control, welches wir für
Listen verwenden, hat den Index 0. Ausserdem kommt die
Variable blnServer hinzu. Diese ist für das ganze
Formular gültig und gibt an, ob die aktuelle
Programm-Instanz ein Server oder ein Client ist.
Ganz anders: Die Kontaktaufnahme
Auch hier wird wieder das Event ConnectionRequest gefeuert,
sobald ein Client Kontakt aufnimmt. Aber wir können jetzt
nicht den Code verwenden, den wir noch im ersten Beispiel
verwendet haben. Der Grund ist, dass wir das Control mit dem
Index Null nicht schliessen dürfen. Schliesslich muss
dieses die Verbindungsversuche für die kommenden Benutzer
beantworten. Deshalb wird hier ein zusätzliches
Winsock-Control hinzugeladen.
Sub wsckChat_ConnectionRequest(Index As Integer, _
ByVal requestID As Long)
wsckChat(GetNextFreeIndex).Accept requestID
End Sub
Erklärungen: Komisch, nicht. Wieso wird hier nur eine Zeile
benötigt? Das Geheimnis liegt in der Funktion
GetNextFreeIndex!
Function GetNextFreeIndex() As Integer
Dim intCounter As Integer
For intCounter = 1 To wsckChat.Count - 1
If wsckChat(intCounter).State = sckClosed Then
GetNextFreeIndex = intCounter
Exit Function
End If
Next
GetNextFreeIndex = wsckChat.Count
Load wsckChat(GetNextFreeIndex)
End Function
Erklärungen: Diese Funktion ist doch ein bisschen
grösser. Die Aufgabe der Funktion ist, den Index des
ersten geschlossenen Winsock-Controls (State = sckClosed)
zurückzugeben. Wenn kein geschlossenes Control gefunden wird,
sorgt die Funktion auch gleich für Nachschub und lädt ein
zusätzliches Control.
Kaum verändert: Der Client
Sub cmdConnect_Click()
Dim strRemoteComputer As String
strNick = Left(txtName & Space(50), 50)
strRemoteComputer = InputBox("RemoteHost:")
If Trim(strRemoteComputer) = "" Then Exit Sub
blnServer = False
wsckChat(0).RemotePort = 12123
wsckChat(0).RemoteHost = strRemoteComputer
wsckChat(0).Connect
End Sub
Erklärungen: Wir Du siehst, ändert sich an dem Code
so gut wie nichts. Die einzige Änderung ist, dass jetzt
über einen Index auf das Winsock-Control zugegriffen
werden muss. Der Client greift immer über das Control mit
dem Index 0 auf den Server zu. Auch hier kommt noch die
Variable blnServer dazu.
Ausserdem kommt noch folgender Code hinzu:
Sub wsckChat_Connect(Index As Integer)
If blnServer = False Then
Senden "1" & strNick
End If
End Sub
Erklärung: Mit diesem Code erreichen wir, dass sich ein Client
beim Server identifiziert. wsckChat_Connect wird aufgerufen,
sobald der Server die Methode Accept aufruft.
Was folgt noch?
Bereits können sich verschiedene Benutzer auf dem Server einloggen. Jetzt folgt noch die Möglichkeit, dass die Benutzer Nachrichten senden könen. Die dazu nötigen Methoden Senden und ForwardMsg haben wir ja bereits implementiert.
Nachrichten senden
Das wichtigste ist, dass wir beim Senden unterscheiden müssen,
ob die aktuelle Instanz als Server oder als Client
läuft. Denn läuft sie als Server, muss die eingegebene
Nachricht an alle verbundenen Benutzer gesendet werden. Der
Client sendet die Nachricht an den Server.
Sub cmdSend_Click()
Dim strMessage As String
strMessage = "0" & strNick & _
txtNachricht.Text
If blnServer Then
ForwardMsg strMessage, 0
Else
Senden strMessage, 0
End If
AddMessage txtNachricht.Text, strNick
End Sub
Erklärungen: Als erstes wird der String zusammengebaut,
welcher über das Netzwerk versendet wird. Dieser besteht aus
einer führenden 0, da es sich um eine normale Nachricht
handelt. Danach kommt der Nick und anschliessend der
eingegebene Text. Dann unterscheidet das Programm zwischen
Server/Client. Wenn die Instanz als Server läuft, wird die
Nachricht an alle verbundenen Clients weitergeleitet. Bei
einem Client wird die Nachricht an den Server geschickt.
Daten empfangen
Nun folgt ein sehr wichtiger Teil: Die ankommenden Daten
müssen verarbeitet werden. Auch hier haben Server und Client
unterschiedliche Aufgaben. Der Server muss Nachrichten an alle
Clients weiterleiten. Ausserdem muss er dafür sorgen, dass
sich die Clients anmelden können.
Sub wsckChat_DataArrival(Index As Integer, ByVal bytesTotal As Long)
Dim strData As String, strNick As String, strMessage As String
wsckChat(Index).GetData strData
If Left(strData, 1) = "0" Then
strNick = Trim(Mid(strData, 2, 50))
strMessage = Mid(strData, 52)
AddMessage strMessage, strNick
If blnServer Then ForwardMsg strData, Index
ElseIf Left(strData, 1) = "1" Then
strNick = Trim(Mid(strData, 2, 50))
If blnServer Then
wsckChat(Index).Tag = strNick
ForwardMsg Left("-system-" & Space(50), 50) & "1" & strNick, Index
End If
AddMessage "Der User " & _
strNick & _
" betritt den Chat.", _
"-system-"
End If
End Sub
Erklärungen: Nach dem Einlesen der einkommenden Daten wird das
erste Zeichen der Daten geprüft. Handelt es sich dabei um
eine Null, so kommt eine normale Nachricht herein, welche
ausgegeben und evtl. an die Clients weitergeleitet werden
muss. Steht jedoch eine Eins an erster Stelle, loggt sich ein
Client ein. Dann wird eine Nachricht ausgeben, dass ein neuer
User den Chat betreten hat und die Clients werden informiert.
Downloads
Auch zu diesem Artikel habe ich wieder zwei verschiedene
Downloads bereitgestellt.
Formular:
Das fertige Formular. Ohne Code
Ganzes
Projekt: Das gesamte Projekt. Mehr Code, als hier
erklärt. Vor allem habe ich den Code kommentiert und ein
wenig mehr Benutzerfreundlichkeit eingebaut.
Schlusswort und Ausblick
Endlich am Ende! Wie Du siehst, ist die Programmierung eines Chats, der mehrere Benutzer zulassen soll, bereits ein wenig komplexer. Doch viele Ideen habe ich hier noch nicht eingebaut. Ein paar Ideen, damit Du Dein Wissen nun ein wenig festigen kannst: Alle User auflisten, private Nachrichten, mehrere Channels, etc. Sei einfach kreativ. Falls Du irgendwelche Fragen hast, so mail mir doch einfach.