Building a Multi-Connection Server
00:19 You’ll be specifying the host and port on the command line when you start the programs. Real Python has a tutorial on command-line arguments should you wish to learn more about them. You’ll look at the server program first. Let’s look at the code to create the listening socket.
The server uses the
types module to create a simple data type to manage the data coming from the client. Next, you’ll see where the program creates a default selector to contain the sockets that are created.
01:26 The first thing you should notice is a diagnostic check that the server program started with the host and port. Including the name of the server, there should be three arguments, so a check is done for that, and if they’re not all there, the program exits after printing why.
01:57 Bind the socket to the provided host and port. Tell the socket to start listening for a connection, but this time you don’t want the process to block. The program needs to continue running, as it’s expecting requests from more than one client. Finally, and this is new, the socket is registered onto the selector.
The selector will notify the program when the socket has read some input, indicated by
EVENT_READ, which is why the socket doesn’t need to block the process while waiting to accept a connection.
sel should monitor the socket and alert the program when it’s ready to be processed. Since it’s a listening socket, it only needs to respond to read events, and as mentioned before, this program is much more useful having the host and port numbers passed as command-line arguments.
03:07 Next is the event loop the server has to create. The program needs to detect if a socket is ready to be processed. It could either be the listening socket, meaning it’s ready to accept a connection, or could be a previously created servicing socket having received data.
You can see the beginning of the
while True loop. This program repeatedly checks if the selector has returned a socket that needs attention. For each one it finds, it checks to see if it’s the listening socket or a processing socket and calls the appropriate helper function to handle it.
04:15 That information is added to a socket designated to process data. If the socket doesn’t actually contain data, that means it was a listening socket. A connection from the client has been requested, and the server needs to accept it. If the key does contain data, then it was a socket processing the communication, and it needs to be serviced to echo the data back.
Let’s review these. Using
timeout=None is telling this method to block. If there are no sockets to be processed, then the program has nothing to do and should pause here until the selector responds that there is a socket event to handle.
.select() method returns a key-mask pair. The key contains information about the socket as it was registered to the selector, and the mask has information about the type of activity the socket reacted to, reading or writing.
Included in the information about the socket is the field
data, which indicates what was received from the socket. If the
data field was empty, then this is a listening socket ready to process a new connection.
The socket shouldn’t blocked since it’s going to be registered on the selector. Here’s where the server specifies what the data will look like. It’s going to be a simple object with the client’s address, a field for the data it received,
inb, and a field for the data being prepared to echo back,
Let’s review some of the important things you saw in that code. Just as in the previous version, you want to save the new socket and address from the
.accept() method call, but this time, you don’t want
.accept() to block. If it blocked, you wouldn’t be able to create or respond to any other interactions until this method was ready.
06:46 This statement creates a simple object of an unnamed class with three fields. These fields will be used by the server to keep track of the data received and sent as it processes an echo request.
The selector will want to know what type of events to listen to. This
events variable will be used to tell the selector to respond to both read and write requests from the sockets it’s managing.
This is done in the
service_connection() function. First, the function extracts the socket and data from the arguments. If the socket is ready to be read, then a one-kilobyte chunk of data is appended to the data in the object’s
outb field. Once that chunk has been read, it will be echoed back since the socket is normally ready to write.
It might not be the entire string of data received, so the program uses a slice operation to remove just those bytes from the
outb string. This process repeats until the client sends something without data.
09:17 And now let’s go over the writing portion of the echo process. First, you want to make sure there is still data to be sent back. There should be if the program gets here, but it’s nice to check so the next statements don’t fail. Next, you want to echo that data back. The argument includes the entire data string, but only so much will be sent depending on what the client is willing to accept.
09:40 This method will actually return the number of bytes sent so the program can keep track. It then uses a slice operation to strip off the part of the data it actually did send so that what’s left can be sent the next time this function is called.
Become a Member to join the conversation.