Sockets programming in Python
In languages with sockets, the socket is universally the same -- it's a conduit between the two applications that can communicate with one another.
Whether you're writing a sockets application in Python, Perl, Ruby, Scheme, or any other useful language (and by useful I mean languages that have a sockets interface), the socket is universally the same. It's a conduit between the two applications that can communicate with one another (either locally on a single machine or between two machines in separate locations).
The difference with sockets programming in a language like Python is in the helper
classes and methods that can simplify sockets programming. In this section I'll demonstrate
socket API. You can execute the Python interpreter with a script
or, if you execute Python by itself, you can interact with it one line at a time. In this way, you
can see the result of each method invoked.
The following example illustrates interacting with
the Python interpreter. Here, I use the
socket class method
gethostbyname to resolve a fully qualified domain name (www.ibm.com) to
a string quad-dotted IP address ('188.8.131.52'):
Listing 3. Using the socket API from the interpreter command line
[camus]$ python Python 2.4 (#1, Feb 20 2005, 11:25:45) [GCC 3.2.2 20030222 (Red Hat Linux 3.2.2-5)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import socket >>> socket.gethostbyname('www.ibm.com') '184.108.40.206' >>>
socket module is imported, I invoke the
class method to resolve the domain name to an IP address.
Now, I'll discuss the basic
socket methods and communicating
through sockets. Feel free to follow along with your Python interpreter.
To create a new socket, you use the
socket method of the
socket class. This is a class method because you don't yet have a
socket object from which to apply the methods. The
method is similar to the BSD API, as demonstrated in the creation of a stream
(TCP) and datagram (UDP) socket:
Listing 4. Creating stream and datagram sockets
streamSock = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) dgramSock = socket.socket( socket.AF_INET, socket.SOCK_DGRAM )
In each case, a
socket object is returned. The
symbol -- argument one -- indicates that you're requesting an Internet Protocol (IP) socket,
specifically IPv4. The second argument is the transport protocol type
SOCK_STREAM for TCP sockets and
for UDP sockets). If your underlying operating system supports IPv6, you can also specify
socket.AF_INET6 to create an IPv6 socket.
To close a connected socket, you use the
Finally, you can delete a socket with the
This statement permanently removes the
socket object. Attempting to
reference the socket thereafter produces an error.
An endpoint address for a socket is a tuple consisting of an interface address and a port number. Because Python can represent tuples easily, the address and port are represented as such. This illustrates an endpoint for interface address 192.168.1.1 and port 80:
( '192.168.1.1', 80 )
You can also use a fully qualified domain name here, such as:
( 'www.ibm.com', 25 )
This example is simple and certainly beats the
that's necessary in C. The following discussion provides examples of addresses in Python.
Server sockets are typically those that expose a service on a network. Because server and client sockets are created in different ways, I discuss them independently.
After you create the socket, you use the
bind method to bind an address
to it, the
listen method to place it in the listening state, and finally the
accept method to accept a new client connection. This is demonstrated
Listing 5. Using server sockets
sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) sock.bind( ('', 2525) ) sock.listen( 5 ) newsock, (remhost, remport) = sock.accept()
For this server, the address
('', 2525) is used which means that the
wildcard is used for the interface address
(''), allowing incoming
connections from any interface on the host. You also bind to port number 2525.
Note here that the
accept method returns not only the new
socket object that represents the client connection
newsock) but also an address tuple (the remote address and port
number of the peer end of the socket). Python's
can simplify this process even further, as demonstrated above.
You can also create datagram servers, but they are connectionless and therefore
have no associated
accept method. The following example creates a
datagram server socket:
Listing 6. Creating a datagram server socket
sock = socket.socket( socket.AF_INET, socket.SOCK_DGRAM ) sock.bind( ('', 2525) )
The upcoming discussion of sockets I/O shows how I/O works for both stream and datagram sockets.
Now, let's explore how a client creates a socket and connects it to a server.
The mechanisms for creating and connecting client sockets are similar to the setup of server sockets. Upon creating a socket, an address is needed -- not to locally bind the socket (as is the case with a server) but rather to identify where the socket should attach. Say there's a server on a host with an interface IP address of '192.168.1.1' and port 2525. The following code creates a new socket and connects it to the defined server:
Listing 7. Creating a stream socket and connecting to the server
sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) sock.connect( ('192.168.1.1', 2525) )
For datagram sockets, the process is a bit different. Recall that datagram sockets are by nature disconnected. One way to the think about it is as follows: Whereas stream sockets are pipes between two endpoints, datagram sockets are message-based, able to communicate with multiple peers at the same time. Here's an example of a datagram client.
Listing 8. Creating a datagram socket and connecting to the server
sock = socket.socket( socket.AF_INET, sock.sock_DGRAM ) sock.connect( ('192.168.1.1', 2525) )
What's different here is that even though I've used the
there's no real connection between the client and server. The connect here is a
simplification for later I/O. Typically in datagram sockets, you must provide the destination
information with the data that you want to send. By using
connect, I've cached
this information with the client and
send methods can occur much like
stream socket versions (no destination address is necessary). You can call
again to re-specify the target of the datagram client's messages.
Sending or receiving data through stream sockets is simple in Python. Several methods
exist to move data through a stream socket (such as
This first example demonstrates a server and client for stream sockets. In this demonstration, the server echoes whatever it receives from the client.
The echo stream server is presented in Listing 9. Upon creating a new stream socket, an
address is bound to it (accept connections from any interface and port 45000) and then the
listen method is invoked to enable incoming connections. The echo server
then goes into a loop for client connections. The
accept method is called
and blocks (that is, does not return) until a new client connects, at which point the new
client socket is returned along with address information for the remote client. With this
new client socket, I call
recv to get a string from the peer, then write this
string back out to the socket. I then immediately close the socket.
Listing 9. Simple Python stream echo server
import socket srvsock = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) srvsock.bind( ('', 23000) ) srvsock.listen( 5 ) while 1: clisock, (remhost, remport) = srvsock.accept() str = clisock.recv(100) clisock.send( str ) clisock.close()
Listing 10 shows the echo client that corresponds with the server in Listing 9. Upon
creating a new stream socket, the
connect method is used to attach
this socket to the server. When connected (when the
returns), the client emits a simple text message with the
then awaits the echo with the
recv method. The
method is performed to close the socket.
Listing 10. Simple Python stream echo server
import socket clisock = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) clisock.connect( ('', 23000) ) clisock.send("Hello World\n") print clisock.recv(100) clisock.close()
Datagram sockets are disconnected by nature which means that communication
requires that a destination address be provided. Similarly, when a message is received
through a socket, the source of the data must be returned. The
sendto methods support the additional address information as you can
see in the datagram echo server and client implementations.
Listing 11 shows the datagram echo server. A socket is first created and then bound to
an address using the
bind method. An infinite loop is then entered for
serving client requests. The
recvfrom method receives a message from
the datagram socket and returns not only the message but also the address of the source
of the message. This information is then turned around with the
method to return the message to the source.
Listing 11. Simple Python datagram echo server
import socket dgramSock = socket.socket( socket.AF_INET, socket.SOCK_DGRAM ) dgramSock.bind( ('', 23000) ) while 1: msg, (addr, port) = dgramSock.recvfrom( 100 ) dgramSock.sendto( msg, (addr, port) )
The datagram client is even simpler. After creating a datagram socket, I use the
sendto method to send a message to a specific address. (Remember:
Datagrams have no connection.) After
sendto finishes, I await the echo
recv, then print it. Note that I don't use
here because I'm not interested in the peer address information.
Listing 12. Simple Python datagram echo client
import socket dgramSock = socket.socket( socket.AF_INET, socket.SOCK_DGRAM ) dgramSock.sendto( "Hello World\n", ('', 23000) ) print dgramSock.recv( 100 ) dgramSock.close()
Sockets default to a set of standard behaviors, but it's possible to alter the behavior
of a socket using options. You manipulate socket options with the
method and capture them with the
Using socket options is simple in Python, as demonstrated in Listing 13. In the first example,
I read the size of the socket send buffer. In the second example, I get the value of the
SO_REUSEADDR option (reuse the address within the
period) and then enable it.
Listing 13. Using socket options
sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) # Get the size of the socket's send buffer bufsize = sock.getsockopt( socket.SOL_SOCKET, socket.SO_SNDBUF ) # Get the state of the SO_REUSEADDR option state = sock.getsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR ) # Enable the SO_REUSEADDR option sock.setsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 )
SO_REUSEADDR option is most often used in socket server
development. You can increase the socket send and receive buffers for greater performance,
but given that you're operating here in an interpreted scripting language, it may not
provide you with much benefit.
Python offers asynchronous I/O as part of the
select module. This feature
is similar to the C select mechanism but has some simplifications. I'll first introduce
select and then show you how to use it in Python.
select method allows you to multiplex events for several sockets
and for several different events. For example, you can instruct
notify you when a socket has data available, when it's possible to write data through a
socket, and when an error occurs on a socket; and you can perform these actions for
many sockets at the same time.
Where C works with bitmaps, Python uses lists to represent the descriptors to monitor and also the return descriptors whose constraints are satisfied. Consider the following example in which you await some input from standard input:
Listing 14. Awaiting input from stdin
rlist, wlist, elist = select.select( [sys.stdin], ,  ) print sys.stdin.read()
The arguments passed to
select are lists representing read events,
write events, and error events. The
select method returns three lists
containing the objects whose events were satisfied (read, write, exception). In this example,
rlist should be
[sys.stdin], indicating that data
are available to read on stdin. This data are then read with the
select method also works on socket descriptors. In the following example (see Listing 15), two client sockets are created and connected to a remote peer. The
method is then used to identify which socket has data available for reading. The data
are then read and emitted to stdout.
Listing 15. Demonstrating the
selectmethod with multiple sockets
import socket import select sock1 = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) sock2 = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) sock1.connect( ('192.168.1.1', 25) ) sock2.connect( ('192.168.1.1', 25) ) while 1: # Await a read event rlist, wlist, elist = select.select( [sock1, sock2], , , 5 ) # Test for timeout if [rlist, wlist, elist] == [ , ,  ]: print "Five seconds elapsed.\n" else: # Loop through each socket in rlist, read and print the available data for sock in rlist: print sock.recv( 100 )