Buchempfehlung
Mikrocomputertechnik mit Controllern der Atmel AVR-RISC-Familie
Mikrocomputertechnik mit Controllern der Atmel AVR-RISC-Familie
Umfassend, aber leicht verständlich führt dieses Buch in die Programmierung von ATMEL AVR Mikrocontrollern ein. [Mehr Infos...]
FreeBASIC-Chat
Es sind Benutzer im FreeBASIC-Chat online.
(Stand:  )
FreeBASIC bei Twitter
Twitter FreeBASIC-Nachrichten jetzt auch über Twitter erhalten. Follow us!

Tutorial

Netzwerkprogrammierung mit TSNE_V3

von MitgliedThePuppetMasterSeite 6 von 8

In diesem Kapitel werden wir 3 oder mehrere Clients gleichzeitig bzw. quasi simultan erzeugen. Dabei gehen wir prinzipiell nach dem selben Prinzip vor, wie im Kapitel zuvor.

Aus diesem Grunde können wir das gesamte Programm kopieren und ändern nur ein paar Kleinigkeiten ab.

#Include once "TSNE_V3.bi"
Dim G_Client_TSNEID as UInteger


Sub TSNE_Disconnected(ByVal V_TSNEID as UInteger)
Print "[Disconnected] TSNEID: "; V_TSNEID
End Sub


Sub TSNE_Connected(ByVal V_TSNEID as UInteger)
Print "[Connected] TSNEID: "; V_TSNEID
Dim T as String
'Header erzeugen
T += "GET / HTTP/1.1" & Chr(13, 10)
T += "Host: www.google.de" & Chr(13, 10)
T += "Connection: Close" & Chr(13, 10)
T += Chr(13, 10)
'Header an den Server senden
Dim RV as Integer
RV = TSNE_Data_Send(V_TSNEID, T)
If RV <> TSNE_Const_NoError Then
    Print "[FEHLER] " & TSNE_GetGURUCode(RV)
    TSNE_Disconnect(V_TSNEID)
End If
End Sub


Sub TSNE_NewData(ByVal V_TSNEID as UInteger, ByRef V_Data as String)
Print "[NewData] TSNEID: "; V_TSNEID; " Datenlänge: "; Len(V_Data)
End Sub


Dim RV as Integer
RV = TSNE_Create_Client(G_Client_TSNEID, "www.google.de", 80, @TSNE_Disconnected, @TSNE_Connected, @TSNE_NewData)
If RV <> TSNE_Const_NoError Then
    Print "[FEHLER] " & TSNE_GetGURUCode(RV)
    End -1
End If
TSNE_WaitClose(G_Client_TSNEID)
End 0

Je nach Programm bzw. Aufgabenstellung kann man das Prinzip auf jede Art von Client-Verbindungen anwenden, da nicht nur die Ereignisse identisch bleiben, sondern zumeist auch der Ablauf. Was sich natürlich ändern kann, ist das verwendete Protokoll sowie die Reihenfolge der Datenübertragung.

Um 3 Clients zu erzeugen, müssen wir natürlich zuerst einmal die Variable "G_Client_TSNEID" abändern, um 3 TSNEIDs für 3 Clients speichern zu können.

'...
Dim G_Client_TSNEID(3) as UInteger
'...

Ein einfaches statisches Array tut hier perfekt seinen Dienst.

Anschließend erzeugen wir 3 Clients und speichern deren IDs in das Array ab.

'...
Dim RV as Integer
For X as UInteger = 1 to 3
    RV = TSNE_Create_Client(G_Client_TSNEID(X), "www.google.de", 80, @TSNE_Disconnected, @TSNE_Connected, @TSNE_NewData)
    If RV <> TSNE_Const_NoError Then
        Print "[FEHLER] " & TSNE_GetGURUCode(RV)
    End If
Next
'...

Hierbei ist darauf zu achten, dass wir bei einem Fehler beim Verbindungsaufbau nicht einfach das Programm mit "End -1" beenden können. Wir wissen nicht, welche Verbindungen erzeugt wurden und welche der 3 nicht.
Darum ignorieren wir dieses Vorgehen und versuchen einfach, die restlichen Verbindungen herzustellen.

Soll das Programm dennoch beendet werden, bei einem Fehler jeglicher Art, dann müssen wir etwas anders vorgehen.
Hierbei muss geprüft werden, ob bereits andere Verbindungen der 3 erzeugt wurden oder nicht.
Ist dies der Fall, muss diese erst beendet werden, bevor wir das Programm selbst beenden.

'...
Dim RV as Integer
For X as UInteger = 1 to 3
    RV = TSNE_Create_Client(G_Client_TSNEID(X), "www.google.de", 80, @TSNE_Disconnected, @TSNE_Connected, @TSNE_NewData)
    If RV <> TSNE_Const_NoError Then
        Print "[FEHLER] " & TSNE_GetGURUCode(RV)
        For Y as UInteger = 1 to 3
            If G_Client_TSNEID(Y) > 0 Then
                TSNE_Disconnect(G_Client_TSNEID(Y))
                TSNE_WaitClose(G_Client_TSNEID(Y))
            End If
        Next
        End -1
    End If
Next
'...

Dadurch, dass bei Fehlern jeglicher Art, beim Verbindungsaufbau, die zurückgegebene TSNEID 0 bleibt, können wir auf einfache Art prüfen, ob Verbindungen vorhanden sind oder nicht.
Ist also die gespeicherte TSNEID grösser 0, dann besteht eine Verbindung bzw. ist im Prozess des Aufbaus. Die ID nutzen wir jetzt, um den Aufbauprozess abzubrechen bzw. falls schon hergestellt, diesen zu beenden (TSNE_Disconnect).
Anschliessend warten wir natürlich noch auf das erfolgreiche Ende der Verbindung, bevor wie den nächsten Eintrag im Array prüfen.

-TIP-
Werden mehrere Verbindungen verwendet, kann es vorteilhaft sein ein anderes Prinzip der Verbindungstrennung zu verwenden.
Die Technik mit der For-Schleife und den beiden darauffolgenden "TSNE_Disconnect" und "TSNE_WaitClose" kann unter Umständen sehr lange dauern.
So wird z.B. bei 1000 Verbindungen nach jedem "TSNE_Disconnect" erst einmal auf das erfolgreiche Ende gewartet "TSNE_WaitClose" bis mit dem nächsten Eintrag weiter gearbeitet wird.
Sind Verbindungen langsam, kann das schonmal mehrere Sekunden, Minuten oder gar Stunden oder Tage dauern!!! Der Grund hierfür liegt im Allgemeinen an Externer Link!TCP-Protokoll, das in der WIKIPedia ausreichend beschrieben sein sollte.

Eine drastische Beschleunigungsmassnahme ist die Aufteilung in 2 Schleifen.

'...
For Y as UInteger = 1 to 3
    If G_Client_TSNEID(Y) > 0 Then
        TSNE_Disconnect(G_Client_TSNEID(Y))
    End If
Next
For Y as UInteger = 1 to 3
    If G_Client_TSNEID(Y) > 0 Then
        TSNE_WaitClose(G_Client_TSNEID(Y))
    End If
Next
'...

Dies ist zwar mehr Code-Aufwand, jedoch beschleunigt diese Technik das Beenden drastisch.
Jedes "TSNE_Disconnect" hat TCP-seitig rund 60sek. Zeit, in der auf die "Disconnect"- Bestätigung der Gegenstelle gewartet werden muss.
Bei 1000 Verbindungen à 60sek. sind das rund 60000 Sekunden für 1000 Verbindungen!!!
Verwenden wir jedoch 2 separate Schleifen, so wird an alle Verbindungen das "Disconnect", quasi gleichzeitig, gesandt.
Dadurch bleibt der Warteprozess für alle Verbindungen quasi bei 60 sek. maximal!

Bei 3 Verbindungen ist das noch unerheblich und kann mit einer Schleife ausreichend gelöst werden.
Bei Server-Anwendungen ist es hingegen nicht mehr ratsam, diese Art der Beendigung zu verwenden, sondern die 2 Schleifen-Variante. Aber dazu im Server-Kapitel mehr.

Um zu verhindern, dass unser Programm nun vorzeitig beendet wird, müssen wir auch hier wieder auf das Ende der Verbindungen warten.
Die einfachste (aber nicht wirklich effektivste) Art dies zu lösen, ist folgende:

'...
For X as UInteger = 1 to 3
    TSNE_WaitClose(G_Client_TSNEID(X))
Next
End 0

Nachteil an dieser Vorgehensweise ist, so wie auch beim vorzeitigen Beenden des Programms, dass unbekannte "hängen"-bleiben in Verbindungen, die langsamer sind als andere.

Darum bietet uns TSNE eine alternative Funktion an, die prüft, ob eine Verbindung noch aktiv ist oder nicht. "TSNE_IsClosed" heisst diese und gibt uns bei einer geschlossenen bzw. beendeten Verbindung eine 1 zurück.
Wir packen also diese Funktion in eine Do-Loop-Schleife und prüfen so alle Verbindungen auf Existenz hin.

'...
Dim C as UInteger
Do
    C = 0
    For X as UInteger = 1 to 3
        If TSNE_IsClosed(G_CLient_TSNEID(X)) = 1 Then C += 1
    Next
    If C = 3 Then Exit Do
    Sleep 1, 1
Loop
End 0

Die Schleife läuft solange bis C den Wert 3 angenommen hat. Diesen Wert erreicht sie dadurch, dass bei jedem Schleifendurchlauf geprüft wird, ob alle 3 Verbindungen noch aktiv sind oder nicht.

Das Prinzip kann z.B. für Server-Timeouts verwendet werden oder ähnlichen statusgebundenen Verfahren.


Und damit wäre eigentlich die gesamten Änderung ausgeführt, die es ermöglicht, 3 Verbindungen gleichzeitig zu Google herzustellen.
Die Ausgabe auf der Konsole zeigt nun den Status jeder der 3 Verbindungen an, die sich teilweise überschneiden. Das liegt daran, weil die Verbindungen von TSNE parallel verarbeitet werden und nicht nacheinander ablaufen.
Das ist auch bei Server-Anwendungen wichtig, da es sonst zu Engpässen bei der Datenübertragung kommen kann.

Der gesamte Quellcode sollte nun so aussehen:

#Include once "TSNE_V3.bi"
Dim G_Client_TSNEID as UInteger


Sub TSNE_Disconnected(ByVal V_TSNEID as UInteger)
Print "[Disconnected] TSNEID: "; V_TSNEID
End Sub


Sub TSNE_Connected(ByVal V_TSNEID as UInteger)
Print "[Connected] TSNEID: "; V_TSNEID
Dim T as String
'Header erzeugen
T += "GET / HTTP/1.1" & Chr(13, 10)
T += "Host: www.google.de" & Chr(13, 10)
T += "Connection: Close" & Chr(13, 10)
T += Chr(13, 10)
'Header an den Server senden
Dim RV as Integer
RV = TSNE_Data_Send(V_TSNEID, T)
If RV <> TSNE_Const_NoError Then
    Print "[FEHLER] " & TSNE_GetGURUCode(RV)
    TSNE_Disconnect(V_TSNEID)
End If
End Sub


Sub TSNE_NewData(ByVal V_TSNEID as UInteger, ByRef V_Data as String)
Print "[NewData] TSNEID: "; V_TSNEID; " Datenlänge: "; Len(V_Data)
End Sub


Dim RV as Integer
For X as UInteger = 1 to 3
    RV = TSNE_Create_Client(G_Client_TSNEID(X), "www.google.de", 80, @TSNE_Disconnected, @TSNE_Connected, @TSNE_NewData)
    If RV <> TSNE_Const_NoError Then
        Print "[FEHLER] " & TSNE_GetGURUCode(RV)
        For Y as UInteger = 1 to 3
            If G_Client_TSNEID(Y) > 0 Then
                TSNE_Disconnect(G_Client_TSNEID(Y))
            End If
        Next
        For Y as UInteger = 1 to 3
            If G_Client_TSNEID(Y) > 0 Then
                TSNE_WaitClose(G_Client_TSNEID(Y))
            End If
        Next
        End -1
    End If
Next


Dim C as UInteger
Do
    C = 0
    For X as UInteger = 1 to 3
        If TSNE_IsClosed(G_CLient_TSNEID(X)) = 1 Then C += 1
    Next
    If C = 3 Then Exit Do
    Sleep 1, 1
Loop
End 0

 

Gehe zu Seite Gehe zu Seite  1  2  3  4  5  6  7  8  
Zusätzliche Informationen und Funktionen
  Bearbeiten Bearbeiten  

  Versionen Versionen