Page 1 of 1

CreateNetworkServer(), Clarification on capabilities

Posted: Tue Aug 23, 2022 10:23 pm
by Oso
I'm experimenting with CreateNetworkServer() following two great examples at the bottom of this page... https://www.purebasic.com/documentation ... ndex.html
I'm pleasantly surprised that the server will successfully service multiple client connections. I was expecting it to be available for only 1 simultaneous client at a time. I've done some work with Visual Basic winsock in the past and I was not able to achieve this with VB. A few questions I have...

1. How does PB achieve multiple clients in this way? I can see from the task manager that it doesn't spawn a new server process for each client (i.e. like Unix tty connections), unless there's something I haven't found. I'm guessing that it negotiates a new port with each new client. Anyway, it seems to work beautifully, but I'd love to have it confirmed that there are no gotchas that I haven't thought about :-)

2. In the server example, the memory which is addressed by the pointer isn’t cleared, so if the client sends a packet containing ABCDEF, followed by a packet containing XYZ, there will be XYZDEF in memory. I tried adding #Null to the end of the string at the client side, but it just adds numeric zero. Is there a simple way to clear memory, or is there a better way?

Code: Select all

*Buffer = AllocateMemory(1000)
ReceiveNetworkData(ClientID, *Buffer, 1000)
3. The server process has a "repeat until quit = 1" loop, which causes the CPU to consume 99 – 100%. What would be the recommended solution to that?

Re: CreateNetworkServer(), Clarification on capabilities

Posted: Tue Aug 23, 2022 10:50 pm
by mk-soft
1) The network functions of PureBasic work very well.
However, a lot of editing still needs to be done. It is not the receive packet that is read out, but the receive buffer of the system. So you have to take care of the completeness of the data yourself. For example, if you send data of up to 65kb at a time via TCP/IP, it will not arrive in the receive buffer all at once. This is because the data in the network is split into different packets and must be reassembled in the programme. This is normal and therefore many protocols also use header information about the size of the data.

2) The PB network server works as listening and informs you via an event that a new client has connected. To manage the clients you should use MAP.

3) The server should not be processed in the main scope, but in a thread. Here you can also build a server event loop, where a delay of 10 ms takes the CPU load away.

4) Read at wiki how the protocol TCP/IP works. Also the meaning of the layers in the ISO model

Small base for server thread:

Code: Select all


Structure udtClientData
  ConnectionID.i
  Time.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

Procedure ThreadServer(*ServerData.udtServerData)
  Protected Event, ConnectionID, keyConnectionID.s, count, Text.s, Name.s, ok, time
  Protected len, *buffer
  
  With *ServerData
    
    time = ElapsedMilliseconds()
    *buffer = AllocateMemory($FFFF)
    Repeat
      Event = NetworkServerEvent(\ServerID)
      If Event
        ConnectionID = EventClient()
        keyConnectionID = Hex(ConnectionID)
      EndIf
      Select Event
        Case #PB_NetworkEvent_Connect
          If FindMapElement(\Client(), keyConnectionID)
            ;TODO
            DeleteMapElement(\Client(), keyConnectionID)
          EndIf
          
          AddMapElement(\Client(), keyConnectionID)
          \Client()\ConnectionID = ConnectionID
          \Client()\Time = ElapsedMilliseconds()
          \Client()\Date = Date()
          ;TODO
          
        Case #PB_NetworkEvent_Data
          len = ReceiveNetworkData(ConnectionID, *Buffer, $FFFF)
          If FindMapElement(\Client(), keyConnectionID)
            \Client()\Time = ElapsedMilliseconds()
            ;TODO
          EndIf
          
        Case #PB_NetworkEvent_Disconnect
          If FindMapElement(\Client(), keyConnectionID)
            ;TODO
            DeleteMapElement(\Client(), keyConnectionID)
          EndIf
          
        Case #PB_NetworkEvent_None
          Delay(10)
          
      EndSelect
    Until \ExitServer
    
    CloseNetworkServer(\ServerID)
    \ThreadID = 0
    \ServerID = 0
    \ExitServer = 0
    ClearMap(\Client())
    FreeMemory(*buffer)
    
  EndWith
EndProcedure

Re: CreateNetworkServer(), Clarification on capabilities

Posted: Tue Aug 23, 2022 11:37 pm
by Oso
mk-soft wrote: Tue Aug 23, 2022 10:50 pm 1) The network functions of PureBasic work very well.
However, a lot of editing still needs to be done. It is not the receive packet that is read out, but the receive buffer of the system. So you have to take care of the completeness of the data yourself. For example, if you send data of up to 65kb at a time via TCP/IP, it will not arrive in the receive buffer all at once. This is because the data in the network is split into different packets and must be reassembled in the programme. This is normal and therefore many protocols also use header information about the size of the data.
Thanks for this. From testing so far, it looks like PB always correctly identifies the particular client from the EventClient() result, so it's surprisingly close to what I need. I understand that if the client sends > 64kb, the packets might not arrive in sequence. Is that what your sample code is dealing with? A lot of it is new to me.

I'm still surprised that PB supports multiple client connections, without us needing to handle a separate process for each client. What if two clients send data at exactly the same time? Does it process them in turn?
mk-soft wrote: Tue Aug 23, 2022 10:50 pm 2) The PB network server works as listening and informs you via an event that a new client has connected. To manage the clients you should use MAP.
It was more a case of dealing with the string being shorter than the last string received. The memory contains XYZDEF, but in fact the DEF was a remnant from the string that the client (or different client) sent previously. I noticed just now that the number of characters is available, so that's solved.

Code: Select all

chrsreceived.i = ReceiveNetworkData()
... but yes, I'd like to better understand your reference to MAP :-)

Re: CreateNetworkServer(), Clarification on capabilities

Posted: Wed Aug 24, 2022 1:39 am
by mk-soft
Sent data up to 65kb also arrives in the correct order, but not necessarily all data arrives at once. This is taken care of by the TCP/IP protocol. If several clients send, they are also separated correctly. You just have to assign the data to the right client until they are complete.
You can also use MAPS for this by using the ConnectionID as a string for the map key.

Re: CreateNetworkServer(), Clarification on capabilities

Posted: Wed Aug 24, 2022 7:38 pm
by Oso
mk-soft wrote: Wed Aug 24, 2022 1:39 am Sent data up to 65kb also arrives in the correct order, but not necessarily all data arrives at once. This is taken care of by the TCP/IP protocol. If several clients send, they are also separated correctly. You just have to assign the data to the right client until they are complete.
Thanks, just trying to follow your example code. Are you staying inside that block of code until the client disconnects, or is there some other way that your know you have all the client's packets?

Re: CreateNetworkServer(), Clarification on capabilities

Posted: Wed Aug 24, 2022 11:05 pm
by mk-soft
Oso wrote: Wed Aug 24, 2022 7:38 pm Thanks, just trying to follow your example code. Are you staying inside that block of code until the client disconnects, or is there some other way that your know you have all the client's packets?
This is the problem that many do not understand. The TCP/IP protocol takes care of many basic communications. Transmission of data, checksum and repetition, establishment and termination of the connection.
But not the length of all the data that is transmitted. Or where the data ends and where the next data begins. Here you have to become active yourself. For example, with control characters STX and ETX or with header info with lengths of the data, etc.