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
subsequent 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
can 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
situations:
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.recv()
for.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)