vb@rchiv
VB Classic
VB.NET
ADO.NET
VBA
C#
NEU! sevCoolbar 3.0 - Professionelle Toolbars im modernen Design!  
 vb@rchiv Quick-Search: Suche startenErweiterte Suche starten   Impressum  | Datenschutz  | vb@rchiv CD Vol.6  | Shop Copyright ©2000-2024
 
zurück
Rubrik: HTML/Internet/Netzwerk · Winsock   |   VB-Versionen: VB4, VB5, VB610.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ßerBewertung:     [ Jetzt bewerten ]Views:  15.025 
www.preisser-it.deSystem:  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:

  1. 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.
     
  2. 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).

Dieser Tipp wurde bereits 15.025 mal aufgerufen.

Voriger Tipp   |   Zufälliger Tipp   |   Nächster Tipp

Über diesen Tipp im Forum diskutieren
Haben Sie Fragen oder Anregungen zu diesem Tipp, können Sie gerne mit anderen darüber in unserem Forum diskutieren.

Aktuelle Diskussion anzeigen (5 Beiträge)

nach obenzurück


Anzeige

Kauftipp Unser Dauerbrenner!Diesen und auch alle anderen Tipps & Tricks finden Sie auch auf unserer aktuellen vb@rchiv  Vol.6
(einschl. Beispielprojekt!)

Ein absolutes Muss - Geballtes Wissen aus mehr als 8 Jahren vb@rchiv!
- nahezu alle Tipps & Tricks und Workshops mit Beispielprojekten
- Symbol-Galerie mit mehr als 3.200 Icons im modernen Look
Weitere Infos - 4 Entwickler-Vollversionen (u.a. sevFTP für .NET), Online-Update-Funktion u.v.m.
 
   

Druckansicht Druckansicht Copyright ©2000-2024 vb@rchiv Dieter Otter
Alle Rechte vorbehalten.
Microsoft, Windows und Visual Basic sind entweder eingetragene Marken oder Marken der Microsoft Corporation in den USA und/oder anderen Ländern. Weitere auf dieser Homepage aufgeführten Produkt- und Firmennamen können geschützte Marken ihrer jeweiligen Inhaber sein.

Diese Seiten wurden optimiert für eine Bildschirmauflösung von mind. 1280x1024 Pixel