Netzwerk Server und Client (Thread basiert)
Verfasst: 01.09.2008 01:52
Da man nicht nur nehmen sondern auch geben soll, will ich meine Netzwerk-Erweiterung vorstellen.
Bisher haben Bison und ich diese getestet und keine Bugs finden können.
Es handelt sich um eine Server-Client Verbindung die zurzeit dem Senden von Strings dient.
Das ganze ist Thread basiert und die Nachrichten werden mit einem Schlüssel markiert, damit auch sicher alles ankommt, selbst wenn die Nachricht unterwegs aus mysteriösen Gründen zerteilt oder mit anderen aneinander gehangen werden sollte.
Schauts euch einfach an, ich freue mich übers Feedback.
Die Kommentierung ist primär im Client vorhanden, da beides fast identisch funktioniert.
Der Client:
Der Server:
Sieht vllt nach mehr aus, als es ist. Sollte nach kurzer Eingewöhnung nur so flutschen im Netz
Bisher haben Bison und ich diese getestet und keine Bugs finden können.
Es handelt sich um eine Server-Client Verbindung die zurzeit dem Senden von Strings dient.
Das ganze ist Thread basiert und die Nachrichten werden mit einem Schlüssel markiert, damit auch sicher alles ankommt, selbst wenn die Nachricht unterwegs aus mysteriösen Gründen zerteilt oder mit anderen aneinander gehangen werden sollte.
Schauts euch einfach an, ich freue mich übers Feedback.
Die Kommentierung ist primär im Client vorhanden, da beides fast identisch funktioniert.
Der Client:
Code: Alles auswählen
; Alles hier zur freien Verfügung, macht damit was ihr woll ;)
;
; Hierbei handelt es sich um eine mögliche Anwendung der Network-Befehle zum Versenden von Informationen.
; Das ganze ist nach folgender Idee aufgebaut: Eine Nachricht besteht aus folgender Structure:
;
; Net_ID -> Netzwerk-ID des Users
; Net_Event -> Welche Art von Event? Es gibt connect, disconnect und data
; Net_Text -> Der Inhalt der Nachricht, immer ein String (könnte aber auch für Datenbuffer angepasst werden)
; Net_Typus -> Der typus dient der Identifikation (zB. Nachricht, SpielerPositionen etc.)
; Net_Time -> Die Zeit, zu der Die Nachricht eingetroffen ist
;
; Beachtet werden muss nur: Die Erstellung, Das Verschicken und Das Auswerten von Nachrichten, dazu mehr im Quellcode
; Zusätzliches ist alles Thread basiert, welches das Senden und Empfangen unabhängig von der Hauptschleife macht.
;
; Dies ist die Lite Version, es fehlt beim Schließen das sichere Verlassen des Threads, sowie der Mutex für die IP Übergabe.
; Habe es aus Gründen der Übersicht ersteinmal weggelassen
;
;/ Diesen Test im DEBUGGER starten
;
; Da Server und Client fast identisch sind, habe ich ersteinmal nur den Client kommentiert,
; Die Unterschiede liegen nur im Detail und sind nicht relevant.
;
EnableExplicit
Enumeration ;Net_Typus deklaration (Die Arten der Nachrichten)
#typus_Nachricht_ohne_Echo
#typus_Nachricht_mit_Echo
EndEnumeration
; Die confidence_Long dient einfach nur als Hinweis einer möglichen neuen Nachricht im Buffer, kann einen belieben Wert haben
#confidence_long = 132592154
; Port und Server sollten klar sein. Vorsicht wegen dem Thread und IP.s (evtl noch Mutex setzen)
#Port = 5566
Global IP.s = "127.0.0.1"
Structure Net_Events
Net_ID.l
Net_Event.l
Net_Text.s
Net_Typus.l
Net_Time.l
EndStructure
Structure Net_Send
Net_ID.l
Net_Typus.l
Net_Text.s
EndStructure
Structure storeNET
StoreClient.l
StoreBuffer.l
StoreTime.l
EndStructure
; Diese hier müssen sein
Global NewList NetEvents.Net_Events()
Global NewList NetSend.Net_Send()
Global Mutex.l = CreateMutex()
; Diese hier kann man noch anders verpacken bei Bedarf
Global ClientThreadID.l = 0
Global ServerID.l
;- DoNetEvents kümmert sich um die Bearbeitung der Daten, einfach die gewünschten Reaktionen eintragen
Procedure DoNetEvents()
ForEach NetEvents()
Select NetEvents()\Net_Event
Case #PB_NetworkEvent_Data
Select NetEvents()\Net_Typus
Case #typus_Nachricht_ohne_Echo
Debug "Client empfängt: " + Str(NetEvents()\Net_ID) + " typus: " + Str(NetEvents()\Net_Typus) + " Zeit: " + Str(NetEvents()\Net_Time) + " text: " + NetEvents()\Net_Text
Case #typus_Nachricht_mit_Echo
;nichts tun
EndSelect
EndSelect
Next
ClearList(NetEvents())
EndProcedure
; Wird benutzt um Daten zu senden. WICHTIG: Nicht direkt benutzen, sondern über AddSend gehen !!!
Procedure SendString(ConID.l, typus.l, text.s)
Protected ByteLength.l = 4+Len(text)+1
Protected *buffer.l = AllocateMemory(ByteLength)
Protected *out.l = AllocateMemory(4+4+4+ByteLength)
PokeL(*buffer, typus)
PokeS(*buffer+4, text)
PokeL(*out, #confidence_long)
PokeL(*out+4, ByteLength)
PokeL(*out+8, CRC32Fingerprint(*buffer,ByteLength))
PokeL(*out+12, typus)
PokeS(*out+16, text)
If ConID
SendNetworkData(ConID, *out, 12+ByteLength)
EndIf
FreeMemory(*buffer)
FreeMemory(*out)
EndProcedure
;- Diese Funktion benutzen um Daten zu versenden. Diese kommen auf den Netzwerkstapel.
Procedure AddSend(ConID.l, typus.l, text.s)
AddElement(NetSend())
NetSend()\Net_ID = ConID
NetSend()\Net_Typus = typus
NetSend()\Net_Text = text
EndProcedure
; Hierbei handelt es sich um den Thread, welcher das Empfangen und senden verarbeitet.
Procedure CreateThreadClient(dummy.l)
;NetzwerkInput verarbeiten
Protected NewList StoreNetwork.storeNET()
Protected CeventID.l
Protected clientID.l
Protected NewList NetEventsStore.Net_Events()
Protected NewList NetSendStore.Net_Send()
ServerID = OpenNetworkConnection(IP.s, #Port)
If ServerID
Repeat
;/ Server Loop
CeventID = NetworkClientEvent(ServerID)
If CeventID
clientID = EventClient()
Select CeventID
Case #PB_NetworkEvent_Data
Protected *income.l = AllocateMemory(1024)
Protected *complete.l = AllocateMemory(1)
Protected Durchgang.l = 0
Protected ReceivedBytes.l = 0
Protected StoreSize.l = 0
Protected completeSize.l = 0
Repeat
ReceivedBytes = ReceiveNetworkData(ServerID, *income, 1024)
*complete = ReAllocateMemory(*complete, (1024*(Durchgang+1))-(1024-ReceivedBytes))
completeSize = (1024*(Durchgang+1))-(1024-ReceivedBytes)
If *complete
CopyMemory(*income, *complete+1024*(Durchgang), ReceivedBytes)
Durchgang+1
Else
ReceivedBytes = 0
EndIf
Until ReceivedBytes <> 1024
ForEach StoreNetwork()
If (ElapsedMilliseconds() - StoreNetwork()\StoreTime) < 500
If StoreNetwork()\StoreClient = clientID
StoreNetwork()\StoreBuffer
StoreSize = MemorySize(StoreNetwork()\StoreBuffer)
*complete = ReAllocateMemory(*complete, StoreSize+completeSize)
MoveMemory(*complete, *complete+StoreSize, completeSize)
CopyMemory(StoreNetwork()\StoreBuffer, *complete, StoreSize)
FreeMemory(StoreNetwork()\StoreBuffer)
DeleteElement(StoreNetwork())
EndIf
Else
FreeMemory(StoreNetwork()\StoreBuffer)
DeleteElement(StoreNetwork())
EndIf
Next
Protected currentByte.l = 0
Protected BreakPoint.l = 0
Protected StoreToBuffer.l = *complete
Protected In_Length.l = 0
Protected In_FingerPrint.l = 0
Protected In_Typus.l = 0
Protected In_String.s = ""
currentByte = *complete
BreakPoint = *complete + completeSize + StoreSize
Repeat
If PeekL(currentByte) = #confidence_long
In_Length = PeekL(currentByte + 4)
In_FingerPrint = PeekL(currentByte + 8)
In_Typus = PeekL(currentByte + 12)
If In_Length <= (BreakPoint - (currentByte + 12)) And In_Length > 0
If CRC32Fingerprint(currentByte + 12, In_Length) = In_FingerPrint
In_String = PeekS(currentByte + 16)
AddElement(NetEventsStore())
NetEventsStore()\Net_ID = clientID
NetEventsStore()\Net_Event = #PB_NetworkEvent_Data
NetEventsStore()\Net_Time = ElapsedMilliseconds()
NetEventsStore()\Net_Typus = In_Typus
NetEventsStore()\Net_Text = In_String
currentByte + In_Length + 12
StoreToBuffer = currentByte
Continue
EndIf
EndIf
EndIf
currentByte + 1
Until currentByte => BreakPoint
If StoreToBuffer < BreakPoint
AddElement(StoreNetwork())
StoreNetwork()\StoreClient = clientID
StoreNetwork()\StoreBuffer = AllocateMemory( (*complete + MemorySize(*complete)) - (StoreToBuffer) )
CopyMemory(*complete, StoreNetwork()\StoreBuffer, (*complete + MemorySize(*complete)) - (StoreToBuffer))
StoreNetwork()\StoreTime = ElapsedMilliseconds()
EndIf
FreeMemory(*complete)
FreeMemory(*income)
EndSelect
EndIf
;/ Daten übertragen
If TryLockMutex(Mutex)
ForEach NetEventsStore()
AddElement(NetEvents())
NetEvents()\Net_ID = NetEventsStore()\Net_ID
NetEvents()\Net_Event = NetEventsStore()\Net_Event
NetEvents()\Net_Time = NetEventsStore()\Net_Time
NetEvents()\Net_Typus = NetEventsStore()\Net_Typus
NetEvents()\Net_Text = NetEventsStore()\Net_Text
Next
ClearList(NetEventsStore())
ForEach NetSend()
AddElement(NetSendStore())
NetSendStore()\Net_ID = NetSend()\Net_ID
NetSendStore()\Net_Typus = NetSend()\Net_Typus
NetSendStore()\Net_Text = NetSend()\Net_Text
Next
ClearList(NetSend())
;/ Net Sends rausschicken
ForEach NetSendStore()
SendString(NetSendStore()\Net_ID, NetSendStore()\Net_Typus, NetSendStore()\Net_Text)
Next
ClearList(NetSendStore())
UnlockMutex(Mutex)
EndIf
Delay(1)
ForEver
EndIf
EndProcedure
;/ Los gehts...
If Not InitNetwork()
Debug "nix InitNetwork()"
End
EndIf
OpenWindow(0,0,0,100,100,"client")
ClientThreadID = CreateThread(@CreateThreadClient(),0)
Delay(500)
Debug "---Start Client---"
Define EventID.l
Define a.l
; Einfach mal 1000 Nachrichten auf den Stapel
For a=0 To 1000
AddSend(ServerID,#typus_Nachricht_mit_Echo,"hallo " + Str(a))
Next
Repeat
EventID = WindowEvent()
LockMutex(Mutex)
; Dieser Aufruf verarbeitet alle eingegangenen Nachrichten. Mutexe nicht vergessen !
DoNetEvents()
UnlockMutex(Mutex)
Delay(1)
Until EventID = #PB_Event_CloseWindow
Der Server:
Code: Alles auswählen
; Alles hier zur freien Verfügung, macht damit was ihr woll ;)
;
; Hierbei handelt es sich um eine mögliche Anwendung der Network-Befehle zum Versenden von Informationen.
; Das ganze ist nach folgender Idee aufgebaut: Eine Nachricht besteht aus folgender Structure:
;
; Net_ID -> Netzwerk-ID des Users
; Net_Event -> Welche Art von Event? Es gibt connect, disconnect und data
; Net_Text -> Der Inhalt der Nachricht, immer ein String (könnte aber auch für Datenbuffer angepasst werden)
; Net_Typus -> Der typus dient der Identifikation (zB. Nachricht, SpielerPositionen etc.)
; Net_Time -> Die Zeit, zu der Die Nachricht eingetroffen ist
;
; Beachtet werden muss nur: Die Erstellung, Das Verschicken und Das Auswerten von Nachrichten, dazu mehr im Quellcode
; Zusätzliches ist alles Thread basiert, welches das Senden und Empfangen unabhängig von der Hauptschleife macht.
;
; Dies ist die Lite Version, es fehlt beim Schließen das sichere Verlassen des Threads, sowie der Mutex für die IP Übergabe.
; Habe es aus Gründen der Übersicht ersteinmal weggelassen
;
;/ Diesen Test im DEBUGGER starten
;
; Da Server und Client fast identisch sind, habe ich ersteinmal nur den Client kommentiert,
; Die Unterschiede liegen nur im Detail und sind nicht relevant.
;
EnableExplicit
Enumeration ;Typus
#typus_Nachricht_ohne_Echo
#typus_Nachricht_mit_Echo
EndEnumeration
#confidence_long = 132592154
#Port = 5566
Structure Net_Events
Net_ID.l
Net_Event.l
Net_Text.s
Net_Typus.l
Net_Time.l
EndStructure
Structure Net_Send
Net_ID.l
Net_Typus.l
Net_Text.s
EndStructure
Structure storeNET
StoreClient.l
StoreBuffer.l
StoreTime.l
EndStructure
Global NewList NetEvents.Net_Events()
Global NewList NetSend.Net_Send()
Global Mutex.l = CreateMutex()
Global ServerThreadID.l = 0
Procedure AddSend(ConID.l, typus.l, text.s)
AddElement(NetSend())
NetSend()\Net_ID = ConID
NetSend()\Net_Typus = typus
NetSend()\Net_Text = text
EndProcedure
Procedure DoNetEvents()
ForEach NetEvents()
Select NetEvents()\Net_Event
Case #PB_NetworkEvent_Connect
Debug "Client ID: " + Str(NetEvents()\Net_ID) + " connected"
Case #PB_NetworkEvent_Disconnect
Debug "Client ID: " + Str(NetEvents()\Net_ID) + " disconnected"
Case #PB_NetworkEvent_Data
Select NetEvents()\Net_Typus
Case #typus_Nachricht_ohne_Echo
;do this
Case #typus_Nachricht_mit_Echo
Debug "Server empfängt: " + Str(NetEvents()\Net_ID) + " typus: " + Str(NetEvents()\Net_Typus) + " Zeit: " + Str(NetEvents()\Net_Time) + " text: " + NetEvents()\Net_Text
AddSend(NetEvents()\Net_ID, #typus_Nachricht_ohne_Echo, "echo " + NetEvents()\Net_Text)
EndSelect
EndSelect
Next
ClearList(NetEvents())
EndProcedure
Procedure SendString(ConID.l, typus.l, text.s)
Protected ByteLength.l = 4+Len(text)+1
Protected *buffer.l = AllocateMemory(ByteLength)
Protected *out.l = AllocateMemory(4+4+4+ByteLength)
PokeL(*buffer, typus)
PokeS(*buffer+4, text)
PokeL(*out, #confidence_long)
PokeL(*out+4, ByteLength)
PokeL(*out+8, CRC32Fingerprint(*buffer,ByteLength))
PokeL(*out+12, typus)
PokeS(*out+16, text)
If ConID
SendNetworkData(ConID, *out, 12+ByteLength)
EndIf
FreeMemory(*buffer)
FreeMemory(*out)
EndProcedure
Procedure CreateThreadServer(dummy.l)
;NetzwerkInput verarbeiten
Protected NewList StoreNetwork.storeNET()
Protected SeventID.l
Protected clientID.l
Protected ServerID.l
Protected NewList NetEventsStore.Net_Events()
Protected NewList NetSendStore.Net_Send()
ServerID = CreateNetworkServer(#PB_Any, #Port, #PB_Network_TCP)
If ServerID
Repeat
;/ Server Loop
SeventID = NetworkServerEvent()
If SeventID
clientID = EventClient()
Select SeventID
Case #PB_NetworkEvent_Connect
AddElement(NetEventsStore())
NetEventsStore()\Net_ID = clientID
NetEventsStore()\Net_Event = #PB_NetworkEvent_Connect
NetEventsStore()\Net_Time = ElapsedMilliseconds()
NetEventsStore()\Net_Typus = 0
NetEventsStore()\Net_Text = ""
Case #PB_NetworkEvent_Disconnect
AddElement(NetEventsStore())
NetEventsStore()\Net_ID = clientID
NetEventsStore()\Net_Event = #PB_NetworkEvent_Disconnect
NetEventsStore()\Net_Time = ElapsedMilliseconds()
NetEventsStore()\Net_Typus = 0
NetEventsStore()\Net_Text = ""
Case #PB_NetworkEvent_Data
Protected *income.l = AllocateMemory(1024)
Protected *complete.l = AllocateMemory(1)
Protected Durchgang.l = 0
Protected ReceivedBytes.l = 0
Protected StoreSize.l = 0
Protected completeSize.l = 0
Repeat
ReceivedBytes = ReceiveNetworkData(clientID, *income, 1024)
*complete = ReAllocateMemory(*complete, (1024*(Durchgang+1))-(1024-ReceivedBytes))
completeSize = (1024*(Durchgang+1))-(1024-ReceivedBytes)
If *complete
CopyMemory(*income, *complete+1024*(Durchgang), ReceivedBytes)
Durchgang+1
Else
ReceivedBytes = 0
EndIf
Until ReceivedBytes <> 1024
ForEach StoreNetwork()
If (ElapsedMilliseconds() - StoreNetwork()\StoreTime) < 500
If StoreNetwork()\StoreClient = clientID
StoreNetwork()\StoreBuffer
StoreSize = MemorySize(StoreNetwork()\StoreBuffer)
*complete = ReAllocateMemory(*complete, StoreSize+completeSize)
MoveMemory(*complete, *complete+StoreSize, completeSize)
CopyMemory(StoreNetwork()\StoreBuffer, *complete, StoreSize)
FreeMemory(StoreNetwork()\StoreBuffer)
DeleteElement(StoreNetwork())
EndIf
Else
FreeMemory(StoreNetwork()\StoreBuffer)
DeleteElement(StoreNetwork())
EndIf
Next
Protected currentByte.l = 0
Protected BreakPoint.l = 0
Protected StoreToBuffer.l = *complete
Protected In_Length.l = 0
Protected In_FingerPrint.l = 0
Protected In_Typus.l = 0
Protected In_String.s = ""
currentByte = *complete
BreakPoint = *complete + completeSize + StoreSize
Repeat
If PeekL(currentByte) = #confidence_long
In_Length = PeekL(currentByte + 4)
In_FingerPrint = PeekL(currentByte + 8)
In_Typus = PeekL(currentByte + 12)
If In_Length <= (BreakPoint - (currentByte + 12)) And In_Length > 0
If CRC32Fingerprint(currentByte + 12, In_Length) = In_FingerPrint
In_String = PeekS(currentByte + 16)
AddElement(NetEventsStore())
NetEventsStore()\Net_ID = clientID
NetEventsStore()\Net_Event = #PB_NetworkEvent_Data
NetEventsStore()\Net_Time = ElapsedMilliseconds()
NetEventsStore()\Net_Typus = In_Typus
NetEventsStore()\Net_Text = In_String
currentByte + In_Length + 12
StoreToBuffer = currentByte
Continue
EndIf
EndIf
EndIf
currentByte + 1
Until currentByte => BreakPoint
If StoreToBuffer < BreakPoint
AddElement(StoreNetwork())
StoreNetwork()\StoreClient = clientID
StoreNetwork()\StoreBuffer = AllocateMemory( (*complete + MemorySize(*complete)) - (StoreToBuffer) )
CopyMemory(*complete, StoreNetwork()\StoreBuffer, (*complete + MemorySize(*complete)) - (StoreToBuffer))
StoreNetwork()\StoreTime = ElapsedMilliseconds()
EndIf
FreeMemory(*complete)
FreeMemory(*income)
EndSelect
EndIf
;/ Daten übertragen
If TryLockMutex(Mutex)
ForEach NetEventsStore()
AddElement(NetEvents())
NetEvents()\Net_ID = NetEventsStore()\Net_ID
NetEvents()\Net_Event = NetEventsStore()\Net_Event
NetEvents()\Net_Time = NetEventsStore()\Net_Time
NetEvents()\Net_Typus = NetEventsStore()\Net_Typus
NetEvents()\Net_Text = NetEventsStore()\Net_Text
Next
ClearList(NetEventsStore())
ForEach NetSend()
AddElement(NetSendStore())
NetSendStore()\Net_ID = NetSend()\Net_ID
NetSendStore()\Net_Typus = NetSend()\Net_Typus
NetSendStore()\Net_Text = NetSend()\Net_Text
Next
ClearList(NetSend())
;/ Net Sends rausschicken
ForEach NetSendStore()
SendString(NetSendStore()\Net_ID, NetSendStore()\Net_Typus, NetSendStore()\Net_Text)
Next
ClearList(NetSendStore())
UnlockMutex(Mutex)
EndIf
Delay(1)
ForEver
EndIf
EndProcedure
;/ Los gehts...
If Not InitNetwork()
Debug "nix InitNetwork()"
End
EndIf
OpenWindow(0,0,0,100,100,"server")
ServerThreadID = CreateThread(@CreateThreadServer(),0)
Delay(500)
Debug "---Start Server---"
Define EventID.l
Repeat
EventID = WindowEvent()
LockMutex(Mutex)
;/ Alle Net Events auswerten
DoNetEvents()
UnlockMutex(Mutex)
Delay(1)
Until EventID = #PB_Event_CloseWindow
Sieht vllt nach mehr aus, als es ist. Sollte nach kurzer Eingewöhnung nur so flutschen im Netz
