Test Server und Client v1.16
Server
Code: Alles auswählen
;-TOP
CompilerIf #PB_Compiler_Thread = 0
CompilerError "Use Compileroption Threadsafe!"
CompilerEndIf
EnableExplicit
IncludeFile "NetworkFunctions.pb"
UseModule NetworkTCP
Enumeration Windows
#WinMain
EndEnumeration
Enumeration #PB_Event_FirstCustomValue
#My_Event_Logging
EndEnumeration
; -----------------------------------------------------------------------------------
Structure udtClientData
ConnectionID.i
Date.i
Login.i
Name.s
Text.s
EndStructure
Structure udtServerData
*ThreadID
*ServerID
ExitServer.i
Map Client.udtClientData()
EndStructure
Global ExitApplication
Global ServerData.udtServerData
Global ServerMutex = CreateMutex()
; -----------------------------------------------------------------------------
;-Testdaten
Global *Random1 = AllocateMemory(200000)
Global *Random2 = AllocateMemory(2000000)
RandomSeed(1000)
RandomData(*Random1, 200000)
RandomSeed(2000)
RandomData(*Random2, 2000000)
;ShowMemoryViewer(*Random1, 64)
Enumeration ProtocolID 1
#ProtocolString
#ProtocolRandom1
#ProtocolRandom2
EndEnumeration
; -----------------------------------------------------------------------------
; Bei Linux und MacOS kann man Gadgets nicht aus Threads ändern. Daher werden die Texte über PostEvent zur Liste gesendet.
Procedure AllocateString(Text.s)
Protected *mem.String
*mem = AllocateStructure(String)
*mem\s = Text
ProcedureReturn *mem
EndProcedure
Procedure.s FreeString(*Text.String)
Protected result.s
If *Text
result = *text\s ;PeekS(*Text)
FreeStructure(*Text)
EndIf
ProcedureReturn result
EndProcedure
Procedure thLogging(Text.s)
PostEvent(#My_Event_Logging, 0, 0, 0, AllocateString(Text))
EndProcedure
Procedure Logging(Text.s)
Protected rows
AddGadgetItem(0, -1, Text)
rows = CountGadgetItems(0)
SetGadgetState(0, rows - 1)
SetGadgetState(0, -1)
EndProcedure
; -----------------------------------------------------------------------------
; Dies ist der Server-Dienst der die Daten im Hintergrund verarbeitet
Procedure ThreadServer(*ServerData.udtServerData)
Protected Event, ConnectionID, keyConnectionID.s, count, Text.s, Name.s, ok, time
Protected NewList ClosedConnectionID()
Protected NewList ListConnectionID()
Protected ndr.sReceiveData
With *ServerData
CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
Protected StopNap = BeginWork(#NSActivityLatencyCritical | #NSActivityUserInitiated, Hex(*ServerData))
CompilerEndIf
time = ElapsedMilliseconds()
Repeat
LockMutex(ServerMutex)
Event = NetworkServerEvent(\ServerID)
If Event
ConnectionID = EventClient()
keyConnectionID = Hex(ConnectionID)
EndIf
UnlockMutex(ServerMutex)
Select Event
Case #PB_NetworkEvent_Connect
; Daten für Client anlegen
TcpNewClientData(ConnectionID, \ServerID)
thLogging("Network: Client Connected: ID " + keyConnectionID)
Case #PB_NetworkEvent_Data
Select TcpReceiveData(ConnectionID)
Case #TcpReceiveBusy
; Daten noch nicht vollständig
Case #TcpReceiveDone
TcpGetReceiveData(ConnectionID, ndr)
Select ndr\ProtocolID
Case #ProtocolString
Text = TcpGetString(ConnectionID)
TcpGetClientList(\ServerID, ListConnectionID())
If LCase(Text) = "#get"
ForEach ListConnectionID()
TcpSendString(ndr\ConnectionID, "Client ID " + Hex(ListConnectionID()), #PB_Unicode, #TcpSendAsynchron, ndr\TransactionID, #ProtocolString)
Next
Else
ForEach ListConnectionID()
TcpSendString(ListConnectionID(), "Text: " + Text, #PB_Unicode, #TcpSendASynchron, ndr\TransactionID, #ProtocolString)
Next
EndIf
Case #ProtocolRandom1
If CompareMemory(ndr\Data, *Random1, ndr\DataLen)
Text = "Random 1 Ok"
Else
Text = "Random 1 Error"
EndIf
FreeMemory(ndr\Data)
TcpSendString(ndr\ConnectionID, Text, #PB_Unicode, #TcpSendAsynchron, ndr\TransactionID, #ProtocolString)
Case #ProtocolRandom2
If CompareMemory(ndr\Data, *Random2, ndr\DataLen)
Text = "Random 2 Ok"
Else
Text = "Random 2 Error"
EndIf
FreeMemory(ndr\Data)
TcpSendString(ndr\ConnectionID, Text, #PB_Unicode, #TcpSendAsynchron, ndr\TransactionID, #ProtocolString)
EndSelect
Case #TcpReceiveEmpty
; Nur Header empfangen
Case #TcpReceiveCancel
; Abbruch empfangen
thLogging("Network Cancel Data: Client ConnectionID " + keyConnectionID + " TransactionID " + ndr\TransactionID)
Case #TcpReceiveError
; Im Fehlerfall Client entfernen
thLogging("Network Error: Client ConnectionID " + keyConnectionID + " Errorcode " + Str(TcpGetReceiveError(ConnectionID)))
CloseNetworkConnection(ConnectionID)
TcpFreeClientData(ConnectionID, \ServerID)
EndSelect
Case #PB_NetworkEvent_Disconnect
TcpFreeClientData(ConnectionID)
; Daten von Client entfernen
thLogging("Network: Client Disconnected: ID " + keyConnectionID)
Default
; Alle 5 Sekunden nach verlorende Clients suchen und Resoursen freigeben
If ElapsedMilliseconds() - time > 5000 ; ms
time = ElapsedMilliseconds()
TcpCheckClientData(\ServerID, ClosedConnectionID())
ForEach ClosedConnectionID()
thLogging("Network: Client Timeout: ID " + ClosedConnectionID())
Next
EndIf
Delay(10)
EndSelect
Until \ExitServer
Delay(500)
; Alle Client Daten freigeben
TcpFreeClientData(0, \ServerID)
; Server beenden, Daten bereinigen und Thread verlassen
CloseNetworkServer(\ServerID)
\ThreadID = 0
\ServerID = 0
\ExitServer = 0
CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
EndWork(StopNap)
CompilerEndIf
EndWith
EndProcedure
; -----------------------------------------------------------------------------
; Hier wird der Server angelegt und beim erfolg der Thread gestartet der die Server-Dienste ausführt
Procedure InitServer(*ServerData.udtServerData, Port, BindedIP.s = "")
Protected ServerID
With *ServerData
ServerID = CreateNetworkServer(#PB_Any, Port, #PB_Network_TCP, BindedIP)
If ServerID
\ServerID = ServerID
\ThreadID = CreateThread(@ThreadServer(), *ServerData)
Logging("Network: Init Server: ID " + Hex(ServerID))
Else
Logging("Network: Error Init Network Server")
EndIf
ProcedureReturn ServerID
EndWith
EnableDebugger
EndProcedure
; -----------------------------------------------------------------------------
; Hier wird das Beenden des Servers angestossen
; Sollte diese nicht erfolgreich sein, wird der Server und der Thread zwangsweise geschlossen
Procedure CloseServer(*ServerData.udtServerData)
Protected timeout
With *ServerData
If \ServerID = 0
ProcedureReturn 0
EndIf
Logging("Network: Close Network Server: ID " + \ServerID)
\ExitServer = 1
Repeat
If \ExitServer = 0
Break
Else
timeout + 100
If timeout > 10000
CloseNetworkServer(\ServerID)
KillThread(\ThreadID)
\ThreadID = 0
\ServerID = 0
\ExitServer = 0
ClearMap(\Client())
Logging("Network: Error - Kill Network Server: ID " + \ServerID)
Break
EndIf
EndIf
Delay(100)
ForEver
EndWith
EndProcedure
; -----------------------------------------------------------------------------
Procedure Main()
Protected Event, rows
If OpenWindow(#WinMain, #PB_Ignore, #PB_Ignore, 600, 400, "Test-Server",#PB_Window_SystemMenu)
CreateStatusBar(0, WindowID(#WinMain))
AddStatusBarField(100)
AddStatusBarField(#PB_Ignore)
ListViewGadget(0, 0, 0, WindowWidth(0), WindowHeight(0) - StatusBarHeight(0))
; Init Server
InitServer(ServerData, 6037)
; LOOP
Repeat
Event = WaitWindowEvent()
Select Event
Case #PB_Event_CloseWindow
Select EventWindow()
Case #WinMain
CloseServer(ServerData)
ExitApplication = #True
EndSelect
Case #My_Event_Logging
Logging(FreeString(EventData()))
EndSelect
Until ExitApplication And ServerData\ExitServer = 0
EndIf
EndProcedure
InitNetwork()
Main()
[/size]
Client
Code: Alles auswählen
;-
EnableExplicit
CompilerIf #PB_Compiler_Thread = 0
CompilerError "Use Compileroption Threadsafe!"
CompilerEndIf
EnableExplicit
IncludeFile "NetworkFunctions.pb"
UseModule NetworkTCP
Enumeration Windows
#WinMain
EndEnumeration
Enumeration MenuItems
#MenuItem_Send
#MenuItem_Connect
#MenuItem_Disconnect
EndEnumeration
Enumeration #PB_Event_FirstCustomValue
#My_Event_Logging
#My_Event_Statusbar
EndEnumeration
; -----------------------------------------------------------------------------
Structure udtMyClientData
*ThreadID
*ConnectionID
ExitClient.i
EndStructure
; -----------------------------------------------------------------------------
;-Testdaten
Global *Random1 = AllocateMemory(200000)
Global *Random2 = AllocateMemory(2000000)
RandomSeed(1000)
RandomData(*Random1, 200000)
RandomSeed(2000)
RandomData(*Random2, 2000000)
Enumeration ProtocolID 1
#ProtocolString
#ProtocolRandom1
#ProtocolRandom2
EndEnumeration
; -----------------------------------------------------------------------------
; Bei Linux und MacOS kann man Gadgets nicht aus Threads ändern. Daher werden die Texte über PostEvent zur Liste gesendet.
Procedure AllocateString(Text.s)
Protected *mem.String
*mem = AllocateStructure(String)
*mem\s = Text
ProcedureReturn *mem
EndProcedure
Procedure.s FreeString(*Text.String)
Protected result.s
If *Text
result = *text\s
FreeStructure(*Text)
EndIf
ProcedureReturn result
EndProcedure
; Logging aus Threads
Procedure thLogging(Text.s)
PostEvent(#My_Event_Logging, 0, 0, 0, AllocateString(Text))
EndProcedure
; Logging aus Mainscope
Procedure Logging(Text.s)
Protected rows
AddGadgetItem(0, -1, Text)
rows = CountGadgetItems(0)
SetGadgetState(0, rows - 1)
SetGadgetState(0, -1)
EndProcedure
; -----------------------------------------------------------------------------
; Statusbar aus Threads
Procedure thStatusBarText(StatusBar, Field, Text.s)
PostEvent(#My_Event_Statusbar, 0, StatusBar, Field, AllocateString(Text))
EndProcedure
; -----------------------------------------------------------------------------
; Dies ist der Client-Dienst der die Daten im Hintergrund verarbeitet
Procedure ThreadClient(*MyClientData.udtMyClientData)
Protected Event, count, Text.s, Size, time1, time2
Static Error
With *MyClientData
If Not TcpNewClientData(\ConnectionID, 0)
ProcedureReturn 0
EndIf
Repeat
Event = NetworkClientEvent(\ConnectionID)
Select Event
Case #PB_NetworkEvent_Data
If TcpReceiveData(\ConnectionID) = #TcpReceiveDone
Select TcpGetProtocolID(\ConnectionID)
Case #ProtocolString
Text = TcpGetString(\ConnectionID)
thLogging(Text)
If Right(Text,5) = "Error"
Error + 1
thStatusBarText(0, 0, "Error = " + Error)
EndIf
EndSelect
EndIf
Case #PB_NetworkEvent_Disconnect
; Server hat die Verbindung beendet
\ExitClient = 1
thStatusBarText(0, 0, "Disconnect from Server")
Default
Delay(20)
If ElapsedMilliseconds() - time1 > 5000
time1 = ElapsedMilliseconds()
If TcpGetSendDataSize(\ConnectionID) < 5
If Random(1)
Size = Random(200000, 100000)
TcpSendData(\ConnectionID, *Random1, Size, #TcpSendASynchron, 0, #ProtocolRandom1)
Else
Size = Random(2000000, 100000)
TcpSendData(\ConnectionID, *Random2, Size, #TcpSendAsynchron, 0, #ProtocolRandom2)
EndIf
EndIf
EndIf
EndSelect
Until \ExitClient
; Exit Thread
TcpFreeClientData(\ConnectionID)
CloseNetworkConnection(\ConnectionID)
\ThreadID = 0
\ConnectionID = 0
\ExitClient = 0
EndWith
EndProcedure
; -----------------------------------------------------------------------------
; Hier wird die Verbindung zum Server angelegt und beim erfolg der Thread gestartet der die Client-Dienste ausführt
Procedure SendDataCB(Status, TransactionID, DataOffset, DataLen)
Select Status
Case #TcpSendBusy
thStatusBarText(0, 1, "Send Busy")
thStatusBarText(0, 2, Str(TransactionID))
thStatusBarText(0, 3, Str(DataOffset) + "/" + Str(DataLen))
If DataOffset > 175000
;ProcedureReturn #True
EndIf
Case #TcpSendDone
thStatusBarText(0, 1, "Send Done")
thStatusBarText(0, 2, Str(TransactionID))
thStatusBarText(0, 3, Str(DataOffset) + "/" + Str(DataLen))
Case #TcpSendCancel
thStatusBarText(0, 1, "Send Cancel")
thStatusBarText(0, 2, Str(TransactionID))
thStatusBarText(0, 3, Str(DataOffset) + "/" + Str(DataLen))
Case #TcpSendError
thStatusBarText(0, 1, "Send Error")
thStatusBarText(0, 2, Str(TransactionID))
thStatusBarText(0, 3, Str(DataOffset) + "/" + Str(DataLen))
EndSelect
ProcedureReturn #False
EndProcedure
; -----------------------------------------------------------------------------
Procedure InitClient(*MyClientData.udtMyClientData, IP.s, Port, Timeout = 0)
Protected ConnectionID
With *MyClientData
If \ConnectionID
ProcedureReturn \ConnectionID
EndIf
ConnectionID = OpenNetworkConnection(IP, Port, #PB_Network_TCP, Timeout)
If ConnectionID
\ConnectionID = ConnectionID
\ThreadID = CreateThread(@ThreadClient(), *MyClientData)
If \ThreadID
Logging("Network: Init Client: ID " + Hex(ConnectionID))
StatusBarText(0, 0, "Connect")
TcpSetSendDataCallback(ConnectionID, @SendDataCB())
SetActiveGadget(1)
Else
Logging("Network: Error Init Thread")
EndIf
Else
Logging("Network: Error Init Connection")
StatusBarText(0, 0, "Error")
EndIf
ProcedureReturn ConnectionID
EndWith
EndProcedure
; -----------------------------------------------------------------------------
; Hier wird das Beenden der Verbindung zu Server angestossen
; Sollte diese nicht erfolgreich sein, wird die Verbindung und der Thread zwangsweise geschlossen
Procedure CloseClient(*MyClientData.udtMyClientData)
Protected timeout
With *MyClientData
If \ConnectionID = 0
ProcedureReturn 0
EndIf
Logging("Network: Close Network Connection: ID " + Hex(\ConnectionID))
\ExitClient = 1
Repeat
If \ExitClient = 0
Break
Else
timeout + 100
If timeout > 10000
CloseNetworkConnection(\ConnectionID)
KillThread(\ThreadID)
\ThreadID = 0
\ConnectionID = 0
\ExitClient = 0
Logging("Network: Error - Kill Network Connection: ID " + Hex(\ConnectionID))
Break
EndIf
EndIf
Delay(100)
ForEver
EndWith
EndProcedure
; -----------------------------------------------------------------------------
;- Main
Global ExitApplication
Global MyClientData.udtMyClientData
Global Host.s = "127.0.0.1"
;Global Host.s = "192.168.170.40"
Global Port = 6037
Procedure Main()
Protected Event, rows, text.s
If OpenWindow(#WinMain, #PB_Ignore, #PB_Ignore, 600, 400, "Test-Client",#PB_Window_SystemMenu)
CreateStatusBar(0, WindowID(#WinMain))
AddStatusBarField(160)
AddStatusBarField(100)
AddStatusBarField(100)
AddStatusBarField(#PB_Ignore)
CreateMenu(0, WindowID(#WinMain))
MenuTitle("Network")
MenuItem(#MenuItem_Connect, "Connect")
MenuItem(#MenuItem_Disconnect, "Disconnect")
ListViewGadget(0, 0, 0, WindowWidth(0), WindowHeight(0) - StatusBarHeight(0) - MenuHeight() - 35)
StringGadget(1, 5, GadgetHeight(0) + 5, WindowWidth(0) - 10, 25, "")
AddKeyboardShortcut(#WinMain, #PB_Shortcut_Return, #MenuItem_Send)
; LOOP
Repeat
Event = WaitWindowEvent()
Select Event
Case #PB_Event_Menu
Select EventMenu()
Case #MenuItem_Connect
InitClient(MyClientData, Host, Port)
Case #MenuItem_Disconnect
CloseClient(MyClientData)
Case #MenuItem_Send
If GetActiveGadget() = 1 And MyClientData\ConnectionID
text = GetGadgetText(1)
If 1; text > ""
TcpSendString(MyClientData\ConnectionID, text, #PB_Unicode, #TcpSendSynchron,0, #ProtocolString)
SetGadgetText(1, "")
EndIf
EndIf
EndSelect
Case #PB_Event_CloseWindow
Select EventWindow()
Case #WinMain
CloseClient(MyClientData)
ExitApplication = #True
EndSelect
Case #My_Event_Logging
Logging(FreeString(EventData()))
Case #My_Event_Statusbar
StatusBarText(EventGadget(), EventType(), FreeString(EventData()))
EndSelect
Until ExitApplication And MyClientData\ExitClient = 0
EndIf
EndProcedure
InitNetwork()
Main()
[/size]