Foundations of Python Network Programming

(WallPaper) #1
Chapter 3 ■ tCp

43

def client(host, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))
print('Client has been assigned socket name', sock.getsockname())
sock.sendall(b'Hi there, server')
reply = recvall(sock, 16)
print('The server said', repr(reply))
sock.close()


if name == 'main':
choices = {'client': client, 'server': server}
parser = argparse.ArgumentParser(description='Send and receive over TCP')
parser.add_argument('role', choices=choices, help='which role to play')
parser.add_argument('host', help='interface the server listens at;'
' host the client sends to')
parser.add_argument('-p', metavar='PORT', type=int, default=1060,
help='TCP port (default 1060)')
args = parser.parse_args()
function = choices[args.role]
function(args.host, args.p)


In Chapter 2, I approached the subject of bind() quite carefully, since the address you provide as its argument
makes an important choice: it determines whether remote hosts can try connecting to our server or whether your server
is protected against outside connections and can be contacted only by other programs running on the same machine.
Accordingly, Chapter 2 started with safe program listings that bound themselves only to the loopback interface and then
progressed to more dangerous program listings that accepted connections from other hosts on the network.
But here I have combined both possibilities into a single listing. With the host argument that you provide from
the command line, either you can make the safer choice of binding to 127.0.0.1 or you can choose to bind to one
of your machine’s external IP addresses instead—or you can supply a blank string to indicate that you will accept
connections at any of your machine’s IP addresses whatsoever. Again, review Chapter 2 if you want to remember all
of the rules, which apply equally to TCP and UDP connections and sockets.
Your choice of port number also carries the same weight as it did when you chose port numbers for UDP in
Chapter 2, and, again, the symmetry between TCP and UDP on the subject of port numbers is similar enough that you
can simply apply the reasoning you used there to understand why the same choice has been used here in this chapter.
So, what are the differences between the earlier efforts with UDP and this new client and server that are instead
built atop TCP?
The client actually looks much the same. It creates a socket, it runs connect() with the address of the server
with which it wants to communicate, and then it is free to send and receive data. But beyond that, there are
several differences.
First, the TCP connect() call—as I discussed a moment ago—is not the innocuous bit of local socket
configuration that it is in the case of UDP, where it merely sets a default remote address to be used with any
subsequent send() or recv() calls. Here, connect() is a real live network operation that kicks off the three-way
handshake between the client and server machine so that they are ready to communicate. This means that connect()
can fail, as you can verify quite easily by executing the client when the server is not running.


$ python tcp_deadlock.py client localhost
Sending 16 bytes of data, in chunks of 16 bytes
Traceback (most recent call last):
...
ConnectionRefusedError: [Errno 111] Connection refused

Free download pdf