Foundations of Python Network Programming

(WallPaper) #1
Chapter 3 ■ tCp

45

Unfortunately, no equivalent Standard Library wrapper is provided for the recv() call, even though it suffers
from the same possibility of incomplete transmission. Internally, the operating system implementation of recv()
uses logic very close to that used when sending.


•    If no data is available, then recv() blocks, and your program pauses until data arrives.

•    If plenty of data is available already in the incoming buffer, then you are given as many bytes
as you gave recv() permission to deliver.

•    If the buffer contains only some waiting data but not as much as you gave recv() permission
to return, then you are immediately returned what does happen to be there even if it is not as
much as you have requested.

That is why the recv() call has to be inside a loop. The operating system has no way of knowing that this simple
client and server are using fixed-width 16-octet messages. Since it cannot guess when the incoming data might finally
add up to what your program will consider a complete message, it gives you whatever data it can as soon as possible.
Why does the Python Standard Library include sendall() but no equivalent for the recv() method? It is
probably because fixed-length messages are so uncommon these days. Most protocols have far more complicated
rules about how part of an incoming stream is delimited than a simple decision that “the message is always 16 bytes
long.” In most real-world programs, the loop that runs recv() is more complicated than the one in Listing 3-1,
because a program often has to read or process part of the message before it can guess how much more is coming.
For example, an HTTP response consists of headers, a blank line, and then however many further bytes of data were
specified in the Content-Length header. You do not know how many times to keep running recv() until you had at
least received the headers and then parsed them to find out the content length, and this kind of detail is best left to
your application instead of the Standard Library.


One Socket per Conversation

Turning to the server code in Listing 3-1, you see a very different pattern than you witnessed earlier, and the difference
hinges on the very meaning of a TCP stream socket. Recall the our previous discussion that there are two different
kinds of stream sockets: listening sockets, with which servers make a port available for incoming connections, and
connected sockets, which represent a conversation that a server is having with a particular client.
In Listing 3-1, you can see how this distinction is carried through in actual server code. The link, which might
strike you as odd at first, is that a listening socket actually returns a new, connected socket as the value that you get by
calling accept()! Let’s follow the steps in the program listing to see the order in which the socket operations occur.
First, the server runs bind() to claim a particular port. Note that this does not yet decide whether the program
will be a client or server, that is, whether it will be actively making a connection or passively waiting to receive
incoming connections. It simply claims a particular port, either on a particular interface or all interfaces, for the use of
this program. Even clients can use this call if, for some reason, they want to reach out to a server from a particular port
on their machine rather than simply using whatever ephemeral port number they would otherwise be assigned.
The real moment of decision comes with the next method call, when the server announces that it wants to use the
socket to listen(). Running this on a TCP socket utterly transforms its character. After listen() has been called,
the socket is irrevocably changed and can never, from this point on, be used to send or receive data. This particular
socket object will now never be connected to any specific client. Instead, the socket can now be used only to receive
incoming connections through its accept() method—a method that you have not seen yet in this book because its
purpose is solely to support listening TCP sockets—and each of these calls waits for a new client to connect and then
returns an entirely new socket that governs the new conversation that has just started with them.
As you can see from the code, getsockname() works fine against both listening and connected sockets, and in
both cases, it lets you find out what local TCP port the socket is using. To learn the address of the client to which a
connected socket is linked, you can run the getpeername() method at any time, or you can store the socket name that
is returned as the second return value from accept(). When you run this server, you see that both values give you the
same address.

Free download pdf