Home

Add

Edit

Without Linenumbers

Code in Textfield

Dieses "Mini-Tutorial" beschäftigt sich mit der Erzeugung eines Komplexen Servers mit TSNE.

Eine Ausführliche Bescheibung findet sich in den Entsprechenden Code-Abschnitten.

Der Quellcode umfasst 5 einzelne Datein:
server.bas
server_declare.bi
server_user.bi
server_comunicate.bi
server_protocol.bi

Entsprechend ist auch die Reihenfolge hier.

server.bas

  1. '####################################################################################################################################
    
  2. 'Dies ist der Server. Hier wird das gesammte Konstrukt definiert und der Zeitliche Ablauf gesteuert.
    
  3. 
    
  4. 
    
  5. 
    
  6. '####################################################################################################################################
    
  7. 'Zuerst fügen wir unsere benötigten Module hinzu. Die Funktionen in den Modulen sind entsprechend Bezeichnet,
    
  8. 'so das Funktionsaufrufe an Ihrem Nahmen erkannt und einem Modul zugeordnet werden können.
    
  9. 'Das macht die Namen teilweise zwar recht lang, ist jedoch dem späterem Programm prinzipiell egal, und beeinflusst auch nicht die
    
  10. 'geschwindigkeit, mit der das Programm abläuft. (Zumindest in 99% aller fälle ;)
    
  11. #INCLUDE Once "TSNE_V3.bi"              'Das TCP-IP Modul, welche die Netzwerk-kommunikation bewerkstelligt.
    
  12. #INCLUDE Once "server_declare.bi"       'In diesem Modul befinden sich, sofern benötigt, alle Deklarationen / Definitionen / Konstanten / ...
    
  13. #INCLUDE Once "server_user.bi"          'In diesem Modul befinden sich Funktionen um alle Benutzer zu verwalten
    
  14. #INCLUDE Once "server_comunicate.bi"    'In diesem Modul befinden sich Funktionen um mit einer oder mehreren Verbindung(en) zu kommunizieren.
    
  15. #INCLUDE Once "server_protocol.bi"      'Dieses Modul beherbergt das Kommunikationsprotokoll. Mit dessen Hilfe werden Nachrichten verpackt und verschickt.
    
  16. 
    
  17. 
    
  18. 
    
  19. '####################################################################################################################################
    
  20. 'Da es sich hier um einen Server handelt, müssen wir Vorkehrungen treffen, um den Server ""sauber zu beenden.
    
  21. 'Hierfür nutzen wir die sogenannten ""SIGNALE. Signale sind nachrichten vom Betriebssytem oder eine User an unser Programm
    
  22. 'welche diesem einen Zustandsänderung der Arbeitsumgebung signaliesieren. Z.b. Wenn jemand das Programm ""terminieren möchte.
    
  23. 'Hierzu benötigen wir eine Funktion welche wie folgt Definiert wird.
    
  24. Declare Function Signal Cdecl Lib "c" Alias "signal" (Byval V_Signal As Integer, V_Function As Any Ptr) As Integer
    
  25. 
    
  26. 'Zusätzlich Definieren wir einige benötigte Signal-Konstanten welche die unterschiedlichen Signale darstellen.
    
  27. Const SIGINT        = 2 'Terminal interrupt (ANSI)
    
  28. Const SIGQUIT       = 3 'Terminal quit (POSIX)
    
  29. Const SIGKILL       = 9 'Kill (can't be caught or ignored) (POSIX)
    
  30. Const SIGUSR1       = 10 'User defined signal 1 (POSIX)
    
  31. Const SIGUSR2       = 12 'User defined signal 2 (POSIX)
    
  32. Const SIGTERM       = 15 'Termination (ANSI)
    
  33. Const SIGCHLD       = 17 'Child process has stopped or exited, changed (POSIX)
    
  34. Const SIGSTOP       = 19 'Stop executing (can't be caught or ignored) (POSIX)
    
  35. Const SIGTSTP       = 20 'Terminal stop signal (POSIX)
    
  36. Const SIGPWR        = 30 'Power failure restart (System V)
    
  37. 
    
  38. 
    
  39. 
    
  40. '####################################################################################################################################
    
  41. '""Term ist eien Routine, welche den Server beendet. Sie enthält alle nötigen Aufgaben, welche zum sauberen Beenden nötig sind.
    
  42. Sub Term()
    
  43. 'Zuerst beenden wir unseren Server, da von Ihm aus alle Steuerbefehle eingehen.
    
  44. 'Es wäre unzweckmässig und hinderlich, wenn wir ihn erst zum ende hin beenden.
    
  45. 'Angenommen, wir schreiben "letzte-"Daten auf die Festplatte, oder schliessen unsere Log-Datei. Würde jetzt noch ein Befehle
    
  46. 'oder ein ereigniss vom Server eintreffen, dann könnten wir dies nicht mehr in das Log schreiben, oder kämen zu unvorhersehbaren
    
  47. 'zuständen, die nicht zu händeln sind.
    
  48. TSNE_Disconnect(G_Server_TSNEID)
    
  49. 'Nach dem Schliessen warten wir noch auf das vollständige ende des Servers
    
  50. TSNE_WaitClose(G_Server_TSNEID)
    
  51. 
    
  52. 'Jetzt müssen wir das selbe auch für alle Verbinundungen durchführen. Sonst können eine dieser ebenfalls noch Daten schicken, wärend
    
  53. 'wir den Server beenden.
    
  54. 'Hierzu senden wir zuerst alle Disconnect Sinale an die Clienten.
    
  55. Mutexlock(G_ClientMux)  'Das Client-Type Mutex speeren, damit wärend unserer Auflistung niemand an der List herum pfuscht.
    
  56. Dim TPtr As Client_Type Ptr = G_ClientF             'Wir beginnen mit der Auflistung beim ersten Element
    
  57. Do Until TPtr = 0
    
  58.     TSNE_Disconnect(TPtr->V_TSNEID)
    
  59.     TPtr = TPtr->V_Next
    
  60. Loop
    
  61. Mutexunlock(G_ClientMux)    'Nach dem Zugriff auf die Liste, die Sperre wieder aufheben.
    
  62. 'Danach Prüfen wir solange die Liste auf Einträge, bis keine mehr vorhanden sind.
    
  63. 'Wenn dies der Fall ist, sind auch alle Verbindungen beendet worden und wir können weiter arbeiten.
    
  64. Do
    
  65.     Mutexlock(G_ClientMux)
    
  66.     If G_ClientF = 0 Then Mutexunlock(G_ClientMux): Exit Do
    
  67.     Mutexunlock(G_ClientMux)
    
  68.     Sleep 10, 1
    
  69. Loop
    
  70. 
    
  71. 'Nächster Schritt ist Variabel. Je nachdem, was der Server so getan hat, müssen wir entsprechend dies Rückgängig machen, oder Schliessen.
    
  72. 'Z.B. Wenn noch Datein offen sind, sollten diese geschlossen werden.
    
  73. 
    
  74. 'Erst wenn alle diese Aufgaben Komplett sind, kann die Routine verlassen werden.
    
  75. End Sub
    
  76. 
    
  77. 
    
  78. 
    
  79. '####################################################################################################################################
    
  80. 'Signal für das 'Terminal-Interrupt' (STRG+C)
    
  81. Sub Signal_SIGINT Cdecl()
    
  82. 'Hier sollte das Programm sauber beendet werden. Idealer weise mit einem "Term()" (siehe oben)
    
  83. Term()
    
  84. 'Anschliessend Beenden wir das Programm mit einem Fehelrcode, welcher dem Signal entspricht.
    
  85. End SIGINT
    
  86. End Sub
    
  87. 
    
  88. '------------------------------------------------------------------------------------------------------------------------------------
    
  89. 'Signal für das 'Terminal-Terminate' (Beenden des Terminals)
    
  90. Sub Signal_SIGQUIT Cdecl()
    
  91. 'Selbes Verhalten wie beim "Signal_SIGINT Signal
    
  92. Term()
    
  93. End SIGQUIT
    
  94. End Sub
    
  95. 
    
  96. '------------------------------------------------------------------------------------------------------------------------------------
    
  97. 'Das Programm wurde mithilfe von ""KILL Terminiert
    
  98. Sub Signal_SIGKILL Cdecl ()
    
  99. 'Selbes Verhalten wie beim "Signal_SIGINT Signal
    
  100. Term()
    
  101. End SIGKILL
    
  102. End Sub
    
  103. 
    
  104. '------------------------------------------------------------------------------------------------------------------------------------
    
  105. 'Programm soll beendet werden! (Vermutlich von start-stop-deamon)
    
  106. Sub Signal_SIGTERM Cdecl ()
    
  107. 'Selbes Verhalten wie beim "Signal_SIGINT Signal
    
  108. Term()
    
  109. End SIGTERM
    
  110. End Sub
    
  111. 
    
  112. '------------------------------------------------------------------------------------------------------------------------------------
    
  113. 'Arbeitsfläche wurde beenden! Eigenes Programm muss ebenfalls beendet werden!
    
  114. Sub Signal_SIGCHLD Cdecl ()
    
  115. 'Selbes Verhalten wie beim "Signal_SIGINT Signal
    
  116. Term()
    
  117. End SIGCHLD
    
  118. End Sub
    
  119. 
    
  120. '------------------------------------------------------------------------------------------------------------------------------------
    
  121. 'Programmausführung muss gestopt werden!
    
  122. Sub Signal_SIGSTOP Cdecl ()
    
  123. 'Selbes Verhalten wie beim "Signal_SIGINT Signal
    
  124. Term()
    
  125. End SIGSTOP
    
  126. End Sub
    
  127. 
    
  128. '------------------------------------------------------------------------------------------------------------------------------------
    
  129. 'Ein Stop-Signal vom Terminal wurde empfangen
    
  130. Sub Signal_SIGTSTP Cdecl ()
    
  131. 'Selbes Verhalten wie beim "Signal_SIGINT Signal
    
  132. Term()
    
  133. End SIGTSTP
    
  134. End Sub
    
  135. 
    
  136. '------------------------------------------------------------------------------------------------------------------------------------
    
  137. 'Vermutlich wurde die Stromzufur vom Server unterprochen. Hier muss das Programm ebenfalls beendet werden.
    
  138. Sub Signal_SIGPWR Cdecl ()
    
  139. 'Da der Server keienn Strom mehr hat, und nur noch einen minimale Restkapazität vorhanden ist, sollte in dieser Routine
    
  140. 'nur das nötigste gemacht werden.
    
  141. 'Wichtig sind hier z.B. aufrufe um Datei Operationen noch schnell auf die Festplatte zu schreiben, bevor der Server Komplett
    
  142. 'Stromlos wird.
    
  143. 'Ein Solches Ereigniss wird z.B. von einer USV (Unterbrechungsfreie Stromversorgung) geschickt, oder in einigen Fällen auch
    
  144. 'vom Motherboard, wenn dies eine Solche Funktion unterstützt.
    
  145. 
    
  146. 'Erst, wenn diese ""wichtigen aufgaben abgearbeitet wurden, können wir den Server (sofern er nicht schon durch Stromlosen
    
  147. 'Computer beendet wurde) normal beenden.
    
  148. Term()
    
  149. End SIGINT
    
  150. End Sub
    
  151. 
    
  152. '------------------------------------------------------------------------------------------------------------------------------------
    
  153. 'Ein Manuelles Benuzterereigniss
    
  154. Sub Signal_SIGUSR1 Cdecl ()
    
  155. 'Diese Funktion kann verwendet werden, um dem Programm bestimmte Aktionen mitzuteilen.
    
  156. 'Wie das im einzelnen Realisiert wird, ist freigestellt.
    
  157. End Sub
    
  158. 
    
  159. '------------------------------------------------------------------------------------------------------------------------------------
    
  160. 'Noch ein Manuelles Benuzterereigniss
    
  161. Sub Signal_SIGUSR2 Cdecl ()
    
  162. 'Wie bei Signal_SIGUSR1 gibt es hier eienn zweite möglichkeit für ein anderes beliebiges Verhalten.
    
  163. End Sub
    
  164. 
    
  165. 
    
  166. 
    
  167. '####################################################################################################################################
    
  168. 'Die folgende Routine zeigt uns an, wenn eine Verbindung mit der ID (V_TSNEID) beendet wurde.
    
  169. 'Dabei ist es egal ob wir die Verbindung beendet haben, oder die Gegenseite. Diese Routine wird immer aufgerufen!
    
  170. Sub TSNE_Disconnected(Byval V_TSNEID As Uinteger)
    
  171. 'Um zu demonstrieren, das sich etwas ""tut geben wir das einfach auf dem Terminal mit Print aus.
    
  172. Print "DISCONNECTED: " & Str(V_TSNEID)
    
  173. 
    
  174. 'In dieser Routine müssen alle Aufgaben stehen um eine Verbindung sauber zu trennen.
    
  175. 'Das betrifft z.B. das Auslogen eines Benutzers, welcher mit dieser Verbindung verbunden ist.
    
  176. 'oder das Speicehrn von Daten auf der Festplatte, oder sonstige Verbinungsspezifische dinge.
    
  177. 
    
  178. 'Auf jeden Fall MUSS am ende dieser Funktion der Temporäre Datenspeicher des benutzers (welchen wir in der TSNE_NewConnection Routine angelegt haben)
    
  179. 'wieder geleert und freigegeben werden.
    
  180. 
    
  181. Dim TClientPtr As Client_Type Ptr
    
  182. Mutexlock(G_ClientMux)                                  'Da der folgende Funktionsaufruf auf einen Globalen Speicher zugreifen wird
    
  183.                                                         'müssen wir das Mutex hierfür sperren.
    
  184. TClientPtr = Server_Com_GetClientType(V_TSNEID)         'Wir rufen die Funktion auf,w elche uns den Pointer auf den Speicher zurück gibt.
    
  185. If TClientPtr = 0 Then Mutexlock(G_ClientMux): Exit Sub 'Sollte kein Pointer gefunden werden, existiert auch kein Speicher, und wir können die Routine verlassen.
    
  186. With *TClientPtr                                        'Andernfalls Greifen wir auf diesen Speicher zu
    
  187.     If .V_LoginOK = 1 Then                              'und prüfen, ob der Benutzer angemeldet war.
    
  188.         'Wenn dies der fall war, solten wir diesen Benutzer auch wieder abmelden.
    
  189.         'Unter umständen müssen wir dies auch der Gemeinschaft (alle anderen verbundenen Clienten) mitteilen.
    
  190.     End If
    
  191.     'Hier könnten noch weitere aufgaben ausgefürht werden.
    
  192.     'z.B. das speichern von Highscores, oder das hinzufügen einer Log-Zeile in der steht, das der Benutzer X zur Zeit Y sich abgemeldet oder die verbindung getrennt hat.
    
  193. 
    
  194. End With
    
  195. 'Nachdem alle benutzerspezifischen aufgaben ausgeführt wurden, müssen wir den Speicher wieder freigeben
    
  196. If TClientPtr->V_Next <> 0 Then TClientPtr->V_Next->V_Prev = TClientPtr->V_Prev     'Wenn wir nicht das letzte element sind, dann geben wir unserem vorgänger bescheid, das unser nachvolger jetzt sein nachfolger ist.
    
  197. If TClientPtr->V_Prev <> 0 Then TClientPtr->V_Prev->V_Next = TClientPtr->V_Next     'Wenn wir nicht das erste element sind, dann geben wir unserem nachfolger bescheid, das unser vorgänger jetzt sein vorgänger ist.
    
  198. If G_ClientF = TClientPtr Then G_ClientF = TClientPtr->V_Next                       'Wenn wir das erste element waren, dann setzen wir unseren nachfolger als neuen ersten
    
  199. If G_ClientL = TClientPtr Then G_ClientL = TClientPtr->V_Prev                       'wenn wir nicht das letzte element sind, dann setzen wir unseren vorgänger als neuen letzten
    
  200. Deallocate(TClientPtr)                                                              'Zum schluss den Speicher freigeben.
    
  201. Mutexunlock(G_ClientMux)                                'Nachdem alles abgearbeitet wurde, können wir das Mutex wieder entsperren.
    
  202. End Sub
    
  203. 
    
  204. '------------------------------------------------------------------------------------------------------------------------------------
    
  205. 'Diese Routine wird kurz nach dem erzeugen der Verbindung via (TSNE_Create_Accept) aufgerufen und zeigt uns an,
    
  206. 'das die Verbindung mit der ID (V_TSNEID) stabil hergestellt wurde und bereit ist mit uns udn wir mit Ihr zu Kommunizieren.
    
  207. Sub TSNE_Connected(Byval V_TSNEID As Uinteger)
    
  208. 'Um zu demonstrieren, das sich etwas ""tut geben wir das einfach auf dem Terminal mit Print aus.
    
  209. Print "CONNECTED:    " & Str(V_TSNEID)
    
  210. End Sub
    
  211. 
    
  212. '------------------------------------------------------------------------------------------------------------------------------------
    
  213. 'Immer wenn die gegenseite uns Daten sendet, wird diese Funktion aufgerufen. Sie enthält die ID (V_TSNEID) der Verbindung welche
    
  214. 'daten zu uns geschickt hat, und die Daten selbst (V_Data).
    
  215. Sub TSNE_NewData(Byval V_TSNEID As Uinteger, Byref V_Data As String)
    
  216. 'Um zu demonstrieren, das sich etwas ""tut geben wir das einfach auf dem Terminal mit Print aus.
    
  217. Print "NEW DATA:     " & Str(V_TSNEID) & " DATALEN:" & Str(Len(V_Data))
    
  218. 
    
  219. 'Um Daten zu verarbeiten müssen wir zuerst den Speicher finden, welcher unsere Verbindung Representiert.
    
  220. 'hierzu suchen wir (wie auch schon beim TSNE_Disconnected) nach dem Speicher mit einer speziellen Funktion.
    
  221. Dim TClientPtr As Client_Type Ptr
    
  222. Mutexlock(G_ClientMux)
    
  223. TClientPtr = Server_Com_GetClientType(V_TSNEID)
    
  224. If TClientPtr = 0 Then Mutexlock(G_ClientMux): Exit Sub
    
  225. 'Wenn der Speichergefunden wurde, dann lesen wir den Temporären Datenspeicher (Puffer) in einer Variable aus, und hängen die neuen Daten daran an.
    
  226. Dim TData As String = TClientPtr->V_Data & V_Data
    
  227. 'Danach entsperren wir das Mutex wieder, damit andere Verbndungen die gelegenheit haben ebenfalls Daten zu verarbeiten, wärend wir unsere verarbeiten.
    
  228. Mutexunlock(G_ClientMux)
    
  229. 'Nachdem das Mutex entsperrt wurde, übermitteln wir die Daten an die Auswert-Funktion (Protocoll)
    
  230. Server_Protocol_ReadStream(V_TSNEID, TData)
    
  231. 'Sobald diese Funktion beendet wurde, müssen wir die übrigen Daten, welche in TData stehen, wieder in den Puffer zurück schreiben.
    
  232. Mutexlock(G_ClientMux)          'Dazu sperren wir wieder den Speicher
    
  233. TClientPtr->V_Data = TData      'und schreiben die Daten zurück
    
  234. Mutexunlock(G_ClientMux)
    
  235. 'Alle weiteren Auswertungen wurden jetzt von der "Server_Protocol_ReadStream()" Routine vorgenommen, und wir können hier unsere Routine verlassen.
    
  236. End Sub
    
  237. 
    
  238. 
    
  239. 
    
  240. 
    
  241. '####################################################################################################################################
    
  242. 'Diese Sub Routine ist Bestandteil von TSNE. Sie wird uns signalisieren (ähnlich den SIGNALEN), wenn ein Client
    
  243. 'eine verbindung mit uns wünscht. Daraufhin müssen wir entsprechend reagieren.
    
  244. Sub TSNE_NewConnection(Byval V_TSNEID As Uinteger, Byval V_RequestID As Socket, Byval V_IPA As String)
    
  245. 'Wir werden hier nicht überprüfen ob ein maximum an Verbindungen vorliegt, um überschüssige Verbindungen abzulehnen.
    
  246. 'Wir Akzeptieren folgich jede Verbindung. (was eigentlich nicht ratsam ist ;)
    
  247. 'Um eine Verbindugn zu akzeptieren rufen wir die entsprechende TSNE Funktion auf und übergeben Ihr die Anfrage-ID (V_RequestID)
    
  248. 'zusammen mit eienr Variable welche nach dem erfolgreichen Aufruf der Funktion die ID der Verbindugn enthalten wird (TNewTSNEID).
    
  249. 'Zusätzlich übergeben wir der Funktion 3 Pointer auf Routinen, die aufgerufen werden sollen, wenn diese neue Verbindung
    
  250. 'ein entsprechenes Ereigniss auslöst. In diesen Routinen können dann entsprechend nach Unseren bedingungen handeln.
    
  251. 
    
  252. Mutexlock(G_ClientMux)                      'Zuerst wird das Mutex gesperrt, welches den Zugriff auf die G_ClientF und G_ClientL Variablen schützt.
    
  253. Dim TNewTSNEID As Uinteger
    
  254. Dim RV As Integer = TSNE_Create_Accept(V_RequestID, TNewTSNEID, "", @TSNE_Disconnected, @TSNE_Connected, @TSNE_NewData)
    
  255. If RV <> TSNE_Const_NoError Then            'Sollte ein Fehler aufgetreten sein, dann können wir nicht weiter arbeiten. (keine Verbindung zustande gekommen)
    
  256.     Mutexunlock(G_ClientMux)                'Wir entsperren das Mutex wieder und
    
  257.     Exit Sub                                'verlassen diese Routine
    
  258. End If
    
  259. 'Andernfalls erzeugen wir einen Speicherplatz für Temoräre Daten welche uns diese Verbindung mitteilt.
    
  260. 'Z.B. die eingehenden Daten werden dort zwischengelagert, sowie der Benutzername der Verbindung (falls er sich später einlogt)
    
  261. 'oder die IP-Adresse, die Verbindugnsnummer (TSNEID), oder sonstige Verbindungsbezogene Informationen
    
  262. If G_ClientL <> 0 Then                                      'Prüfen ob wir schon Elemente angelegt haben. Wenn ja, dann
    
  263.     G_ClientL->V_Next = Callocate(Sizeof(Client_Type))      'Hängen wir ein neue erzeugtes Element an die Liste an.
    
  264.     G_ClientL->V_Next->V_Prev = G_ClientL                   'Zeigem diesem neuem Element seinem Vorgänger
    
  265.     G_ClientL = G_ClientL->V_Next                           'Und machen das Neue Element zum Letzten Element der Liste.
    
  266. Else                                                        'Sollten wir noch keine Einträge erzeugt haben, dann
    
  267.     G_ClientL = Callocate(Sizeof(Client_Type))              'Erzeugen wir ein neues Element udn setzen es direkt als Letzten
    
  268.     G_ClientF = G_ClientL                                   'Und gleichzeitig auch als Erstes Element.
    
  269. End If
    
  270. With *G_ClientL                             'Das Neue Element wird ausgewählt um es mit Informationen zu bestücken.
    
  271.     .V_TSNEID   = TNewTSNEID                'Die Verbindugns-ID
    
  272.     .V_IPA      = V_IPA                     'Die IP-Adresse der neuen Verbindung
    
  273.     .V_ConTime  = Now()                     'Wann die Verbindung zustande kam (jetzt)
    
  274. End With
    
  275. Mutexunlock(G_ClientMux)                    'Zum Schluss das Mutex wider entsperren.
    
  276. 'Damit ist eine neue Verbindung etabliert und bereit mit uns zu Kommunizieren.
    
  277. 'Der nächste Schritt der neuen Verbindung ist der Aufruf der von uns oben angegebenen "@"TSNE_Connected Routine.
    
  278. 'Dies wird anzeigen, das die neue Verbindung erfolgreich hergestellt wurde. Diese Routine liefert auch die neue Verbindungs-ID in (V_TSNEID) mit.
    
  279. End Sub
    
  280. 
    
  281. 
    
  282. 
    
  283. '####################################################################################################################################
    
  284. 'Dies ist die Hauptfunktion
    
  285. Function Main() As Integer
    
  286. 'Nach dem Aufruf dieser Funktion sollten zuerst alle wichtigen udn betrieblichen Funktionen ausgeführt werden.
    
  287. 
    
  288. 'Gut, das ist nciht ganz so wichtig, aber man weis, wo das Programm gerade steht.
    
  289. Print "Starte..."
    
  290. 
    
  291. 'Die Signal-Callbacks dem System mitteilen. Dies ist nötig, damit das Betriebssystem die Funktionen kennt, welche aufgerufen werden
    
  292. 'wenn eines der angegebenen Ereignisse auftritt.
    
  293. 'Je nach Situation werden entsprechend unterschiedliche Funktionen aufgerufen.
    
  294. Signal(SIGQUIT, @Signal_SIGQUIT)
    
  295. Signal(SIGKILL, @Signal_SIGKILL)
    
  296. Signal(SIGTERM, @Signal_SIGTERM)
    
  297. Signal(SIGCHLD, @Signal_SIGCHLD)
    
  298. Signal(SIGSTOP, @Signal_SIGSTOP)
    
  299. Signal(SIGTSTP, @Signal_SIGTSTP)
    
  300. Signal(SIGPWR, @Signal_SIGPWR)
    
  301. Signal(SIGUSR1, @Signal_SIGUSR1)
    
  302. Signal(SIGUSR2, @Signal_SIGUSR2)
    
  303. 
    
  304. 'Der nächste Schritt sieht vor, alle nötigen Vorbereitugnen zu treffen, welche ein Server für seinen betrieb benötigt.
    
  305. 'Darunte fallen z.B. das Laden von Configurations-Datein, Highscore zustände, Benutzerlisten, Recht, Sonstige Datein, usw.
    
  306. 
    
  307. 'Wenn dies erfolgreich umgesetzt wurde, kann der Server seinen Betrieb aufnahmen, indem wir diesen Erzeugen.
    
  308. 'Dies wird durch eine Do Loop schleife getätigt, die solange versucht den Server zu erstellen, bis dies erfolgreich war.
    
  309. 'Unter umständen kann es nämlich dazu führen, das ein Socket noch von einem anderem Prozess belegt ist, oder
    
  310. 'das Socket noch nicht freigegeben wurde, (z.B. nach einem Programmabsturz)
    
  311. Dim RV As Integer
    
  312. Do
    
  313.     'Das erzeugen des Servers wird mithilfe der TSNE vollführt. Näheres hierzu findet sich in der TSNE-Dokumentation
    
  314.     RV = TSNE_Create_Server(G_Server_TSNEID, 1234, 100, @TSNE_NewConnection)
    
  315.     If RV = TSNE_Const_NoError Then Exit Do
    
  316.     'Das Warten von einer Sekunde zwischen den Versuchen ist eine angenehme Zeitspanne.
    
  317.     Sleep 1000, 1
    
  318. Loop
    
  319. 'Nachdem der Server erfolgreich gestartet wurde, ist das Programm einsatzbereit.
    
  320. 'Jetzt sollten keine Server-Betrieblichen Dinge mehr geladen werden. Dies sollte VOR dem Erzeugen des Server
    
  321. 'geamcht werden. Sonst könnte es zu Problemen kommen, z.B. das ein "just in diesem "Moment verbundener Client versucht
    
  322. 'auf etwas zuzugreifen, das wir noch garnicht iniziiert haben.
    
  323. 'Daher macht man solche ""PreInits VOR dem anlegen des Servers.
    
  324. 
    
  325. 'Nachdem alle Läuft, schreiben wir das auf unsere Konsole.
    
  326. Print "Bereit!"
    
  327. 'Die Hauptschleife. In dieser können wir z.B. Prüfungen durchführen, ob eine Verbinung noch stabil ist (TimeOut),
    
  328. 'oder Daten senden (falls ein Puffer hierfür verwendet wird)
    
  329. 'oder sonstige Zeitgesteuerte Aufgabem oder oder oder.
    
  330. 'Ein Beenden der Hauptschleife ist unnötig, kann aber für die Entwicklugn hilfreich sein (z.B. beim ESC drücken)
    
  331. 'Wenn der Server selbstständig arbeitet (Linux-Deamon), dann sind eingaben nicht möglich. Hier wird das beenden durch
    
  332. 'eines der zuvor registrierten Signale übernommen.
    
  333. 'Dennoch definieren wir ein Tastendruck (ESC), um bei der Entwicklung einfacher den Server beenden zu können.
    
  334. Do Until Inkey() = Chr(27)
    
  335.     Sleep 10, 1
    
  336. Loop
    
  337. 'Da wir das ESC hinzugefügt haben, müssen wir natürlich nach dem verlassen der hauptschleife das Programm auch Beenden.
    
  338. 'Hierfür nutzen wir die schon bekannte TERM() Routine
    
  339. 
    
  340. 'Zuvor geben wir das beenden auf dem Terminal aus.
    
  341. Print "Beende..."
    
  342. 
    
  343. Term()
    
  344. 'Zum schluss geben wir 0 als Rückgabewert zurück, woraufhin unser Programm (siehe weiter unten) mit "kein "Fehler beendet wird.
    
  345. Return 0
    
  346. End Function
    
  347. 
    
  348. 
    
  349. 
    
  350. '####################################################################################################################################
    
  351. 'die Hauptfunktion Aufrufen und den Rückgabewert beim verlassen dieser, als End Signal verwenden.
    
  352. End Main()
    



server_declare.bi

  1. '####################################################################################################################################
    
  2. 'Hier befinden sich alle nötigen Deklarationen von Funktionen oder Sub Routinen, Konsstanten, Typen, Enums, usw.
    
  3. 
    
  4. 
    
  5. 
    
  6. '####################################################################################################################################
    
  7. 'Dieser Enum wird von den meisten Funktionen als Rückgabe genutzt. Es sind Konstanten, welche Fehler (oder auch nicht) definieren.
    
  8. Enum Server_Return_Enum
    
  9.     'Positive Werte sind OK-Werte
    
  10.     SR_NoError                  = 1     'Kein Fehler entstanden
    
  11.     
    
  12.     '0 ist standardmässig ein unbekannter Fehler
    
  13.     SR_UnknownError             = 0     'z.B. eine Funktion ohne Return
    
  14.     
    
  15.     'Negative Werte sind bekannte Fehler
    
  16.     SR_DataStreamError          = -1    'Ein Datenstrom fehler.
    
  17.     SR_NotEnoughData            = -2    'Nicht genügend Daten vorhanden
    
  18.     SR_ComError                 = -3    'Kommunikationsfehler (z.B. fehler beim Senden von Daten)
    
  19. End Enum
    
  20. 
    
  21. 
    
  22. 
    
  23. '####################################################################################################################################
    
  24. 'Das folgende ENUM definiert alle verfügbaren Kommandos welche der Server beherscht.
    
  25. 'Die Enums sind in Gruppen zu je 100 Element aufgeteilt.
    
  26. Enum Protocol_Command_Enum
    
  27.     'Unbekannt
    
  28.     PCE_Unknown                 = 0         '0 ist standardmässig ein unbekanntes Kommandos
    
  29.     
    
  30.     'Fehler
    
  31.     PCE_Error_UnknownCMD        = 100       'Unbekanntes Kommando
    
  32.     
    
  33.     'Benutzer
    
  34.     PCE_User_Regist             = 201       'neuen Benutzer Registrieren
    
  35.     PCE_User_Login              = 202       'Benutzer Anmelden
    
  36.     
    
  37. End Enum
    
  38. 
    
  39. 
    
  40. 
    
  41. '####################################################################################################################################
    
  42. 'Dieser Type representiert einen Verbundenen Benutzer.
    
  43. 'Der Type ist eine LinkedList (V_Next, V_Prev as Ptr). Näheres zur LinkedList siehe: http://www.freebasic-portal.de/tutorials/verkettete-listen-in-freebasic-37.html
    
  44. Type Client_Type
    
  45.     V_Next                      As Client_Type Ptr      'Zeigt auf das nächste Element in der LinkedList
    
  46.     V_Prev                      As Client_Type Ptr      'Zeigt auf das vorherige Element in der LinkedList
    
  47.     
    
  48.     V_TSNEID                    As Uinteger             'Die TSNEID von TSNE_V3 welche die Benutzerverbindugn identifiziert
    
  49.     V_IPA                       As String               'Die IP-Adresse dieser Verbindung
    
  50.     V_ConTime                   As Double               'Der Zeitpunkt zu dem die Verbindung zustande kam
    
  51.     
    
  52.     V_Username                  As String               'Der Benutzername
    
  53.     V_LoginOK                   As Ubyte                'Steht auf 1, wenn der Login erfolgreich war.
    
  54.     
    
  55.     V_Data                      As String               'Hier werden die Empfangenen Daten der Verbindung zwischengespeichert, bis sie verarbeitet wurden.
    
  56. End Type
    
  57. '------------------------------------------------------------------------------------------------------------------------------------
    
  58. Dim Shared G_ClientF            As Client_Type Ptr      'Das Erste Element der LinkedList
    
  59. Dim Shared G_ClientL            As Client_Type Ptr      'Das Letzte Element der LinkedList
    
  60. Dim Shared G_ClientMux          As Any Ptr              'Ein Mutex, das die LinkedList schützt
    
  61. 
    
  62. 
    
  63. 
    
  64. '####################################################################################################################################
    
  65. 'Einige Variablen, welceh Global Nutzbar sein sollen.
    
  66. Dim Shared G_Server_TSNEID      As Uinteger             'In dieser Variable wird die TSNEID des Servers gespeichert.
    
  67. 
    
  68. 
    
  69. 
    
  70. '####################################################################################################################################
    
  71. 'Einige Funktionen müssen ""Vordeclariert werden, um Sie im Server nutzen zu können, da die Implementierung erst später als die Nutzung erfolgt.
    
  72. Declare Function Server_Protocol_MakeStream(Byref V_Command As Protocol_Command_Enum, Byref V_OptionalDataString As String = "") As String
    
  73. 
    



server_user.bi

  1. '####################################################################################################################################
    
  2. Function User_Regist(V_Username As String, V_Passwort As String) As Server_Return_Enum
    
  3. 'Hier kann jetzt die registrierung eines benutzers erfolgen.
    
  4. 'Bei Dateisystem Zugriffe sollte vor dem zugriff darauf eine Mutex-Sperre erfolgen!!!
    
  5. 
    
  6. Return SR_NoError
    
  7. End Function
    
  8. 
    
  9. 
    
  10. 
    
  11. '####################################################################################################################################
    
  12. Function User_Login(V_Username As String, V_Passwort As String) As Server_Return_Enum
    
  13. 'Hier kommt der source für den Login hinein.
    
  14. 'Bei Dateisystem Zugriffe sollte vor dem zugriff darauf eine Mutex-Sperre erfolgen!!!
    
  15. 
    
  16. Return SR_NoError
    
  17. End Function
    
  18. 
    
  19. 
    
  20. 
    



server_comunicate.bi

  1. '####################################################################################################################################
    
  2. 'Das "Kommunikations-"Modul
    
  3. 'Hier stehen Funktionen bereit, welche die Kommunikation mit einem oder mehreren, oder allen Benutzern ermöglicht.
    
  4. 
    
  5. 
    
  6. 
    
  7. '####################################################################################################################################
    
  8. Function Server_Com_GetClientType(V_TSNEID As Uinteger) As Client_Type Ptr
    
  9. 'Diese Funktion sucht nach dem Benutzer-Type in der LinkedList udn gibt Ihn, fall gefunden, zurück.
    
  10. Dim TPtr As Client_Type Ptr = G_ClientF             'Wir beginnen mit der Suche beim ersten Element
    
  11. Do Until TPtr = 0                                   'Die Schleife wird solange wiederholt, bis das ende erreicht wurde.
    
  12.     If TPtr->V_TSNEID = V_TSNEID Then Return TPtr   'Wenn die gesuchte TSNEID gefunden wurde, dann das Element zurückgeben.
    
  13.     TPtr = TPtr->V_Next                             'Als nächstes Element wird das nächste gewählt.
    
  14. Loop
    
  15. Return 0                                            'Wenn dieser nicht gefunden wurde, dann wird 0 zurück gegeben.
    
  16. End Function
    
  17. 
    
  18. 
    
  19. 
    
  20. '####################################################################################################################################
    
  21. Function Server_Com_SendToUser(V_TSNEID As Uinteger, V_Command As Protocol_Command_Enum, V_OptionalData As String = "") As Server_Return_Enum
    
  22. 'Diese Funktion sendet zu EINEM Benutzer eine Nachricht
    
  23. 'Hierfür wird zuerst die Nachricht vorbereitet
    
  24. Dim TMSG As String = Server_Protocol_MakeStream(V_Command, V_OptionalData)      'Diese Funktion befindet sich im "server_protocol."bi Modul
    
  25. 'Danach wird die Nachricht verschickt.
    
  26. If TSNE_Data_Send(V_TSNEID, TMSG) <> TSNE_Const_NoError Then 'Ist beim versenden ein Fehler aufgetreten,
    
  27.     'Dann wird die Verbindung getrennt.
    
  28.     TSNE_Disconnect(V_TSNEID)
    
  29.     'Entsprechenden Fehler zurück geben.
    
  30.     Return SR_ComError
    
  31. End If
    
  32. 'Andernfals ein "Kein "Fehler zurückgeben
    
  33. Return SR_NoError
    
  34. End Function
    
  35. 
    
  36. 
    
  37. 
    
  38. '####################################################################################################################################
    
  39. Function Server_Com_SendToAll(V_Command As Protocol_Command_Enum, V_OptionalData As String = "") As Server_Return_Enum
    
  40. 'Diese Funktion sendet zu ALLEN Benutzer eine Nachricht
    
  41. 'Hierfür wird zuerst die Nachricht vorbereitet
    
  42. Dim TMSG As String = Server_Protocol_MakeStream(V_Command, V_OptionalData)      'Diese Funktion befindet sich im "server_protocol."bi Modul
    
  43. 'Nächster Schritt sieht vor, eine Liste der Clienten zu erzeugen. Das ist nötig, damit wir zum einen nicht in eienn Konflikt mit der LinkedList kommen und
    
  44. 'zum anderen, damit wir kein langes MutexLock auf unsere Liste ausführen müssen und damit die Kommunikation zu anderen Clienten stören oder verlangsamen.
    
  45. Dim TSIDL() As Uinteger 'Die Liste welche alle derzeit gültigen TSNEID's enthält.
    
  46. Dim TSIDC As Uinteger   'Die Anzahl Einträge dieser Liste
    
  47. Dim TSIDX As Uinteger   'Die Anzahl der Gesammten Erzeugten Plätze in der Liste (ist für das erzeugen schneller)
    
  48. Mutexlock(G_ClientMux)  'Das Client-Type Mutex speeren, damit wärend unserer Auflistung niemand an der List herum pfuscht.
    
  49. Dim TPtr As Client_Type Ptr = G_ClientF             'Wir beginnen mit der Auflistung beim ersten Element
    
  50. Do Until TPtr = 0
    
  51.     If TPtr->V_LoginOK = 1 Then 'Nur dann hinzufügen, wenn der Benutzer auch angemeldet ist.
    
  52.         TSIDC += 1              'Den Counter hochzählen
    
  53.         If TSIDC > TSIDX Then   'Wenn der Counter die Anzahl der Plätze übersteigt
    
  54.             TSIDX += 10         'Dann 10 weitere Plätze hinzufügen
    
  55.             Redim Preserve TSIDL(TSIDX) As Uinteger 'Den den Speicher hierfür erzeugen
    
  56.         End If
    
  57.         TSIDL(TSIDC) = TPtr->V_TSNEID   'Danach die TSNE-ID in die Liste einfügen.
    
  58.     End If
    
  59.     TPtr = TPtr->V_Next
    
  60. Loop
    
  61. Mutexunlock(G_ClientMux)    'Nach dem Zugriff auf die Liste, die Sperre wieder aufheben.
    
  62. 'Wärend die Daten gesendet weren, ist das Mutex entsperrt, so das von anderen clienten eingehende Nachrichten sofort parallel verarbeitet werden können.
    
  63. 'Andernfalls müsste der client warten, bis wir hier fertig sind udn das Mutex wieder entsperrt haben.
    
  64. For X As Uinteger = 1 To TSIDC
    
  65.     'Danach wird die Nachricht verschickt.
    
  66.     If TSNE_Data_Send(TSIDL(TSIDC), TMSG) <> TSNE_Const_NoError Then 'Ist beim versenden ein Fehler aufgetreten,
    
  67.         'Dann wird die Verbindung getrennt.
    
  68.         TSNE_Disconnect(TSIDL(TSIDC))
    
  69.     End If
    
  70. Next
    
  71. 
    
  72. 'dieses Verfahren ist ein direktes. Empfohlen wird jedoch bei einem Server auf ein Indirektes Senden über zu gehen.
    
  73. 'Hierfür wird eine Liste für jeden Benutzer angelegt, in dem jedes zu sendende Datenpacket enthalten ist.
    
  74. 'Dieses wird anschliessend über einen eigenen Haupt-Thread (z.B. dem App-Thread) verschickt. Das beschleunigt den ablauf
    
  75. 'und führt nicht zu geschwindigkeitsengpässe.
    
  76. Return SR_NoError
    
  77. End Function
    
  78. 
    
  79. 
    



server_protocol.bi

  1. '####################################################################################################################################
    
  2. 'Dies ist das Kommunikationsprotokoll-Modul
    
  3. 
    
  4. 'In diesem befinden sich alle Komponenten welche das Protokoll für die Kommunikation zwischen Client und Server bereitstellen.
    
  5. 'Ein Server hat, vorallem bei vielen Clienten, eine Menge an Verwaltungs-kram zu bearbeiten. Teilweise auch viel Daten zu übertagen.
    
  6. 'Daher sollte ein Protokoll möglichst effizient sein, oder zumindest sehr Kompakt und ""Direkt.
    
  7. 
    
  8. 'Aus diesem Grunde wird in diesem Protokoll ein ""Streaming Verfahren angewendet.
    
  9. 'Das Senden von ""Informationen wird ""Komprimiert, zu einem ""Element zusammen gesetzt und zur Übertragung fertig gemacht.
    
  10. 'der Empfangsteil ist entsprechend das Gegenstück. Es zerlegt eingehende Daten wieder in Ihre Ursprungsform und übergibt Sie der
    
  11. 'entsprechenden Funktion zur Verarbeitung.
    
  12. 
    
  13. 'Ein weiterer Punkt eines Protokolls ist die sicherstellung der Nachrichten-Konsistenz. Sprich. Erst dann eien Nachricht einer Funktion
    
  14. 'oder einem Verarbeitungsprozess zu übergeben, wenn die Nachricht auch vollständig empfangen wurde.
    
  15. 'Dies wird mit einem "Längen-"Indikator vor jeder Nachricht bewerkstelligt. Dieses Gibt an, wie lang die Nachricht ist.
    
  16. 'Dies stellt sicher, das erst dann die nachricht vom restlichen Datenstrom abgeschnitten wird, Wenn genügend Daten vorhanden sind.
    
  17. 'Und auch erst dan diese Nachricht zur weiterverarbeitung gegeben wird.
    
  18. 
    
  19. 
    
  20. 
    
  21. '####################################################################################################################################
    
  22. Function Server_Protocol_MakeStream(Byref V_Command As Protocol_Command_Enum, Byref V_OptionalDataString As String = "") As String
    
  23. 'Diese Funktion erzeugt aus einem Commando und eventuell angehängten Daten ein Element
    
  24. 'Hierbei ist ein Element wie folgt aufgebaut
    
  25. '<ELEMENTLÄNGE><COMMANDO><DATEN>
    
  26. 'ELEMENTLÄNGE gibt an, wie lang die Darauffolgenden Daten incl. dem Commando sind.
    
  27. 'Diese Elemente sehen Byteweise wie folgt aus:
    
  28. '<ELEMENTLÄNGE-1><ELEMENTLÄNGE-0><COMMANDO-3><COMMANDO-2><COMMANDO-1><COMMANDO-0><DATEN>
    
  29. 'Ein Beispiel: Commando = 8, Daten = ""Hallo
    
  30. 'Byteweise würde dieses Element wie folgt aussehen (Dezimalwerte)
    
  31. '0 0 0 9 0 0 0 8 72 97 108 108 111
    
  32. '^ ^ ^ ^ ^ ^ ^ ^  ^  ^  ^   ^   ^
    
  33. '| | | | | | | |  |  |  |   |   \__ o \
    
  34. '| | | | | | | |  |  |  |   \______ l |
    
  35. '| | | | | | | |  |  |  \__________ l |> Bilden die Optionalen Daten
    
  36. '| | | | | | | |  |  \_____________ a |
    
  37. '| | | | | | | |  \________________ H /
    
  38. '| | | | \_\_\_\___________________ 8 => Ist das Kommando
    
  39. '\_\_\_\___________________________ 9 => Ist die Länge des Elementes (4 Byte für das Kommando + 5 Byte Daten)
    
  40. 
    
  41. Dim T As String
    
  42. 'Zuerst wird das Kommando in eine Byte-Sequenz Zerlegt
    
  43. 'Wir nutzen hierfür KEINEN CAST, um sicherzustellen, das die Reihenfolge der Bytes überall gleich bleibt.
    
  44. T = Chr((V_Command Shr 24) And 255, (V_Command Shr 16) And 255, (V_Command Shr 8) And 255, (V_Command And 255))
    
  45. 'Anschliessend hängen wir die Optionalen Daten an.
    
  46. T += V_OptionalDataString
    
  47. 'Hier speichern wir die Länge dieses Elementes in eien Variable ab
    
  48. Dim TLen As Uinteger = Len(T)
    
  49. 'Zum schluss muss der Stream erzeugt werden. Hier wird die Länge in Bytes umgewandelt, und anschliessend das Element angehängt.
    
  50. 'Das ganze wird dann als Funktionsrückgabe genutzt.
    
  51. Return Chr((TLen Shr 24) And 255, (TLen Shr 16) And 255, (TLen Shr 8) And 255, (TLen And 255)) & T
    
  52. 'Optimierungsmöglichkeiten wären hierbei, ein Element in der Gröse zu beschränken, indem die Optionalen Daten z.B. nur Max. (&HFFFF - 2) lang sein dürfen.
    
  53. 'Und die Komamndos nur bis &HFFFF gehen (2 Byte). Dann kann automatisch die Elementlängen-Bytes und die Commando-Bytes von 4 Byte auf 2 Byte Reduziert werden
    
  54. 'Das ist eim Optimalfall eine Ersparniss von 50%!
    
  55. End Function
    
  56. 
    
  57. 
    
  58. 
    
  59. '####################################################################################################################################
    
  60. Function Server_Protocol_GetStream(Byref RV_Data As String, Byref R_Command As Protocol_Command_Enum, Byref R_OptionalDataString As String = "") As Server_Return_Enum
    
  61. 'Diese Funktion extrahiert aus einem Empfangenem Datenstrom (Stream) die einzelnen Elemente, und gibt sie zurück
    
  62. 'Der Aufbau eines solchen Elementes findet sich eien Funktion weiter Oben (Server_Protocol_MakeStream)
    
  63. 'Zuerst wrid überprüft, ob genügend Daten zur verfügung stehen
    
  64. If Len(RV_Data) < 8 Then 'Wenn die Funktion ""Server_Protocol_MakeStream wie oben beschrieben Optimiert wurde, dann muss hier 4 stat 8 stehen.
    
  65.     'Wenn nicht genügend Daten vorhanden sind, dann wird die Funktion mit einem entsprechendem Fehler verlassen.
    
  66.     Return SR_NotEnoughData 'Nicht genügend Daten vorhanden.
    
  67. End If
    
  68. 'Sollten genügend Daten vorhanden sind, muss die Länge dieser Daten erfasst werden.
    
  69. 'Hierbei werden die ersten 4 Byte (welche zusammen ""ELEMENTLÄNGE sind) zurück zu einem UInteger gewandelt.
    
  70. Dim TLen As Uinteger = (RV_Data[0] Shl 24) Or (RV_Data[1] Shl 16) Or (RV_Data[2] Shl 8) Or RV_Data[3]
    
  71. 'Als nächstes muss geprüft werden, ob Genügend Daten für diese Element vorhanden sind.
    
  72. If TLen > &HFFFFFFFF Then 'Wenn die Elementlänge Größer ist, als wir dies Definiert haben dann ist womöglich ein Stream-Fehler aufgetreten
    
  73.     Return SR_DataStreamError 'Dies geben wir als Fehlermeldung zurück.
    
  74. End If
    
  75. If TLen > Len(RV_Data) - 4 Then 'Wenn weniger Daten vorhanden sind, als wir benötigen, dann wird entsprechend ein Fehler ausgegeben (wie zuvor auch)
    
  76.     Return SR_NotEnoughData 'Nicht genügend Daten vorhanden.
    
  77. End If
    
  78. 'Erst wen genügend Daten vorhanden sind, kann das Element fehlerfrei extrahiert werden
    
  79. 'Nach den LÄNGEN-Bytes kommen die Kommando-Bytes.
    
  80. R_Command = (RV_Data[4] Shl 24) Or (RV_Data[5] Shl 16) Or (RV_Data[6] Shl 8) Or RV_Data[7]
    
  81. 'Anschliessend die optionalen Daten (sofern vorhanden) Diese ergeben sich aus der Länge des Elementes - 4 (Byte Kommando)
    
  82. Dim T As String = Mid(RV_Data, 5, TLen - 4)
    
  83. 'Zuletzt muss das Element von den Restlichen Daten getrennt werden.
    
  84. RV_Data = Mid(RV_Data, TLen + 9) 'hier wird eine 9 angegeben, da die Funktion ""Mid als Startwert eine 1 und keine 0 für die Position hat. Daher muss +1 gerechnet werden
    
  85. 'Da nun ein Element erfolgreich extrahiert wurde, geben wir eien entsprechende Meldung zurück
    
  86. Return SR_NoError
    
  87. End Function
    
  88. 
    
  89. 
    
  90. 
    
  91. '####################################################################################################################################
    
  92. Sub Server_Protocol_ReadStream(V_TSNEID As Uinteger, Byref RV_Data As String)
    
  93. 'Diese Funktion empfängt einen Datenstrom, extrahiert die Elemente und führt die entsprechenden Kommandos aus.
    
  94. Dim TCMD As Protocol_Command_Enum   'Das Empfangene Kommando des Elementes
    
  95. Dim TData As String                 'Die Zugehörigen Daten
    
  96. Dim RV As Server_Return_Enum        'Eine Statusrückgabe für die Funktionsaufrufe der Kommandos
    
  97. Dim TString1 As String              'Eine Temporäre String Variable
    
  98. Dim TString2 As String              'Eine Temporäre String Variable
    
  99. Dim TClientPtr As Client_Type Ptr   'Eine Temporäre Pointer Variable auf einen Client Type
    
  100. 'Der folgende Teil wird solange ausgeführt, bis alle Elemente extrahiert wurden, oder keien weiteren Daten mehr verfügbar sind.
    
  101. Do
    
  102.     Select Case Server_Protocol_GetStream(RV_Data, TCMD, TData)
    
  103.         Case SR_NoError         'Wenn kein Fehler beim extrahieren entstand, kann mit der Auswertung des Kommandos begonnen werden.
    
  104.             'Jetzt müssen wir die Kommandos auswerten und die entsprechende Funktion hierfür aufrufen
    
  105.             Select Case TCMD
    
  106.                 Case PCE_Error_UnknownCMD       'Der Client hat unser Kommandos nicht verstanden, und schickt uns einen Fehler sammt Kommando
    
  107.                     'In TData befindet sich das Kommando, welchen diesen Fehler ezeugt hat.
    
  108.                     'Beim Client ist dies ebenfalls so geregelt, falls der Server ein Kommando schickt, das der Client nicht verstanden hat.
    
  109.                     'In beiden Fällen ist eien weitere Kommunikation nicht möglich, da fehler entstanden sind, welche die Kommunikation
    
  110.                     'fehlerhaft machen könnte. Vorsorglich beenden wir deshalb die Verbindung.
    
  111.                     TSNE_Disconnect(V_TSNEID)
    
  112.                     'Da wir nicht mehr weiter arbeiten können, verlassen wir die schleife.
    
  113.                     Exit Do
    
  114.                     
    
  115.                 Case PCE_User_Regist            'Ein Benutzer möchte sich registrieren
    
  116.                     'Hier kann jetzt eine Funktion aufgerufen werden, mit der ein Benutzer Registriert wird.
    
  117.                     'Dabei enthält TData die nötigen Informationen zur Registrierung dieses Benutzers
    
  118.                     'Als Beispiel rufen wir die BenutzerverwaltungsFunktion ""User_Regist im Modul "server_user."bi auf.
    
  119.                     
    
  120.                     RV = User_Regist(TString1, TString2)        'Diese Funktion Registriert einen neuen Benutzer.
    
  121.                     'Nach dem Aufruf erhalten wir eien Rückgabe dieser Funktion welche wir ""Native zum Clienten senden.
    
  122.                     'Diese ""Native Rückgabe ist ein Regulärer ""Server_Return_Enum Wert. Dieser kann vom Clienten ausgewertet werden.
    
  123.                     'Er gibt folglich an, ob die Registrierugn erfolgreich war, oder nicht.
    
  124.                     Server_Com_SendToUser(V_TSNEID, TCMD, Str(RV))      'Wir senden zum Benutzer das Selbe Kommando Zurück, sammt Return-Wert
    
  125.                     
    
  126.                 Case PCE_User_Login             'Ein benutzer möchte sich Anmelden
    
  127.                     'Selbes verhalten wie beim ""PCE_User_Regist.
    
  128.                     RV = User_Login(TString1, TString2)
    
  129.                     Server_Com_SendToUser(V_TSNEID, TCMD, Str(RV))          'Auch hier schicken wir wieder das Ergebniss zurück.
    
  130.                     If RV = SR_NoError Then 'Zusätzlich Werten wir selbst aus, ob der Login erfolgreich war.
    
  131.                         'Wenn dies der Fall ist, dann können wir weitere Funktionen ausführen.
    
  132.                         'Oder, Z.B. Den anderen Cienten mitteilen, das sich ein neuer Benutzer angemeldet hat, oder sonst irgend was, das danach nötig ist.
    
  133.                         'Auf jeden Fall Setzen wir hier den Gewählten Benutzername in das Typ, um ihn später identifizieren zu können.
    
  134.                         Mutexlock(G_ClientMux)                              'Das Client-Type Mutex speeren, damit wärend des Zugriffs darauf, niemand drin herum Pfuscht.
    
  135.                         TClientPtr = Server_Com_GetClientType(V_TSNEID)     'Das Client-Type zur TSNE-ID suchen.
    
  136.                         If TClientPtr = 0 Then                              'Wenn es nicht gefunden werden konnte, liegt ein schwerer Fehler vor.
    
  137.                             Mutexunlock(G_ClientMux)                        'Sperre wieder aufheben.
    
  138.                             TSNE_Disconnect(V_TSNEID)                       'Wegen eines Fehler wird die Verbindung getrennt.
    
  139.                                                                             'Wir konnten nicht das Passende ClientType zu dieser verbindung finden.
    
  140.                             'Da wir nicht mehr weiter arbeiten können, verlassen wir die schleife.
    
  141.                             Exit Do
    
  142.                         End If
    
  143.                         With *TClientPtr
    
  144.                             .V_LoginOK  = 1                                 'Den Client-Type mit ""LoginOK Markieren.
    
  145.                             .V_Username = TString1                          'Jetzt setzen wir den vom Benutzer gewählten Name in das Client-Type zu dieser Verbindung.
    
  146.                         End With
    
  147.                         Mutexunlock(G_ClientMux)                            'Sperre wieder aufheben.
    
  148.                     End If
    
  149.                     
    
  150.                 
    
  151.                 
    
  152.                 'Hier können noch viele weitere Kommandos stehen, die entsprechend unteschiedliche aufgaben erfüllen.
    
  153.                 
    
  154.                 
    
  155.                 
    
  156.                 Case Else 'Alle uns unbekannten Komandos werden mit einem Fehler beantwortet
    
  157.                     Server_Com_SendToUser(V_TSNEID, PCE_Error_UnknownCMD, Str(TCMD))    'Das Unbekannte Kommando senden wir als Klartext,
    
  158.                                                                                         'sammt Fehlermeldung an den Clienten zurück.
    
  159.                                                                                         'Dieser sollte daraufhin ebenfalls die Verbindung trennen.
    
  160.                     'Da eine weitere Kommunikation unmöglich für uns ist (wie beim Client ebenfalls). Beenden wir die Verbindung vorsorglich.
    
  161.                     TSNE_Disconnect(V_TSNEID)
    
  162.                     'Da wir nicht mehr weiter arbeiten können, verlassen wir die schleife.
    
  163.                     Exit Do
    
  164.                     
    
  165.             End Select
    
  166.             
    
  167.         Case SR_NotEnoughData   'Wenn nicht genügend weiterer Daten vorhanden sind, dann kann die Schleife verlassen werden.
    
  168.             Exit Do
    
  169.             
    
  170.         Case Else               'In allen anderen Fällen (schwerwiegende Fehler), muss die Verbindung getrennt werden. (z.B. bei SR_DataStreamError)
    
  171.             'Verbindugn trennen
    
  172.             TSNE_Disconnect(V_TSNEID)
    
  173.             'Da die Verbindugn getrennt wurde, können weitere Extraktionen entfallen.
    
  174.             Exit Do
    
  175.             
    
  176.     End Select
    
  177. Loop
    
  178. End Sub
    
  179. 
    
  180. 
    



MfG + HF
TPM