Section 16.4. SocketServer Module


16.4. *SocketServer Module

SocketServer is a higher-level module in the standard library. Its goal is to simplify a lot of the boilerplate code that is necessary to create networked clients and servers. In this module are various classes created on your behalf:

Table 16.3. SocketServer Module Classes

Class

Description

BaseServer

Contains core server functionality and hooks for mix-in classes; used only for derivation so you will not create instances of this class; use TCPServer or UDPServer instead

TCPServer/UDPServer

Basic networked synchronous TCP/UDP server

UnixStreamServer/UnixDatagramServer

Basic file-based synchronous TCP/UDP server

ForkingMixIn/Threading MixIn

Core forking or threading functionality; used only as mix-in classes with one of the server classes to achieve some asynchronicity; you will not instantiate this class directly

ForkingTCPServer/ForkingUDPServer

Combination of ForkingMixIn and TCPServer/UDPServer

THReadingTCPServer/ThreadingUDPServer

Combination of ThreadingMixIn and TCPServer/UDPServer

BaseRequestHandler

Contains core functionality for handling service requests; used only for derivation so you will not create instances of this class; use StreamRequestHandler or DatagramRequestHandler instead

StreamRequest Handler/DatagramRequest-Handler

Implement service handler for TCP/UDP servers


We will create a TCP client and server that duplicates the base TCP example shown earlier. You will notice the immediate similarities but should recognize how some of the dirty work is now taken care of so you do not have to worry about that boilerplate code. These represent the simplest synchronous server you can write. Please check out the exercises at the end of the chapter to turn your server into an asynchronous one.

In addition to hiding implementation details from you, another difference is that we are now writing our applications using classes. Doing things in an object-oriented way helps us organize our data and logically direct functionality to the right places. You will also notice that our applications are now "event-driven," meaning they only work when "reacting to" an occurrence of an event in our system.

Events include the sending and receiving of messages. In fact, you will see that our class definition only consists of an event handler for the receiving of a client message. All other functionality is taken from the SocketServer classes we use. GUI programming (Chapter 18) is also event-driven. You will notice the similarity immediately as the final line of our code is usually a server's infinite loop waiting for and responding to client service requests. It works almost the same as our infinite while loop in the original base TCP server we create earlier in the chapter.

In our original server loop, we block waiting for a request, then service it when something comes in, and then go back to waiting. In the server loop here, instead of building your code in the server, you define a handler that the server can just call your function when it receives an incoming request.

16.4.1. Creating a SocketServer TCP Server

In our code, we first import our server classes, then define the same host constants as before. That is followed by our request handler class, and then startup. More details follow our code snippet.

Line-by-Line Explanation
Lines 19

The initial stuff consists of importing the right classes from SocketServer. Note that we are using the Python 2.4 multi-line import. If you are using an earlier version of Python, then you will have use the fully-qualified module.attribute names or put both attribute imports on the same line:

from SocketServer import TCPServer as TCP, StreamRequestHandler as SRH


Example 16.5. SocketServer Timestamp TCP Server (tsTservSS.py)

Creates a timestamp TCP server using SocketServer classes TCPServer and StreamRequestHandler.

        1   #!/usr/bin/env python         2         3   from SocketServer import (TCPServer as TCP,         4        StreamRequestHandler as SRH)         5   from time import ctime         6         7   HOST = ''         8   PORT = 21567         9   ADDR = (HOST, PORT)         10         11  class MyRequestHandler(SRH):         12       def handle(self):         13            print '...connected from:', self.client_address         14            self.wfile.write('[%s] %s' % (ctime(),         15                self.rfile.readline()))         16         17  tcpServ = TCP(ADDR, MyRequestHandler)         18  print 'waiting for connection...'         19  tcpServ.serve_forever()

Lines 1115

The bulk of the work happens here. We derive our request handler MyRequestHandler as a subclass of SocketServer's StreamRequestHandler and override its handle() method, which is stubbed out in the Base Request class with no default action as:

def handle(self):     pass


The handle() method is called when an incoming message is received from a client. The StreamRequestHandler class treats input and output sockets as file-like objects, so we will use readline() to get the client message and write() to send a string back to the client.

In accordance, we need additional carriage return and NEWLINE characters in both the client and server code. Actually, you will not see it in the code because we are just reusing those which come from the client. Other than these minor differences we have mentioned, it should look just like our earlier server.

Lines 1719

The final bits of code create the TCP server with the given host information and request handler class. We then have our entire infinite loop waiting for and servicing client requests.

16.4.2. Creating a SocketServer TCP Client

Our client will naturally resemble our original client, much more so than the server, but it has to be tweaked a bit to work well with our new server.

Line-by-Line Explanation
Lines 18

Nothing special here ... this is an exact replica of our original client code.

Example 16.6. SocketServer Timestamp TCP Client (tsTclntSS.py)

This is a timestamp TCP client that knows how to speak to the file-like Socket Server class StreamRequestHandler objects.

        1   #!/usr/bin/env python         2         3   from socket import *         4         5   HOST = 'localhost'         6   PORT = 21567         7   BUFSIZ = 1024         8   ADDR = (HOST, PORT)         9         10  while True:         11       tcpCliSock = socket(AF_INET, SOCK_STREAM)         12       tcpCliSock.connect(ADDR)         13       data = raw_input('> ')         14       if not data:         15           break         16       tcpCliSock.send('%s\r\n' % data)         17       data = tcpCliSock.recv(BUFSIZ)         18       if not data:         19           break         20       print data.strip()         21       tcpCliSock.close()

Lines 1021

The default behavior of the SocketServer request handlers is to accept a connection, get the request, and close the connection. This makes it so that we cannot keep our connection throughout the execution of our application, so we need to create a new socket each time we send a message to the server.

This behavior makes the TCP server act more like a UDP server; however, this can be changed by overriding the appropriate methods in our request handler classes. We leave this as an exercise at the end of this chapter.

Other than the fact that our client is somewhat "inside-out" now (because we have to create a connection each time), the only other minor difference was previewed in the line-by-line explanation for the server code: the handler class we are using treats socket communication like a file, so we have to send line-termination characters (carriage return and NEWLINE) each way. The server just retains and reuses the ones we send here. When we get a message back from the server, we strip() them and just use the NEWLINE automatically provided by the print statement.

16.4.3. Executing our TCP Server and Client(s)

Here is the output of our SocketServer TCP client:

     $ tsTclntSS.py      > 'Tis but a scratch.      [Tue Apr 18 20:55:49 2006] 'Tis but a scratch.      > Just a flesh wound.      [Tue Apr 18 20:55:56 2006] Just a flesh wound.      >      $


And here is the server's:

    $ tsTservSS.py     waiting for connection...     ...connected from: ('127.0.0.1', 53476)     ...connected from: ('127.0.0.1', 53477)


The output is similar to that of our original TCP client and servers, however, you will notice that we connected to the server twice.



Core Python Programming
Core Python Programming (2nd Edition)
ISBN: 0132269937
EAN: 2147483647
Year: 2004
Pages: 334
Authors: Wesley J Chun

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net