Buchempfehlung
Windows-Programmierung. Das Entwicklerhandbuch zur WIN32-API
Windows-Programmierung. Das Entwicklerhandbuch zur WIN32-API
"Der" Petzold, das über 1000 Seiten starke Standardwerk zum Win32-API - besonders nützlich u. a. bei der GUI-Programmierung in FreeBASIC! [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 5 von 8

In diesem Kapitel werden wir eine Client-Verbindung mit dem Google-Server herstellen, die Hauptseite herunterladen und mithilfe von "Print" auf der Konsole auszugeben.

Damit wir die Funktionen von TSNE nutzen können, müssen wir das "TSNE_V3.bi" Modul zum Quellcode hinzufügen.
Der einfachste Weg ist ein

#Include once "TSNE_V3.bi"
'...

an den Anfang unseres Programms zu packen. Dadurch werden alle API-Funktionen von TSNE in unserem Programm verfügbar.

Als Nächstes müssen wir eine Variable erzeugen, welche später die neu erzeugte TSNEID des folgenden Funktionsaufrufs an TSNE speichert.
Diese TSNEID wird verwendet, um den Status des Sockets zu prüfen.

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

Die TSNEIDs sind vom UInteger Datentyp. Daher sollte die Erzeugung der Variable kein Problem darstellen.

Jetzt könnten wir theoretisch die Funktion aufrufen, mit der TSNE eine Verbindung zu Google herstellt.

#Include once "TSNE_V3.bi"
Dim G_Client_TSNEID as UInteger
'...
TSNE_Create_Client(G_Client_TSNEID, "www.google.de", 80, 0, 0, 0)
'...

(Die API-Dokumentation zeigt hier, welche Parameter an welcher Stelle eingesetzt werden müssen.)
Nachdem TSNE (THEORETISCH) die Verbindung erfolgreich hergestellt hat, müssen wir auf das "Connect"-Ereignis reagieren.
Das Problem ist jedoch, das wir noch keine Callbacks erzeugt haben. Ohne Callbacks bekommen wir jedoch niemals signalisiert, wann die Verbindung erfolgreich hergestellt wurde und diese bereit ist, Daten auszutauschen.

Darum müssen wir erst einmal Callback-Routinen erzeugen, bevor wir die eigentliche "TSNE_Create_xxx"-Funktion aufrufen.

'...

Sub TSNE_Disconnected(ByVal V_TSNEID as UInteger)
'...
End Sub


Sub TSNE_Connected(ByVal V_TSNEID as UInteger)
'...
End Sub


Sub TSNE_NewData(ByVal V_TSNEID as UInteger, ByRef V_Data as String)
'...
End Sub

'...

Wir benötigen insgesamt 3 Callbacks. Mindestens ist das "NewData"-Event nötig. Da wir jedoch auch mit dem Server kommunizieren wollen und nicht nur Daten von diesem Server erwarten, benötigen wir auch ein Ereignis, das uns signalisiert, dass die Verbindung erfolgreich hergestellt wurde.
Zusätzlich interessiert uns auch, wann die Verbindung wieder getrennt wurde. Das ist zwar in unserem Beispiel nicht sonderlich wichtig, ist jedoch für die Demonstration der Callback-Funktionen ganz hilfreich.

Die Bezeichnung der Sub-Routinen kann, wie schon im vorherigen Kapitel erwähnt, frei gewählt werden, ich habe der Einfachheit und Übersichtlichkeit halber entsprechende Bezeichnungen gewählt.
Das "Verbindung beendet" (Disconnect)-Ereignis möchten wir später, beim Aufruf von "TSNE_Create_xxx" an die TSNE_Disconnected Funktionen übergeben. Da wir natürlich wissen wollen, welches Socket seine Verbindung getrennt hat, übermittelt uns TSNE die TSNEID zu diesem Socket.

Es mag gerade etwas komisch erscheinen, dass wir einfach 3 Funktionen schreiben, die "irgendwann" und von irgendwem aufgerufen werden, obwohl wir die Funktionen nie wirklich selbst aufrufen.
Im Grunde stimmt das, zumindest teilweise. Beim Erzeugen einer Verbindung teilen wir TSNE mit, das wir 3 Funktionen geschrieben haben, die bei Statusänderungen, bzw. beim Eintreten von Ereignissen aufgerufen werden sollen.
Somit haben wir eigentlich schon definiert, wann welche Funktion aufgerufen wird.

Wie gerade erwähnt, müssen wir der "TSNE_Create_Client"-Funktion mitteilen, dass wir Funktionen geschrieben haben, die für bestimmte Ereignisse zuständig sind.
Diese Funktionen werden in den drei Parametern nach der Portangabe ("80") angegeben.

'...
TSNE_Create_Client(G_Client_TSNEID, "www.google.de", 80, @TSNE_Disconnected, @TSNE_Connected, @TSNE_NewData)
'...

Durch das voranstehende "@" übergeben wir einen Pointer (Zeiger) auf die von uns festgelegten Funktionen an "TSNE_Create_Client".
TSNE speichert nun diese Pointer intern in einer Datenbank ab und verknüpft diese mit dem neu erzeugtem Socket. Dadurch wird beim Ändern des Status dieses Sockets die entsprechende Funktion aufgerufen.

Jedes Socket erhält damit eine Liste von Pointern zu Routinen in unserem Programm, das auf bestimmte Ereignisse reagiert. Dadurch ist es z.B. möglich, mehrere Sockets mit den selben Callbacks zu belegen oder für unterschiedliche Sockets auch unterschiedliche Callbacks zu definieren.

Der Quellcode sollte jetzt wie folgt aussehen:

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



Sub TSNE_Disconnected(ByVal V_TSNEID as UInteger)
'...
End Sub


Sub TSNE_Connected(ByVal V_TSNEID as UInteger)
'...
End Sub


Sub TSNE_NewData(ByVal V_TSNEID as UInteger, ByRef V_Data as String)
'...
End Sub



TSNE_Create_Client(G_Client_TSNEID, "www.google.de", 80, @TSNE_Disconnected, @TSNE_Connected, @TSNE_NewData)
'...

Nach dem Aufruf von TSNE_Create_Client wird eine Verbindung zum Server "www.google.de" auf dem Port "80" hergestellt. Außerdem soll beim Eintritt eines "Disconnect" Ereignisses die Funktion "TSNE_Disconnected" aufgerufen werden, sowie für für die anderen Ereignisse die entsprechenden anderen Routinen.

Aber einen wichtigen Punkt darf man nicht außer acht lassen. Wir wissen nämlich noch nicht, ob das Herstellen der Verbindung wirklich fehlerfrei war oder ob wir Fehler bei den Parametern gemacht haben.
Sollte irgendeine Art von Fehler auftreten, dann wäre es sicher gut zu wissen, was für ein Fehler das war. Aus diesem Grunde liefert so gut wie jede Funktion (mit einigen kleinen Ausnahmen) einen sogenannten "GURU-CODE" zurück.
"GURU-Code" stammt aus der Frühzeit der Computertechnik und dient eigentlich nur der Identifizierung von Fehlern.
TSNE gibt nach dem Aufruf von "TSNE_Create_Client" einen Integer Wert (den GURU-Code) zurück. Dieser Wert gibt an, ob ein Fehler entstanden ist und wenn ja, welcher dies ist.

Daher brauchen wir eine weitere Variable, welche die Rückgabe zwischenspeichert, damit wir diese überprüfen können.

'...
Dim RV as Integer
RV = TSNE_Create_Client(G_Client_TSNEID, "www.google.de", 80, @TSNE_Disconnected, @TSNE_Connected, @TSNE_NewData)
'...

In RV wird nun der GURU-Code abgespeichert.

Der nächste Schritt sieht vor, zu prüfen, ob Fehler enstanden.

'...
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
    '...
End If
'...

Die "If" Angabe überprüft nun, ob in der Variable RV ein anderer Wert als der der Konstante "TSNE_Const_NoError" (Kein Fehler) vorhanden ist.
Ist dies der Fall, dann entstand ein Fehler. Diesen Fehler können wir mithilfe der Funktion "TSNE_GetGURUCode" in einen Klartext umsetzen lassen.

'...
If RV <> TSNE_Const_NoError Then
    Print "[FEHLER] " & TSNE_GetGURUCode(RV)
    End -1
End If
'...

Die Funktion "TSNE_GetGURUCode" benötigt als Parameter den GURU-Code und gibt daraufhin einen String zurück, der genau beschreibt, was für ein Fehler entstanden ist.
Da bei solch einem Fehler das Weiterarbeiten unseres Programms nicht möglich ist, beenden wir dieses mit "End -1". Schliesslich dreht es sich in diesem Programm darum, dass eine Verbindung hergestellt werden soll. Geht dies nicht, ist unser Programm sinnlos.
Ein Programm MUSS natürlich NICHT beendet werden, wenn ein Fehler entstand. Es kann durchaus auch möglich sein, dass Google gerade nicht erreichbar ist und daher die Verbindung nicht aufgebaut werden kann.
Dieser Fehler wird natürlich entsprechend angegeben, wird jedoch bei Google wohl niemals eintreten :)

Sollte kein Fehler autreten, dann werden die oben genannten Callbacks von TSNE aus aufgerufen.
Da das Socket einen eigenen Thread erhält, müssen wir ab diesem Zeitpunkt auf das Ende des Sockets warten. Würde das Programm nach der "If"-Prüfung sich sofort beenden wollen, könnte dies zu unkoordinierten Handlungen, bis hin zum Absturz führen.
Das können wir verhindern, indem wir eine weitere API-Funktion nutzen, die auf das Ende bzw. Disconnect dieses Sockets wartet.

'...
If RV <> TSNE_Const_NoError Then
    Print "[FEHLER] " & TSNE_GetGURUCode(RV)
    End -1
End If
TSNE_WaitClose(G_Client_TSNEID)
End 0

Erst wenn das Socket auch wirklich beendet wurde, können wir das "End 0" im Programm zulassen.



Zu guter Letzt fügen wir in unserer Callback-Funktion ein paar Print-Anweisungen hinzu, um zu sehen, wie sich die Aufrufe verhalten.

'...

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
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

'...

Der komplete Quellcode müsste jetzt 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
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

Übersetzen wir den Quellcode und führen das Programm aus, wird fast sofort das "Connect"-Ereignis eintreten. Anschließend, nach ca. 60 Sek., "NewData" und "Disconnect".

Wir müssen jetzt beachten, das wir zwar eine Verbindung mit dem Server hergestellt haben, jedoch dem Server nicht mitteilen, was wir eigentlich von ihm wollen.
Dieses Phänomen beruht jedoch nicht auf TSNE selbst, sondern auf dem benutzten Kommunikations-Protokoll des Servers.

Zu beachten wäre hier, dass TSNE ein Modul ist, das NUR die Verbindungen zwischen den Computern herstellt, sowie die Übertragung von Daten ermöglicht! WAS im Endeffekt übertragen wird, liegt am Benutzer, bzw. am Programmierer.
Der Google-Server verwendet das HTTP-Protokoll. Dieses spezifiziert, dass eine Verbindung zu einem HTTP-Server aufgebaut werden muss und nach erfolgreicher Etablierung dieser Verbindung eine Anfrage an den Server gesendet werden muss. Erst dann erhält man eine konkrete Antwort.
Dabei definiert das Protokoll den gesamten Ablauf der Kommunikation, den Aufbau der Daten welche übertragen werden müssen und ein paar weitere wichtige Parameter.

Hier empfiehlt es sich in der WIKI das Externer Link!OSI-Layer Model anzusehen. Dort kann man erkennen, wo TSNE angesiedelt ist, sowie das HTTP-Protokoll. Auch für das Externer Link!HTTP-Protokoll existiert ein WIKI-Eintrag.

Glücklicherweise ist das HTTP-Protokoll "relativ" einfach zu handhaben. Es reicht aus, einen kleinen String zu erzeugen, welcher die Anfrage beinhaltet und ihn an den Server zu senden.
Die Erzeugung des Headers nehmen wir praktischerweise im "Connect"-Ereignis vor.
Ich erspare mir hier einmal die Beschreibung des HTTP-Protokolls, da es nicht wirklich zum TSNE-Tutorial passt. Informieren kann man sich hierüber in der WIKIPedia, falls das Interesse dazu besteht.

Hierzu erstellen wir ersteinmal eine Variable, die den Header aufnimmt.
Anschließend erzeugen wir einen Header und senden ihn an den Server.

'...
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
'...

Die "TSNE_Data_Send"-Funktion benötigt 2 Parameter. Einmal die TSNEID der Verbindung, an welche die Daten gesandt werden sollen und natürlich die Daten selbst, die in der Variable "T" gespeichert wurden.
Auch diese Funktion gibt einen GURU-Code zurück und sollte ausgewertet werden. Hier beenden wir jedoch nicht das Programm, sondern nur die Verbindung, wenn ein Fehler auftreten sollte.

Wie sicher schon bemerkt wurde, wird hier nicht "G_Client_TSNEID" als TSNEID verwendet, sondern die Variable "V_TSNEID", die im Callback übergeben wurde.
Theoretisch kann auch "G_Client_TSNEID" verwendet werden, da TSNE jedoch die ID des Sockets mitübermittelt, wenn ein Ereignis ausgelöst wird, greifen wir (vor allem auch aus Gründen der Sicherheit) auf die V_TSNEID zurück.
Diese ID ist zu 100% genau die TSNEID, die dieses Ereignis auch ausgelöst hat. Würden wir eine andere Variable nutzen, könnte es (vor allem bei Server-Anwendungen) zu Problemen kommen. Daher sollte man sich von Beginn an angewöhnen, die ID aus der Callback-Variable zu verwenden.

In diesem kleinen Beispiel mag das noch überschaubar sein, da wir nur eine Verbindung haben. Würden wir jedoch mehrere Verbindungen aufbauen, wäre es schon ratsamer, die V_TSNEID zu nutzen.

Wir verwenden ausserdem auch kein "End", das bei einem Fehler aufgerufen wird, sondern beenden einfach die Verbindung zum entsprechendem Socket mit der Funktion "TSNE_Disconnect".
Da wir, wie gerade geschrieben, nur eine Verbindung besitzen, wird genau diese einzige Verbindung beendet.
Nachdem das Socket beendet wurde, muss das Programm natürlich auch beendet werden.
Wir nutzen hier die Eigenschaft von "TSNE_WaitClose", das solange gewartet hat, bis die Verbindung beendet wurde und erst dann mit der nächsten Programmzeile weiter macht, nämlich dem "End 0".

Wie der ganze Ablauf mit mehreren Verbindungen aussehen wird, beschreibe ich im nächsten Kapitel


Jetzt ist das Programm komplett und kann nach dem Kompilieren ausgeführt werden.
- Das Programm baut daraufhin eine Verbindung zum Google-Server auf (TSNE_Create_Client).
- Wurde die Verbindung erfolgreich erzeugt, wird über "TSNE_WaitClose" solange gewartet, bis die Verbindung beendet wurde. Außerdem wird von TSNE aus das "Connected"-Ereignis aufgerufen (TSNE_Connected).
- Dort erzeugen wir einen Header und senden diesen an den Server (TSNE_Data_Send).
- Hat der Server eine Antwort zusammengestellt, sendet er diese an uns zurück. Dabei ruft TSNE bei jedem Datenempfang das "NewData"-Ereignis auf (TSNE_NewData).
- Da wir das HTTP-Protokoll verwenden und wir im Header den Parameter "connection: close" integriert haben, wird der Google-Server nach dem Senden der gesamten Daten die Verbindung beenden. Dies hat zur Folge, dass TSNE das "Disconnected"-Event ausführt (TSNE_Disconnected).
- Nach dem Aufruf des "Disconnect"-Ereignisses ist die Verbindung geschlossen und das Programm kann ab "TSNE_WaitClose" weiter arbeiten.
- Letzter Programmschritt ist das Beenden des Programmes selbst.







 

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

  Versionen Versionen