1 """
  2 __version__ = '$Revision: 1.1 $'
  3 __date__    = '$Date: 2004-11-24 20:15:11 +0000 (Wed, 24 Nov 2004) $'
  4 __author__  = 'Robin Munn, Peter Arwanitis'
  5 __license__ = 'pyBlaster is under LGPL, see http://www.xmlBlaster.org/license.html'
  6 
  7 last change by $Author: ruff $ 
  8 
  9 """
 10 
 11 """
 12 Found in thread:
 13 http://groups.google.com/groups?hl=de&lr=&ie=UTF-8&threadm=slrna3pabh.1rq.rmunn%40rmunn.dyndns.org&rnum=1&prev=/groups%3Fhl%3Dde%26lr%3D%26ie%3DUTF-8%26selm%3Dslrna3pabh.1rq.rmunn%2540rmunn.dyndns.org%26rnum%3D1
 14 
 15 I've been playing around with the SocketServer module recently. It
 16 provides some useful classes for building various kinds of servers. But
 17 there's one feature I wanted that didn't seem available. I wanted to be
 18 able to kill the server externally at any time, without using kill -9 or
 19 anything that relied on Unix signals, since I also needed to be able to
 20 run this under Windows. After a little bit of pondering, I came up with
 21 the following solution:
 22 
 23 1) Subclass the ThreadingTCPServer class so that instead of calling
 24    socket.accept() and blocking until a connection comes in, it will
 25    call select.select() with a timeout; at the end of the timeout, it
 26    will check a "Quit now?" flag and if the flag is set, will close the
 27    connection and exit. If the quit flag is not set, go back into the
 28    select.select() for another N seconds.
 29 
 30 2) A "master control thread" launches an instance of the
 31    ThreadingTCPServer subclass, then goes and blocks on some event
 32    (which could be anything from just pressing Return on the console the
 33    server was run from, as in the simplified example below, to receiving
 34    a signal from the Windows NT service manager, to something else).
 35    Once that event takes place, it sets the quit flag on the server
 36    thread and then join()s the thread, which will exit as soon as the
 37    next select.select() call returns.
 38 
 39 3) If the Master Control Thread ever gets out of hand, a "Tron" thread
 40    will activate and kill it. (Just kidding). :)
 41 
 42 A fully-functional example is below. Any comments? I'd be especially
 43 interested if you know a better way of doing what I was trying to do
 44 (stay responsive to "quit now" commands from an external source without
 45 completely re-inventing the wheel).
 46 
 47 -- 
 48 Robin Munn
 49 rmunn@pobox.com
 50 
 51 
 52 ----- begin code -----
 53 """
 54 # Witty socket server
 55 
 56 # Note: In subclassing ThreadingTCPServer, try overriding get_request()
 57 #       to select() on the single socket for a certain timeout period,
 58 #       then poll the quit flag at the end of that timeout before
 59 #       returning
 60 #       to the select(). That should provide a good balance between
 61 #       blocking
 62 #       to keep CPU usage down and speed of response to a shutdown
 63 #       request.
 64 
 65 import os, sys
 66 
 67 import socket
 68 import SocketServer
 69 import time
 70 import threading
 71 import select
 72 
 73 
 74 class TimeToQuit(Exception):
 75     pass
 76 
 77 
 78 class ResponsiveThreadingTCPServer(SocketServer.ThreadingTCPServer):
 79     def __init__(self, server_address, RequestHandlerClass, lock, timeout = 5.0):
 80         SocketServer.ThreadingTCPServer.__init__(self, server_address, RequestHandlerClass)
 81         self.timeout = timeout  # Default timeout: 5.0 seconds
 82         self.lock = lock        # Should be a preexisting threading.RLock() object
 83         self.lock.acquire()
 84         self.QuitFlag = 0
 85         self.lock.release()
 86 
 87     def get_request(self):
 88         socklist = [self.socket]
 89         while 1:
 90             # Select with a timeout, then poll a quit flag. An alternate
 91             # approach would be to let the master thread "wake us up"
 92             # with
 93             # a socket connection.
 94             ready = select.select(socklist, [], [], self.timeout)
 95             self.lock.acquire()
 96             time_to_quit = self.QuitFlag
 97             self.lock.release()
 98             if time_to_quit:
 99                 raise TimeToQuit        # Get out now
100             if ready[0]:        # A socket was ready to read
101                 return SocketServer.ThreadingTCPServer.get_request(self)
102             else:               # We timed out, no connection yet
103                 pass            # Just go back to the select()
104 
105     def serve_forever(self):
106         try:
107             SocketServer.ThreadingTCPServer.serve_forever(self)
108         except TimeToQuit:
109             self.server_close() # Clean up before we leave
110 
111 
112 
113 #
114 # Test the ResponsiveThreadingTCPServer
115 #
116 
117 class CommandHandler(SocketServer.StreamRequestHandler):
118     def handle(self):
119         self.requestline = self.rfile.readline()
120         while self.requestline[-1] in ['\r', '\n']:
121             self.requestline = self.requestline[:-1]   # Strip trailing CR/LF if any
122         command = 'do_' + self.requestline.upper()
123         if not hasattr(self, command):
124             self.send("Unknown command:")
125             self.send(self.requestline)
126             return
127         method = getattr(self, command)
128         method()
129 
130     def send(self, text):
131         self.wfile.write(text)
132         self.wfile.flush()
133 
134     def do_SONG(self):
135         self.send('Old McDonald had a farm, E-I-E-I-O!\r\n')
136 
137     def do_QUOTE(self):
138         self.send('And now for something completely different...\r\n')
139 
140     def do_POEM(self):
141         self.do_HAIKU()
142 
143     def do_HAIKU(self):
144         self.send('A haiku on "Error 404"\r\n')
145         self.send('\r\n')
146         self.send('  You step in the stream,\r\n')
147         self.send('  But the water has moved on.\r\n')
148         self.send('  This file is not here.\r\n')
149 
150 #
151 # Pattern of a master thread
152 #
153 
154 class MasterControlThread(threading.Thread):
155     def __init__(self, port=31310):
156         threading.Thread.__init__(self)
157         self.port = port
158         self.lock = threading.RLock()
159 
160     def run(self):
161         print "Serving on port", self.port
162         self.server = ResponsiveThreadingTCPServer(('', self.port), CommandHandler, self.lock)
163         # Note: Five seconds timeout instead of a minute, for testing.
164         self.thread = threading.Thread(target = self.server.serve_forever)
165         self.thread.start()
166         print "Press Enter to quit."
167         raw_input()
168         # Tell the server it's time to shut down
169         self.server.lock.acquire()
170         self.server.QuitFlag = 1
171         self.server.lock.release()
172         print "Waiting for server to shut down (could take several seconds)..."
173         self.thread.join()
174         print "Exiting now."
175 
176 
177 if __name__ == '__main__':
178     mct = MasterControlThread()
179     mct.start()
180     mct.join()


syntax highlighted by Code2HTML, v. 0.9.1