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