2 # Id: asyncore.py,v 2.51 2000/09/07 22:29:26 rushing Exp
3 # Author: Sam Rushing <rushing@nightmare.com>
5 # ======================================================================
6 # Copyright 1996 by Sam Rushing
10 # Permission to use, copy, modify, and distribute this software and
11 # its documentation for any purpose and without fee is hereby
12 # granted, provided that the above copyright notice appear in all
13 # copies and that both that copyright notice and this permission
14 # notice appear in supporting documentation, and that the name of Sam
15 # Rushing not be used in advertising or publicity pertaining to
16 # distribution of the software without specific, written prior
19 # SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
20 # INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
21 # NO EVENT SHALL SAM RUSHING BE LIABLE FOR ANY SPECIAL, INDIRECT OR
22 # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
23 # OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
24 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
25 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
26 # ======================================================================
28 """Basic infrastructure for asynchronous socket service clients and servers.
30 There are only two ways to have a program on a single processor do "more
31 than one thing at a time". Multi-threaded programming is the simplest and
32 most popular way to do it, but there is another very different technique,
33 that lets you have nearly all the advantages of multi-threading, without
34 actually using multiple threads. it's really only practical if your program
35 is largely I/O bound. If your program is CPU bound, then pre-emptive
36 scheduled threads are probably what you really need. Network servers are
37 rarely CPU-bound, however.
39 If your operating system supports the select() system call in its I/O
40 library (and nearly all do), then you can use it to juggle multiple
41 communication channels at once; doing other work while your I/O is taking
42 place in the "background." Although this strategy can seem strange and
43 complex, especially at first, it is in many ways easier to understand and
44 control than multi-threaded programming. The module documented here solves
45 many of the difficult problems for you, making the task of building
46 sophisticated high-performance network servers and clients a snap.
64 from errno
import EALREADY
, EINPROGRESS
, EWOULDBLOCK
, ECONNRESET
, ENOTCONN
, ESHUTDOWN
71 class ExitNow (exceptions
.Exception):
76 def poll (timeout
=0.0, map=None):
81 r
= []; w
= []; e
= []
82 for fd
, obj
in map.items():
87 r
,w
,e
= select
.select (r
,w
,e
, timeout
)
96 obj
.handle_read_event()
108 obj
.handle_write_event()
116 def poll2 (timeout
=0.0, map=None):
120 # timeout is in milliseconds
121 timeout
= int(timeout
*1000)
124 for fd
, obj
in map.items():
129 flags
= flags | poll
.POLLOUT
131 l
.append ((fd
, flags
))
132 r
= poll
.poll (l
, timeout
)
137 if (flags
& poll
.POLLIN
):
138 obj
.handle_read_event()
139 if (flags
& poll
.POLLOUT
):
140 obj
.handle_write_event()
148 def loop (timeout
=30.0, use_poll
=0, map=None):
159 poll_fun (timeout
, map)
168 def __init__ (self
, sock
=None, map=None):
170 self
.set_socket (sock
, map)
171 # I think it should inherit this anyway
172 self
.socket
.setblocking (0)
178 if self
.accepting
and self
.addr
:
179 status
.append ('listening')
181 status
.append ('connected')
183 status
.append ('%s:%d' % self
.addr
)
184 return '<%s %s at %x>' % (
185 self
.__class
__.__name
__,
186 string
.join (status
, ' '),
195 return '<__repr__ (self) failed for object at %x (addr=%s)>' % (id(self
),ar
)
197 def add_channel (self
, map=None):
198 #self.log_info ('adding channel %s' % self)
201 map [self
._fileno
] = self
203 def del_channel (self
, map=None):
208 #self.log_info ('closing channel %d:%s' % (fd, self))
211 def create_socket (self
, family
, type):
212 self
.family_and_type
= family
, type
213 self
.socket
= socket
.socket (family
, type)
214 self
.socket
.setblocking(0)
215 self
._fileno
= self
.socket
.fileno()
218 def set_socket (self
, sock
, map=None):
219 self
.__dict
__['socket'] = sock
220 self
._fileno
= sock
.fileno()
221 self
.add_channel (map)
223 def set_reuse_addr (self
):
224 # try to re-use a server port if possible
226 self
.socket
.setsockopt (
227 socket
.SOL_SOCKET
, socket
.SO_REUSEADDR
,
228 self
.socket
.getsockopt (socket
.SOL_SOCKET
, socket
.SO_REUSEADDR
) |
1
233 # ==================================================
234 # predicates for select()
235 # these are used as filters for the lists of sockets
236 # to pass to select().
237 # ==================================================
243 # The macintosh will select a listening socket for
244 # write if you let it. What might this mean?
246 return not self
.accepting
251 # ==================================================
252 # socket object methods.
253 # ==================================================
255 def listen (self
, num
):
257 if os
.name
== 'nt' and num
> 5:
259 return self
.socket
.listen (num
)
261 def bind (self
, addr
):
263 return self
.socket
.bind (addr
)
265 def connect (self
, address
):
268 self
.socket
.connect (address
)
269 except socket
.error
, why
:
270 if why
[0] in (EINPROGRESS
, EALREADY
, EWOULDBLOCK
):
273 raise socket
.error
, why
275 self
.handle_connect()
279 conn
, addr
= self
.socket
.accept()
281 except socket
.error
, why
:
282 if why
[0] == EWOULDBLOCK
:
285 raise socket
.error
, why
287 def send (self
, data
):
289 result
= self
.socket
.send (data
)
291 except socket
.error
, why
:
292 if why
[0] == EWOULDBLOCK
:
295 raise socket
.error
, why
298 def recv (self
, buffer_size
):
300 data
= self
.socket
.recv (buffer_size
)
302 # a closed connection is indicated by signaling
303 # a read condition, and having recv() return 0.
308 except socket
.error
, why
:
309 # winsock sometimes throws ENOTCONN
310 if why
[0] in [ECONNRESET
, ENOTCONN
, ESHUTDOWN
]:
314 raise socket
.error
, why
320 # cheap inheritance, used to pass all other attribute
321 # references to the underlying socket object.
322 def __getattr__ (self
, attr
):
323 return getattr (self
.socket
, attr
)
325 # log and log_info maybe overriden to provide more sophisitcated
326 # logging and warning methods. In general, log is for 'hit' logging
327 # and 'log_info' is for informational, warning and error logging.
329 def log (self
, message
):
330 sys
.stderr
.write ('log: %s\n' % str(message
))
332 def log_info (self
, message
, type='info'):
333 if __debug__
or type != 'info':
334 print '%s: %s' % (type, message
)
336 def handle_read_event (self
):
338 # for an accepting socket, getting a read implies
339 # that we are connected
340 if not self
.connected
:
343 elif not self
.connected
:
344 self
.handle_connect()
350 def handle_write_event (self
):
351 # getting a write implies that we are connected
352 if not self
.connected
:
353 self
.handle_connect()
357 def handle_expt_event (self
):
360 def handle_error (self
):
361 (file,fun
,line
), t
, v
, tbinfo
= compact_traceback()
363 # sometimes a user repr method will crash.
365 self_repr
= repr (self
)
367 self_repr
= '<__repr__ (self) failed for object at %0x>' % id(self
)
370 'uncaptured python exception, closing channel %s (%s:%s %s)' % (
380 def handle_expt (self
):
381 self
.log_info ('unhandled exception', 'warning')
383 def handle_read (self
):
384 self
.log_info ('unhandled read event', 'warning')
386 def handle_write (self
):
387 self
.log_info ('unhandled write event', 'warning')
389 def handle_connect (self
):
390 self
.log_info ('unhandled connect event', 'warning')
392 def handle_accept (self
):
393 self
.log_info ('unhandled accept event', 'warning')
395 def handle_close (self
):
396 self
.log_info ('unhandled close event', 'warning')
399 # ---------------------------------------------------------------------------
400 # adds simple buffered output capability, useful for simple clients.
401 # [for more sophisticated usage use asynchat.async_chat]
402 # ---------------------------------------------------------------------------
404 class dispatcher_with_send (dispatcher
):
405 def __init__ (self
, sock
=None):
406 dispatcher
.__init
__ (self
, sock
)
409 def initiate_send (self
):
411 num_sent
= dispatcher
.send (self
, self
.out_buffer
[:512])
412 self
.out_buffer
= self
.out_buffer
[num_sent
:]
414 def handle_write (self
):
418 return (not self
.connected
) or len(self
.out_buffer
)
420 def send (self
, data
):
422 self
.log_info ('sending %s' % repr(data
))
423 self
.out_buffer
= self
.out_buffer
+ data
426 # ---------------------------------------------------------------------------
427 # used for debugging.
428 # ---------------------------------------------------------------------------
430 def compact_traceback ():
431 t
,v
,tb
= sys
.exc_info()
435 tb
.tb_frame
.f_code
.co_filename
,
436 tb
.tb_frame
.f_code
.co_name
,
446 file, function
, line
= tbinfo
[-1]
447 info
= '[' + string
.join (
449 lambda x
: string
.join (x
, '|'),
454 return (file, function
, line
), t
, v
, info
456 def close_all (map=None):
459 for x
in map.values():
463 # Asynchronous File I/O:
465 # After a little research (reading man pages on various unixen, and
466 # digging through the linux kernel), I've determined that select()
467 # isn't meant for doing doing asynchronous file i/o.
468 # Heartening, though - reading linux/mm/filemap.c shows that linux
469 # supports asynchronous read-ahead. So _MOST_ of the time, the data
470 # will be sitting in memory for us already when we go to read it.
472 # What other OS's (besides NT) support async file i/o? [VMS?]
474 # Regardless, this is useful for pipes, and stdin/stdout...
477 if os
.name
== 'posix':
482 # here we override just enough to make a file
483 # look like a socket for the purposes of asyncore.
484 def __init__ (self
, fd
):
487 def recv (self
, *args
):
488 return apply (os
.read
, (self
.fd
,)+args
)
490 def send (self
, *args
):
491 return apply (os
.write
, (self
.fd
,)+args
)
497 return os
.close (self
.fd
)
502 class file_dispatcher (dispatcher
):
503 def __init__ (self
, fd
):
504 dispatcher
.__init
__ (self
)
506 # set it to non-blocking mode
507 flags
= fcntl
.fcntl (fd
, FCNTL
.F_GETFL
, 0)
508 flags
= flags | FCNTL
.O_NONBLOCK
509 fcntl
.fcntl (fd
, FCNTL
.F_SETFL
, flags
)
512 def set_file (self
, fd
):
514 self
.socket
= file_wrapper (fd
)