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