Here you can find the code of a simple TCP client and server that send and receive 16 octets:
import socket, sys s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) HOST = sys.argv.pop() if len(sys.argv) == 3 else '127.0.0.1' PORT = 1060 def recv_all(sock, length): data = '' while len(data) < length: more = sock.recv(length - len(data)) if not more: raise EOFError('socket closed %d bytes into a %d-byte message' % (len(data), length)) data += more return data if sys.argv[1:] == ['server']: s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind((HOST, PORT)) s.listen(1) while True: print 'Listening at', s.getsockname() sc, sockname = s.accept() print 'We have accepted a connection from', sockname print 'Socket connects', sc.getsockname(), 'and', sc.getpeername() message = recv_all(sc, 16) print 'The incoming sixteen-octet message says', repr(message) sc.sendall('Farewell, client') sc.close() print 'Reply sent, socket closed' elif sys.argv[1:] == ['client']: s.connect((HOST, PORT)) print 'Client has been assigned socket name', s.getsockname() s.sendall('Hi there, server') reply = recv_all(s, 16) print 'The server said', repr(reply) s.close() else: print >>sys.stderr, 'usage: tcp_local.py server|client [host]'
First, the TCP
connect() call is not the innocuous bit of local
socket configuration that it is in the case of UDP, where it merely sets a default address used with any
send() calls, and places a filter on packets arriving at our socket. 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 this script when the server is not running:
root@erlerobot:~/Python_files# python tcp_sixteen.py client Traceback (most recent call last): File "tcp_sixteen.py", line 29, in <module> s.connect((HOST, PORT)) File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 224, in meth return getattr(self._sock,name)(*args) socket.error: [Errno 61] Connection refused
You will see that this TCP client is in one way much simpler than our UDP client, because it
does not need to make any provision for missing data. Because of the assurances that TCP provides, it
send() data without checking whether the remote end receives it, and run
recv() without having to
consider the possibility of re-transmitting its request.
When we perform a TCP
send(), our operating system’s networking stack will face one of three
send()returns immediately, and it will return the length of your data string because the whole string was transmitted.
send()is simply to block, pausing your program until the data can be accepted.
send()completes immediately and returns the number of bytes accepted from the beginning of your data string, but leaves the rest of the data unprocessed.
Fortunately, Python does not force us to do this dance ourselves every time we have a block of data
to send: the Standard Library socket implementation provides a friendly
sendall() method. Not only is
sendall()faster than doing it ourselves, it releases the Global Interpreter Lock during its
loop so that other Python threads can run without contention until all of the data has been transmitted.
Unfortunately, no equivalent is provided for the
recv() call, despite the fact that it might return only
part of the data that is on the way from the client. Internally, the operating system implementation of
recv() uses logic very close to that used when sending:
recv()blocks and your program pauses until data arrives.
In the code stored in
tcp_sixteen.py, you can see how the distinction between active and listening socket is carried through in actual server code. The link,
which might strike you as odd at first, is that a listening socket actually produces new connected sockets
as the return value that you get by listening. Follow the steps in the program listing to see the order in
which the socket operations occur.
Run the server:
root@erlerobot:~/Python_files# python tcp_sixteen.py server Listening at ('127.0.0.1', 1060)
And then the client(in another terminal window):
root@erlerobot:~/Python_files# python tcp_sixteen.py client Client has been assigned socket name ('127.0.0.1', 49607) The server said 'Farewell, client'
The server returns this:
We have accepted a connection from ('127.0.0.1', 49607) Socket connects ('127.0.0.1', 1060) and ('127.0.0.1', 49607) The incoming sixteen-octet message says 'Hi there, server' Reply sent, socket closed Listening at ('127.0.0.1', 1060)