Rubrik: HTML/Internet/Netzwerk · Winsock | VB-Versionen: VB4, VB5, VB6 | 10.09.07 |
Daten vollständig + fehlerfrei über Winsock senden Dieser Tipp zeigt eine Universal-Routine für das Winsock-Control, die alle Probleme beim Übertragen großer und kleiner Daten behebt. | ||
Autor: Konstantin Preißer | Bewertung: | Views: 15.029 |
www.preisser-it.de | System: Win9x, WinNT, Win2k, WinXP, Win7, Win8, Win10, Win11 | Beispielprojekt auf CD |
Sie kennen wahrscheinlich das Problem: Einfache Daten über das Winsock-Control zu versenden, ist kein Problem. Aber bei größeren Daten (ab 8 KB) und bei mehreren hintereinander versendeten Daten wird es etwas schwierig.
Hier bei vb@rchiv gibt es zwar schon zwei Tipps, die diese Probleme zu beheben versuchen, allerdings funktionieren diese nicht ganz perfekt, und man kann sie nicht gemeinsam verwenden.
Um das Winsock-Control mit anspruchsvollen Daten zu verwenden, müssen Sie folgendes wissen:
- Wenn ein Programm hintereinander mehrere Datenpakete sendet, kommen diese beim Empfänger als ein Paket an. Dieser Effekt tritt auch auf, wenn zwar zwischen dem Versenden von Paketen Zeit vergeht, die Empfänger-Anwendung zu dieser Zeit jedoch ausgelastet ist. Wenn Sie also wirklich einzelne Pakete auch als solche empfangen wollen, ist es nötig, diese voneinander abzugrenzen.
- Wenn Sie größere Daten versenden, werden diese in Blöcke mit jeweils ca. 8 KB aufgeteilt. Dies bedeutet, dass das Winsock_DataArrival-Ereignis mehrfach ausgeführt wird und die einzelnen Blöcke empfangen werden, als wären sie separat versandte Daten. Es gibt in VB aber keine Möglichkeit, die gesamte Länge der Daten zu ermitteln; die bytesTotal-Variable enthält nur die Länge der gerade empfangenen Daten. Um also größere Daten problemlos zu versenden, muss das Empfangsprogramm ebenfalls wissen, wann ein Paket aufhört.
Eine Lösung dieses Problems erreicht man am einfachsten, indem man die Länge des zu versendenden Datenpaketes vor den eigentlichen Nutzdaten sendet. Hier haben jedoch die bereits veröffentlichten Tipps Nachteile:
- Der Tipp Datenpakete getrennt verschicken und empfangen versucht zwar das 1. Problem zu beheben, in dem die ersten 4 Stellen der Daten zum Versenden der Länge genutzt werden. Allerdings ist dabei die Größe der Daten auf 9999 Bytes (Zeichen) begrenzt, und er berücksichtigt nicht das Problem, dass Daten in mehrere Pakete aufgeteilt werden können.
- Der Tipp Dateien binär über das Winsock-Control senden berücksichtigt das 2. Problem, in dem ebenfalls am Anfang die Länge gesendet wird. Allerdings ist dieser Tipp auf das Versenden von Dateien beschränkt. Außerdem wird dort bei jedem Empfang von Daten geprüft, ob der String mit "<begin size=" beginnt, um neue Dateien zu erkennen. Was aber ist, wenn zufällig in der Datei genau an dieser Stelle, an der das neue empfangene Blockpaket beginnt, genau diese Zeichenfolge enthalten ist? Dann funktioniert der Tipp nicht mehr richtig.
Um diese Probleme zu beheben und eine universelle Winsock Sende- und Empfangsroutine zu bekommen, die einzelne Pakete als solche erkennt und auch keine Probleme mit großen Daten hat, können Sie die folgenden Prozeduren verwenden. Zum Senden von Daten verwenden Sie die Winsock1Senden-Prozedur, die die Länge der zu versendenden Daten am Anfang, gefolgt von einem Leerzeichen als Trennzeichen zu den Nutzdaten, sendet. Beim Empfangen von Daten wird die Prozedur WsockEmpfangsstatus ausgeführt, die den Übertragungsfortschritt des aktuellen Pakets in Prozent angibt. Wenn das Paket vollständig übertragen wurde, wird die Prozedur WsockStrVerarbeiten ausgelöst, mit der Sie das empfangene Paket weiter verarbeiten können.
Anmerkung: Da Strings in VisualBasic im Unicode-Format im Speicher abgelegt werden (1 Zeichen wird durch 2 Bytes dargestellt), das Winsock-Control einen String jedoch im ASCII/ANSI-Format sendet (1 Zeichen = 1 Byte), sollten Sie vorsichtig beim Versenden von sehr großen Daten sein. Wenn Sie z. B. eine 50 MB große Datei versenden wollen, belegt das Empfangsprogramm während der Übertragung mindestens 100 MB an Arbeitsspeicher.
Public Sub Winsock1Senden(sData As String) ' Sendet die Länge des Datenpakets und ' ein Leerzeichen zur Abgrenzung zu den Nutzdaten Winsock1.SendData CStr(Len(sData)) & " " & sData End Sub
Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long) ' Fehlerbehandlung On Error GoTo WsockErr ' Schutz vor Überlappung und mehrfacher Ausführung ' (z.B. bei "DoEvents" in der Verarbeitungsprozedur) Static bAlreadyRunning As Boolean ' Andere Variablen, die benötigt werden Static sWsockZwischenspeicher As String Static sPaketZwischenspeicher As String Static nGesamtlaengePaket As Long Static nBereitsuebertragenPaket As Long If bytesTotal = 0 Then ' Die Prozedur wurde manuell aufgerufen, um nach ' einer Verbindungstrennung die Variablen zurückzusetzen sWsockZwischenspeicher = "" nGesamtlaengePaket = 0 Exit Sub End If Dim strTemp As String Dim lngTemp As Long ' Daten empfangen Winsock1.GetData strTemp ' Empfangene Daten dem Winsock-Zwischenspeicher hinzufügen sWsockZwischenspeicher = sWsockZwischenspeicher & strTemp ' Wenn Prozedur bereits ausgeführt wird, an dieser Stelle ' abbrechen und warten, bis die ursprüngliche Prozedur ' fortgesetzt wird If bAlreadyRunning Then Exit Sub bAlreadyRunning = True ' Ein schon angefangenes Paket wird fortgesetzt ' (Anm.: Dieser Teil muss zuerst kommen, da nach einer ' vollständigen Übertragung schon ein neues Paket im ' Übertragungsstrom sein kann und dieses dann auch ' ausgewertet werden muss) If nGesamtlaengePaket > 0 Then ' Empfangene Daten dem Paket-Zwischenspeicher hinzufügen Mid$(sPaketZwischenspeicher, nBereitsuebertragenPaket + 1) _ = Left$(sWsockZwischenspeicher _ , nGesamtlaengePaket - nBereitsuebertragenPaket) lngTemp = Len(Left$(sWsockZwischenspeicher, _ nGesamtlaengePaket - nBereitsuebertragenPaket)) ' Informationen über den Empfangsstatus aktualisieren nBereitsuebertragenPaket = nBereitsuebertragenPaket + lngTemp ' Die neuen Daten vom Winsock-Zwischenspeicher entfernen ' (bis auf evtl. neue eingetroffene Pakete) sWsockZwischenspeicher = Mid(sWsockZwischenspeicher, 1 + lngTemp) ' Übertragungsfortschrittsanzeige aufrufen WsockEmpfangsstatus 100 * nBereitsuebertragenPaket / nGesamtlaengePaket If nGesamtlaengePaket = nBereitsuebertragenPaket Then ' Das Paket wurde vollständig übertragen und kann ' weiterverarbeitet werden nGesamtlaengePaket = 0 WsockStrVerarbeiten sPaketZwischenspeicher End If End If ' Neues Paket empfangen Do While InStr(1, sWsockZwischenspeicher, " ") > 0 And _ nGesamtlaengePaket = 0 'wird nur ausgeführt, ' Wenn die Bytes, die die Länge und das Trennzeichen ' (zur Trennung von Länge und Nutzdaten) enthalten, ' vollständig übertragen sind (dabei kann jedoch die ' Länge der bereits übertragenen Nutzdaten auch 0 sein, ' z.B. wenn ein neues Paket am Ende eines großen Paketes ' übertragen wird); ansonsten bleiben die Daten im ' Winsock-Zwischenspeicher und es wird gewartet, bis ein ' Trennzeichen übertragen wird ' Gesamtlänge des Pakets ermitteln nGesamtlaengePaket = CLng(Left$(sWsockZwischenspeicher, _ InStr(1, sWsockZwischenspeicher, " ") - 1)) ' Ermitteln, wie viel vom Paket bereits übertragen wurde nBereitsuebertragenPaket = Len(Mid$(sWsockZwischenspeicher, _ InStr(1, sWsockZwischenspeicher, " ") + 1, nGesamtlaengePaket)) ' Paket-Zwischenspeicher-String mit der gesamten Länge des ' Pakets erstellen, damit stückweise hinzugekommene Daten ' nicht aufwendig im Speicher umstrukturiert werden müssen ' (wenn große Pakete übertragen werden (ab 1 MB), bringt dies ' einen erheblichen Geschwindigkeitsvorteil) sPaketZwischenspeicher = String$(nGesamtlaengePaket, "x") ' Den Anfang des Paketzwischenspeichers durch den empfangenen Daten ersetzen Mid(sPaketZwischenspeicher, 1) = Mid(sWsockZwischenspeicher _ , InStr(1, sWsockZwischenspeicher, Chr$(32)) + 1, nGesamtlaengePaket) ' Die neuen Daten vom Winsock-Zwischenspeicher entfernen ' (bis auf evtl. neue eingetroffene Pakete) sWsockZwischenspeicher = Mid(sWsockZwischenspeicher, _ InStr(1, sWsockZwischenspeicher, " ") + 1 + nBereitsuebertragenPaket) ' Übertragungsfortschrittsanzeige aufrufen WsockEmpfangsstatus 100 * nBereitsuebertragenPaket / nGesamtlaengePaket If nGesamtlaengePaket = nBereitsuebertragenPaket Then ' Das Paket wurde bereits vollständig übertragen und kann ' weiterverarbeitet werden nGesamtlaengePaket = 0 WsockStrVerarbeiten sPaketZwischenspeicher End If ' So lange wiederholen, bis keine neuen Pakete bzw. abgeschlossene ' Längenangaben mit Trennzeichen mehr im Winsock-Zwischenspeicher ' enthalten sind Loop bAlreadyRunning = False Exit Sub WsockErr: ' Fehler beim Verarbeiten der Daten bzw. beim Ermitteln der Länge ' des Pakets (falsches Format beim Sender?) nGesamtlaengePaket = 0 sWsockZwischenspeicher = "" bAlreadyRunning = False MsgBox "Fehler beim Empfangen der Dateien; falsches Sendeformat.", vbCritical End Sub
Private Sub WsockEmpfangsstatus(Prozent As Double) ' Diese Prozedur gibt den Empfangsfortschritt ' in Prozent an... End Sub
Private Sub WsockStrVerarbeiten(sData As String) ' Hier werden die empfangenen Pakete weiterverarbeitet... ... End Sub
Zwei Sachen gibt es noch zu beachten. Wenn die Verbindung vom Winsock-Control getrennt und wiedergergestellt wird, müssen zwei der Variablen zurückgesetzt werden, damit die Übertragung wieder funktioniert. Da die Variablen mit Static deklariert sind und man deshalb nicht außerhalb der DataArrival-Prozedur aus sie zugreifen kann, ist es am einfachsten, dies in der DataArrival-Prozedur zu machen. Sie müssen nur bei allen Verbindungstrennungsereignissen (Winsock_Close, Winsock_Error und Winsock.Close) oder -wiederherstellungsereignissen (Winsock_Connect und Winsock_ConnectionRequest oder Winsock.Connect und Winsock.Accept) folgende Zeile hinzufügen:
Winsock1_DataArrival 0
Beachten Sie außerdem, dass die Empfangsroutinen nur für ein Winsock-Control funktionieren. Wenn Sie sie mit einem Winsock-Steuerelementfeld verwenden wollen, müssen Sie die 5 mit Static deklarierten Variablen als Array dimensionieren und den Code entsprechend anpassen ("Index" an die Variablennamen anfügen).