Page 1 of 1
New networking command
Posted: Sat Apr 21, 2012 4:21 am
by RichAlgeni
It appears that EventClient() is incremented to the next client connection event (if available) only after ReceiveNetworkData() is called. The complication with this comes into play when using threads with a loop. It's possible to loop back to interrogate EventClient() again before a thread calls ReceiveNetworkData(). This leads EventClient() to return the same value as it was in the previous iteration. This situation makes it look like multiple connection events occurred, when there was in fact only one.
Would it be possible to create a new network command such as 'NextNetworkServerEvent()', which would increment NetworkServerEvent() to the next server event in the queue? This would, in turn, also increment EventClient().
For instance:
Code: Select all
Repeat
NSEvent = NetworkServerEvent() ; if we receive data, it will be indicated here
Select NSEvent
Case #PB_NetworkEvent_Connect ; a socket has connected
Case #PB_NetworkEvent_Data ; raw data has been received
thisClient = EventClient() ; get the event client identifier
NextNetworkServerEvent() ; new command to increment to next server event, comes after EventClient() interrogated
CreateThread(@ProcessRequest(), thisClient); thread to process incoming data
Case #PB_NetworkEvent_Disconnect; a socket disconnected
Default; if no event data has been received, sleep so we don't hammer the processor
Delay(250)
EndSelect
ForEver
Re: New networking command
Posted: Sat Apr 21, 2012 11:44 am
by STARGÅTE
I don't understand your problem.
EventClient() can be used if NetworkServerEvent() is called and then passed to the thread.
Code: Select all
InitNetwork()
Procedure Test(Client)
Protected String.s{256}
If ReceiveNetworkData(Client, @String, 255) > 0
Debug String
EndIf
EndProcedure
CreateNetworkServer(#PB_Any, 7000)
Define N, Connection
For N = 1 To 10
Connection = OpenNetworkConnection("127.0.0.1", 7000)
SendNetworkString(Connection, "Hallo from Client "+Str(N))
Next
Repeat
Select NetworkServerEvent()
Case #PB_NetworkEvent_Data
CreateThread(@Test(), EventClient())
Default
Delay(5)
EndSelect
ForEver
Hallo from Client 10
Hallo from Client 9
Hallo from Client 8
Hallo from Client 7
Hallo from Client 6
Hallo from Client 5
Hallo from Client 4
Hallo from Client 3
Hallo from Client 2
Hallo from Client 1
Can you post a code that shows your problem?
Re: New networking command
Posted: Sat Apr 21, 2012 8:50 pm
by RichAlgeni
Here is the server process:
Code: Select all
EnableExplicit
Procedure ProcessRequest(clientNumber)
Protected *memoryLoc = AllocateMemory(10000)
Protected result
Protected TextToSend.s = "ok"
result = ReceiveNetworkData(clientNumber, *memoryLoc, 10000)
FreeMemory(*memoryLoc)
Delay(5); delay as if reading data from a file
SendNetworkData(clientNumber, @TextToSend, Len(TextToSend))
PrintN("Client " + Str(clientNumber) + " received " + Str(result) + " bytes")
EndProcedure
Define NSEvent
Define thisClient
Define KeyPressed.s
InitNetwork()
CreateNetworkServer(#PB_Any, 12000)
OpenConsole()
PrintN("Started Network Server on port 12000")
PrintN("Press Escape to exit")
PrintN("")
Repeat
NSEvent = NetworkServerEvent() ; if we receive data, it will be indicated here
Select NSEvent
Case #PB_NetworkEvent_Connect ; a socket has connected
Case #PB_NetworkEvent_Data ; raw data has been received
thisClient = EventClient() ; get the event client identifier
CreateThread(@ProcessRequest(), thisClient); thread to process incoming data
Case #PB_NetworkEvent_File ; uploading a file
Case #PB_NetworkEvent_Disconnect; a socket disconnected
Default; if no event data has been received, sleep so we don't hammer the processor
Delay(50)
EndSelect
KeyPressed = Inkey()
Until KeyPressed = #ESC$
PrintN("Escape pressed, press <enter> to terminate this process")
Input()
End
; IDE Options = PureBasic 4.51 (Windows - x64)
; CursorPosition = 10
; Folding = -
; EnableThread
; Executable = test_server.exe
; CurrentDirectory = D:\dev\PureBasic\temp\
; CompileSourceDirectory
Here is the client process:
Code: Select all
EnableExplicit
Procedure netSend(counter)
Protected connectNumber.i
Protected TextToSend.s
Protected result.i
Protected *MemoryID
connectNumber = OpenNetworkConnection("localhost", 12000)
If connectNumber
TextToSend = "This is counter # " + RSet(Str(counter), 1000, " ")
result = SendNetworkData(connectNumber, @TextToSend, Len(TextToSend))
If result > 0
*MemoryID = AllocateMemory(100)
If *MemoryID
result = ReceiveNetworkData(connectNumber, *MemoryID, 100)
FreeMemory(*MemoryID)
EndIf
Else
PrintN("Could not write to the socket, result = " + Str(result))
EndIf
CloseNetworkConnection(connectNumber)
Else
PrintN("Could not open socket to localhost on port 12000")
EndIf
EndProcedure
Define Thread.i
Define counter.f = 0
Define loopNdx.i
Define loopCT.i = 50
Define startTime.i = ElapsedMilliseconds()
InitNetwork()
OpenConsole()
PrintN("Starting " + Str(loopCT) + " loops")
For loopNdx=1 To loopCT
counter = counter + 1
Thread = CreateThread(@netSend(), counter)
If counter/10 = Int(counter/10)
PrintN(Str(counter) + " send attempts processed")
EndIf
Delay(50); sleep 1/20th of a second here
Next loopNdx
PrintN("Loop went " + Str(loopNdx - 1) + " times")
PrintN("Process took " + Str((ElapsedMilliseconds() - startTime) / 1000) + " seconds")
Input()
End
; IDE Options = PureBasic 4.51 (Windows - x64)
; CursorPosition = 12
; Folding = -
; EnableThread
; Executable = test_client.exe
; HideErrorLog
; CurrentDirectory = D:\dev\PureBasic\temp\
; CompileSourceDirectory
; EnablePurifier
Here is the client output:
Code: Select all
Starting 50 loops
10 send attempts processed
20 send attempts processed
30 send attempts processed
40 send attempts processed
50 send attempts processed
Loop went 50 times
Process took 2 seconds
Here is the Server output, note there should only be 50 lines, for 50 client connection attempts:
Code: Select all
Started Network Server on port 12000
Press Escape to exit
Client 8796063864560 received -1 bytes
Client 8796063864560 received 1018 bytes
Client 8796063864560 received -1 bytes
Client 8796063866080 received 1018 bytes
Client 8796063866080 received -1 bytes
Client 8796063866016 received -1 bytes
Client 8796063866016 received 1018 bytes
Client 8796063865968 received 1018 bytes
Client 8796063865968 received -1 bytes
Client 8796063865968 received -1 bytes
Client 8796063866016 received -1 bytes
Client 8796063866016 received 1018 bytes
Client 8796063865936 received 1018 bytes
Client 8796063865936 received -1 bytes
Client 8796063866128 received 1018 bytes
Client 8796063866128 received -1 bytes
Client 8796063866080 received 1018 bytes
Client 8796063866080 received -1 bytes
Client 8796063866128 received 1018 bytes
Client 8796063866128 received -1 bytes
Client 8796063865936 received -1 bytes
Client 8796063865936 received 1018 bytes
Client 8796063866032 received 1018 bytes
Client 8796063866032 received -1 bytes
Client 8796063865936 received -1 bytes
Client 8796063865936 received 1018 bytes
Client 8796063866032 received -1 bytes
Client 8796063866032 received 1018 bytes
Client 8796063865936 received 1018 bytes
Client 8796063865936 received -1 bytes
Client 8796063866032 received 1018 bytes
Client 8796063866032 received -1 bytes
Client 8796063866032 received -1 bytes
Client 8796063865936 received 1018 bytes
Client 8796063865936 received -1 bytes
Client 8796063866032 received 1018 bytes
Client 8796063866032 received -1 bytes
Client 8796063865936 received -1 bytes
Client 8796063865936 received 1018 bytes
Client 8796063866032 received -1 bytes
Client 8796063866032 received 1018 bytes
Client 8796063865936 received 1018 bytes
Client 8796063865936 received -1 bytes
Client 8796063866032 received 1018 bytes
Client 8796063866032 received -1 bytes
Client 8796063865936 received 1018 bytes
Client 8796063865936 received -1 bytes
Client 8796063866032 received -1 bytes
Client 8796063866032 received 1018 bytes
Client 8796063866032 received -1 bytes
Client 8796063865936 received -1 bytes
Client 8796063865936 received -1 bytes
Client 8796063865936 received 1018 bytes
Client 8796063866032 received -1 bytes
Client 8796063866032 received 1018 bytes
Client 8796063865936 received -1 bytes
Client 8796063865936 received 1018 bytes
Client 8796063866032 received 1018 bytes
Client 8796063866032 received -1 bytes
Client 8796063865936 received 1018 bytes
Client 8796063865936 received -1 bytes
Client 8796063866032 received 1018 bytes
Client 8796063866032 received -1 bytes
Client 8796063865936 received 1018 bytes
Client 8796063865936 received -1 bytes
Client 8796063866032 received 1018 bytes
Client 8796063866032 received -1 bytes
Client 8796063865936 received -1 bytes
Client 8796063865936 received 1018 bytes
Client 8796063866032 received 1018 bytes
Client 8796063866032 received -1 bytes
Client 8796063865936 received 1018 bytes
Client 8796063865936 received -1 bytes
Client 8796063865936 received -1 bytes
Client 8796063866032 received 1018 bytes
Client 8796063866032 received -1 bytes
Client 8796063865936 received -1 bytes
Client 8796063865936 received 1018 bytes
Client 8796063866032 received -1 bytes
Client 8796063866032 received 1018 bytes
Client 8796063865936 received 1018 bytes
Client 8796063865936 received -1 bytes
Client 8796063882752 received 1018 bytes
Client 8796063882752 received -1 bytes
Client 8796063865936 received 1018 bytes
Client 8796063865936 received -1 bytes
Client 8796063866032 received -1 bytes
Client 8796063866032 received 1018 bytes
Client 8796063865936 received -1 bytes
Client 8796063865936 received 1018 bytes
Client 8796063866032 received 1018 bytes
Client 8796063866032 received -1 bytes
Client 8796063865936 received 1018 bytes
Client 8796063865936 received -1 bytes
Client 8796063866032 received 1018 bytes
Client 8796063866032 received -1 bytes
Client 8796063865936 received 1018 bytes
Client 8796063865936 received -1 bytes
Client 8796063866032 received 1018 bytes
Client 8796063866032 received -1 bytes
Client 8796063865936 received 1018 bytes
Client 8796063865936 received -1 bytes
Client 8796063866032 received -1 bytes
Client 8796063866032 received 1018 bytes
Client 8796063865936 received 1018 bytes
Client 8796063865936 received -1 bytes
Escape pressed, press <enter> to terminate this process
There are actually 106 lines where the server thought there was a new client connection, when there really wasn't. Please also note the client number. It appears that EventClient() is not reset until data is read from a socket, at least that's what I theorize.
Re: New networking command
Posted: Sat Apr 21, 2012 9:24 pm
by STARGÅTE
The result is perfectly correct and logical.
The Output have only 50 Client-Numbers.
EventClient() get the ClientID from the result of NetworkServerEvent().
If NetworkServerEvent() return #PB_NetworkEvent_Data you create a Thread and continue the loop.
If the Thread not execute ReceiveNetworkData() before come the next NetworkServerEvent() you create a second with the same task and same ClientID.
So you have two or three Threads with the same ClientID and same task.
So if the first Thread execute ReceiveNetworkData() all other threads give the result -1 (see your output).
You must protect it like this:
Code: Select all
thisClient = EventClient() ; get the event client identifier
If Not IsClientThread(thisClient)
CreateThread(@ProcessRequest(), thisClient); thread to process incoming data
EndIf
IsClientThread() is a funktion, with a value, set from the thread, or so.
Note: The ClientID is
not unique. If Client1 have 12345, Client2 have 67890 and Client1 close the connection, Client3 can get the ClientID 12345 as well!
Re: New networking command
Posted: Sat Apr 21, 2012 9:50 pm
by RichAlgeni
I understand that, and have created a work around that does exactly what you propose. In fact, I took it a step further by creating a map, saving the client id into the map, then removing the client id after a network read has been done.
As far as
(t)he result is perfectly correct and logical
one can debate that, it may very well be. That however does not address my request, which is to add a command that would return the next server event, or 0, if there were no more server events. I'm hoping that somewhere in the receive network code there is a process which increments to the next server event. I respectfully request that the code to do this be made into a new command.
Code: Select all
NextNetworkServerEvent() ; new command to increment to next server event
That way resources would not be wasted in trying to receive data on a socket that has already been, or is in the process of being interrogated.
Re: New networking command
Posted: Sat Apr 21, 2012 10:40 pm
by STARGÅTE
oke, i understand.
But if you use a NextNetworkServerEvent() you/we need also a ResetNetworkServerEvent().
The other problem is, if you send more data, it can be, that the server need 2 events to receive all datas.
Then your code wouldn't run with the new command too.
You split one data packet on two threads, thats a problem.
Re: New networking command
Posted: Sat Apr 21, 2012 11:01 pm
by RichAlgeni
Glad you understand! Understood about your reset point. I would have no problem with that.
As far as sending more data, I guess it would depend on whether the socket dropped in between? I have tested using two machines on different sub-nets with large amounts of data. I have also tested with multiple serial messages, where one machine waits for the other to send a message before it replies back. I haven't had any trouble with events in this way, as I don'rt believe a server event is raised after a connection has take place, except for the socket drop event.
Just in case anyone is interested, let me know and I will post the code I ultimately came up with. Understand that I have this written for Windows, and running as a service.
Rich