Server Basics

How long does it take to explain 6 self-explanatory simple lines of code?

A whole page, apparently.

When are we getting to some actual code?

Right now! Lets get started! We're still looking at dots_server.py if you're following along at home.


Setting it All Up

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
server.bind((HOST,PORT))
server.listen(BACKLOG)

These three lines create the socket, connect it to the specified HOST and PORT, and listen and accepts users. (Until there are BACKLOG users already connected). Our server looks like this:

Skip def handleClient() and def serverThread() -- we'll get to those later

Creating the Server Thread

clientele = dict()
playerNum = 0

This creates a dictionary of all the clients and sets the current playerNum to 0 (for the first client to join).

serverChannel = Queue(100)
threading.Thread(target = serverThread, args = (clientele, serverChannel)).start()

Now, we create the server channel, which is a queue of string instructions that need to be executed. We give it a size 100, but it probably doesn't need to be that long. This means that we can hold 100 messages at once (they'll be processed as we go, so it is unlikely we'll ever get that many stacked up)

Then, we create the server thread, which is the core of sockets; it handles incoming client messages (taken from the server channel) and relays them back to the other clients. This thread runs the serverThread function continuously. (explained later)

Right now, our server looks like this:

Scary while True Loop #1

while True:
  client, address = server.accept()
  # myID is the key to the client in the clientele dictionary
  myID = names[playerNum]
  print(myID, playerNum)
  for cID in clientele:
    print (repr(cID), repr(playerNum))
    clientele[cID].send(("newPlayer %s\n" % myID).encode())
    client.send(("newPlayer %s\n" % cID).encode())
  clientele[myID] = client
  client.send(("myIDis %s \n" % myID).encode())
  print("connection recieved from %s" % myID)
  threading.Thread(target = handleClient, args = 
                        (client ,serverChannel, myID, clientele)).start()
  playerNum += 1

Don't be scared! This loop lets you add new players. This is done by waiting for new clients to arrive

client, address = server.accept()

and once someone comes, they let everyone else know that they have arrived

clientele[cID].send(("newPlayer %s\n" % myID).encode())

and add that everyone they just talked to to his/her/their own client list. (notice how I said "to" twice in a row. I think that still makes sense)

client.send(("newPlayer %s\n" % cID).encode())

Once these meet and greet shenanigans have happened with all the existing clients (did anyone else hate O-week?), the new client is added to the list of clientele, and a new client thread is started for them. What does the second line do? Well that lets the new client know his/her/their own name, of course!

clientele[myID] = client
client.send(("myIDis %s \n" % myID).encode())
threading.Thread(target = handleClient, args = (client ,serverChannel, myID, clientele)).start()

This is what the process looks like. Here is our trusty server:

Notice how even though clients are being added, our server is still running the "Receiving Clients" thread in case anyone else tries to join.

Why does the server need threads for each client?

Great question! The each client thread on the server is essentially a mailbox for the client. In order for the server to actually be able to send messages to the clients, they need a place for the server to actually relay messages to. Just think. Without a client thread, there would be no way for the server to communicate with the clients.

results matching ""

    No results matching ""