Let's Talk About Threads
BlitzMax Forums/BlitzMax Beginners Area/Let's Talk About Threads
| ||
| 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
|
| ||
| 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. |
| ||
| 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? |
| ||
| 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: |
| ||
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
|