Chapter 2 ■ UDp
26
First, UDP’s unreliability means that the client has to perform its request inside a loop. It either has to be
prepared to wait forever for a reply or else be somewhat arbitrary in deciding when it has waited “too long” for a reply
and that it needs to send another one. This difficult choice is necessary because there is generally no way for the client
to distinguish between these three quite different events:
• The reply is taking a long time to come back, but it will soon arrive.
• The reply will never arrive because it, or the request, was lost.
• The server is down, and it is not replying to anyone.
So, a UDP client has to choose a schedule on which it will send duplicate requests if it waits a reasonable period
of time without getting a response. Of course, it might wind up wasting the server’s time by doing this because the
first reply might be about to arrive and the second copy of the request might cause the server to perform needless
duplicate work. At some point, however, the client must decide to resend the request or it risks waiting forever.
Thus, rather than letting the operating system leave it forever paused in the recv() call, this client first does a
settimeout() on the socket. This informs the system that the client is unwilling to stay stuck waiting inside a socket
operation for more than delay seconds, and it wants the call interrupted with a socket.timeout exception once a call
has waited for that long.
A call that waits for a network operation to complete is said to block the caller. The term blocking is used to
describe a call like recv() that makes the client wait until new data arrives. When you get to Chapter 7 where server
architecture is discussed, the distinction between blocking and nonblocking network calls will loom very large!
This particular client starts with a modest tenth-of-a-second wait. For my home network, where ping times are
usually a few dozen milliseconds, this will rarely cause the client to send a duplicate request simply because the reply
is delayed in getting back.
An important feature of this client program is what happens if the timeout is reached. It does not simply
start sending out repeat requests over and over again at a fixed interval! Since the leading cause of packet loss is
congestion—as anyone knows who has tried sending normal data upstream over a DSL modem at the same time that
photographs or videos are uploading—the last thing you want to do is to respond to a possibly dropped packet by
sending even more of them.
Therefore, this client uses a technique known as exponential backoff, where its attempts become less and less
frequent. This serves the important purpose of surviving a few dropped requests or replies, while making it possible
that a congested network will slowly recover as all of the active clients back off on their demands and gradually send
fewer packets. Although there exist fancier algorithms for exponential backoff—for example, the Ethernet version
of the algorithm adds some randomness so that two competing network cards are unlikely to back off on exactly the
same schedule—the basic effect can be achieved quite simply by doubling the delay each time that a reply is not
received.
Please note that if the requests are being made to a server that is, say, 200 milliseconds away, this naive algorithm
will always send at least two copies of each request, every time, because it will never learn that requests to this
server always take more than 0.1 seconds. If you are writing a UDP client that lives a long time, think about having it
remember how long the last few requests have taken to complete so that it can delay its first retry until the server has
had enough time to reply.
When you run the Listing 2-2 client,, give it the hostname of the other machine on which you are running the
server script, as shown previously. Sometimes, this client will get lucky and get an immediate reply.
$ python udp_remote.py client guinness
Client socket name is ('127.0.0.1', 45420)
Waiting up to 0.1 seconds for a reply
The server says 'Your data was 23 bytes long'