Let's Talk About Threads

BlitzMax Forums/BlitzMax Beginners Area/Let's Talk About Threads

Chroma(Posted 2009) [#1]
So I'm sitting here scratching my head on how threads will benefit network code. Obviously if each player has it's own thread on the server that would be a bonus.

The server listens on a specific port for incoming connections. When a request is made it connects the client and starts the client it's own thread. So now we have the server app running and a client thread. The client thread is listening to and sending packets to that specific client. So how would the client thread interface with the server app? I'm guessing you're trying to avoid the whole looping through a list of players to see if a packet has arrived. So you'd have to make it so the client is listening and when a packet arrives it just forces the packet into the server via some means. Hmm...May have a packetqueue on the server app that is set to 0 and when the client thread has a packet it adds it to the packetque and sets a variable to true so the server knows a packet is there. No looping through all clients. Just have to figure that one out.

Pseudo-code:

While Not KeyHit(KEY_ESCAPE)

   Local clientID% = server.isPacketWaiting()
   If clientID then server.ProcessPacket(clientID)

Flip 0
Wend
End

Type TServer
   field Max_Players%
   Field ClientList:TList
   Field ClientThread:TThread[]
   Field isPacketWaiting%

   Function Create:TServer(maxPlayers%=32)
      Local s:TServer = New TServer
      s.Max_Players = maxPlayers
      s.ClientList = New TList
      s.ClientThread = [maxPlayers]
      Return S
   End Function
End Type

Type TClient
   Field id%

   Function Create:TClient()
      Local c:TClient = New TClient
      c.id = GetFreeId()
      Return c
   End Fuction
End Type



_Skully(Posted 2009) [#2]
Warning: Threading with Network code runs the risk of denial of service attacks... I would be thinking about that while you code it.

I would most definitely want to queue the requests, otherwise your network code could swallow your game/program.

You would likely want a thread for packet arrival and either another for processing or a time-limited loop in the main program.

Packet arrives and goes to queue if queue is not full
packets get processed during packet processing time

As far as client / server goes I think you might be mixing apples and oranges. a client connects to the server over the network, the server serves the client. If you are hosting the client and the server on the same box then you would likely want to create an alternate path for those packets to get to the server rather than sending anything over the network.


Chroma(Posted 2009) [#3]
So you have the main server app and then one separate thread that's listening for new packets. Wait, but why not one thread per client that's just listening for that one specific client? Is that overkill?


Chroma(Posted 2009) [#4]
Ok I'm thinking that there has to be an optimal number of threads to run. And do threads soley rely on how many cores a processor has?

If 4 threads is optimal then I can just split the number of clients among the 4 threads evenly. That way it's like 4 servers running and each is only serving a limited number of clients. The main server app would just monitor the threads to make sure everything was running ok. If you had 16 players and 4 threads, that's 4 clients per thread. Each thread receives a packet from the 4 players assigned to it but it broadcasts the packet back out to all players excluding itself.

Here's the new pseudo-code:



Chroma(Posted 2009) [#5]
Here's something new.

'TServer2

'8 players over 2 threaded servers
Local server:TServer = TServer.Create(8,2)

Graphics 800,600,0
While Not KeyDown(KEY_ESCAPE)

   server.Listen()

Flip 0
Wend
EndGraphics
End


Type TServer
   Field numClients%, maxClients%
   Field maxClientsPerServerThread%
   Field numServerThreads%, maxServerThreads%
   Field ServerThreadList:TList = New TList
   Field MasterClientList:TList = New TList

   Function Create:TServer(maxClients%, maxServerThreads%)
      Local s:TServer = New TServer
      s.maxClients = maxClients
      s.maxServerThreads = maxServerThreads
      s.maxClientsPerServerThread = maxClients / maxServerThreads
      s.numServerThreads = 0
      Return s
   End Function

   Method Listen()
      'Listen for New Connections
      Local data = Connection.SocketListen()
      If data then server.ConnectClient(data)
   End Method

   Method ConnectClient(data)
      'Here's where we assign the new client to a ServerThread
      'If there are no ServerThreads available, we create a new one
      If Self.numClients < Self.maxClients    'Ok there's room for this guy, let's connect him
         Self.numClients :+ 1
      Else
         'No dice, the game is too popular...start your own server
      Endif   
   End Method

   Method AddServerThread()
      Local st:TServerThread = New TServerThread
      Self.ServerThreadList.AddLast( CreateThread( st.Run() ) )
   End Method

End Type

'-------

Type TServerThread
   Field id%, state%
   Field maxClients%
   Field numClients%
   Field ClientList:TList

   Function Create:TServerThread(id%, maxClients%)
      Local st:TServerThread = New TServerThread
      st.id = id
      st.maxClients = maxClients
      st.ClientList = New TList
      Return st
   End Function

   Method Run()
      Repeat
         Local client:TClient
         For client = Eachin Self.ClientList
            Local data = client.Listen()
            If data Then BroadCast(server.MainClientList
         Next

      Until Self.state = 0
   End Method
End Type

'-------

Type TClient
   Field id%

   Method Send(packet:TPacket)
      WriteBank packet
   End Method
End Type


Type TPacket
   Field data:TBank
End Type

Function BroadCast(client:TList, excludeIP%)

End Function