I l@ve RuBoard Previous Section Next Section

10.3 Writing a TCP Server

Credit: Luther Blissett

10.3.1 Problem

You want to write a server that waits for clients to connect over the network to a particular port.

10.3.2 Solution

Assuming you're using the Internet to communicate:

import socket

# Create a socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Ensure that you can restart your server quickly when it terminates
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# Set the client socket's TCP "well-known port" number
well_known_port = 8881
sock.bind(('', well_known_port))

# Set the number of clients waiting for connection that can be queued
sock.listen(5)

# loop waiting for connections (terminate with Ctrl-C)
try:
    while 1:
        newSocket, address = sock.accept(  )
        print "Connected from", address
        # loop serving the new client
        while 1:
            receivedData = newSocket.recv(1024)
            if not receivedData: break
            # Echo back the same data you just received
            newSocket.send(receivedData)
        newSocket.close(  )
        print "Disconnected from", address
finally:
    sock.close(  )

10.3.3 Discussion

Setting up a server takes a bit more work than setting up a client. We need to bind to a well-known port that clients will use to connect to us. Optionally, as we do in this recipe, we can set SO_REUSEADDR so we can restart the server when it terminates without waiting for a few minutes, which is quite nice during development and testing. We can also optionally call listen to control the number of clients waiting for connections that can be queued.

After this preparation, we just need to loop, waiting for the accept method to return; it returns a new socket object already connected to the client and the client's address. We use the new socket to hold a session with a client, then go back to our waiting loop. In this recipe, we just echo back the same data we receive from the client.

The SocketServer module lets us perform the same task in an object-oriented way. Using it, the recipe becomes:

import SocketServer

class MyHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        while 1:
            dataReceived = self.request.recv(1024)
            if not dataReceived: break
            self.request.send(dataReceived)

myServer = SocketServer.TCPServer(('',8881), MyHandler)
myServer.serve_forever(  )

One handler object is instantiated to serve each connection, and the new socket for that connection is available to its handle method (which the server calls) as self.request.

Using the SocketServer module instead of the lower-level socket module is particularly advisable when we want more functionality. For example, to spawn a new and separate thread for each request we serve, we would need to change only one line of code in this higher-level solution:

myServer = SocketServer.ThreadingTCPServer(('',8881), MyHandler)

while the socket-level recipe would need substantially more recoding to be transformed into a similarly multithreaded server.

10.3.4 See Also

Recipe 10.2; documentation for the standard library module socket in the Library Reference; Perl Cookbook Recipe 17.2.

    I l@ve RuBoard Previous Section Next Section