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.
63 from errno
import EALREADY
, EINPROGRESS
, EWOULDBLOCK
, ECONNRESET
, ENOTCONN
, ESHUTDOWN
70 class ExitNow (exceptions
.Exception):
75 def poll (timeout
=0.0, map=None):
80 r
= []; w
= []; e
= []
81 for fd
, obj
in map.items():
86 r
,w
,e
= select
.select (r
,w
,e
, timeout
)
95 obj
.handle_read_event()
107 obj
.handle_write_event()
115 def poll2 (timeout
=0.0, map=None):
119 # timeout is in milliseconds
120 timeout
= int(timeout
*1000)
123 for fd
, obj
in map.items():
128 flags
= flags | poll
.POLLOUT
130 l
.append ((fd
, flags
))
131 r
= poll
.poll (l
, timeout
)
136 if (flags
& poll
.POLLIN
):
137 obj
.handle_read_event()
138 if (flags
& poll
.POLLOUT
):
139 obj
.handle_write_event()
147 def poll3 (timeout
=0.0, map=None):
148 # Use the poll() support added to the select module in Python 2.0
151 # timeout is in milliseconds
152 timeout
= int(timeout
*1000)
153 pollster
= select
.poll()
156 for fd
, obj
in map.items():
159 flags
= select
.POLLIN
161 flags
= flags | select
.POLLOUT
163 pollster
.register(fd
, flags
)
164 r
= pollster
.poll (timeout
)
169 if (flags
& select
.POLLIN
):
170 obj
.handle_read_event()
171 if (flags
& select
.POLLOUT
):
172 obj
.handle_write_event()
180 def loop (timeout
=30.0, use_poll
=0, map=None):
186 if hasattr (select
, 'poll'):
194 poll_fun (timeout
, map)
203 def __init__ (self
, sock
=None, map=None):
205 self
.set_socket (sock
, map)
206 # I think it should inherit this anyway
207 self
.socket
.setblocking (0)
213 if self
.accepting
and self
.addr
:
214 status
.append ('listening')
216 status
.append ('connected')
218 status
.append ('%s:%d' % self
.addr
)
219 return '<%s %s at %x>' % (
220 self
.__class
__.__name
__,
230 return '<__repr__ (self) failed for object at %x (addr=%s)>' % (id(self
),ar
)
232 def add_channel (self
, map=None):
233 #self.log_info ('adding channel %s' % self)
236 map [self
._fileno
] = self
238 def del_channel (self
, map=None):
243 #self.log_info ('closing channel %d:%s' % (fd, self))
246 def create_socket (self
, family
, type):
247 self
.family_and_type
= family
, type
248 self
.socket
= socket
.socket (family
, type)
249 self
.socket
.setblocking(0)
250 self
._fileno
= self
.socket
.fileno()
253 def set_socket (self
, sock
, map=None):
254 self
.__dict
__['socket'] = sock
255 self
._fileno
= sock
.fileno()
256 self
.add_channel (map)
258 def set_reuse_addr (self
):
259 # try to re-use a server port if possible
261 self
.socket
.setsockopt (
262 socket
.SOL_SOCKET
, socket
.SO_REUSEADDR
,
263 self
.socket
.getsockopt (socket
.SOL_SOCKET
, socket
.SO_REUSEADDR
) |
1
268 # ==================================================
269 # predicates for select()
270 # these are used as filters for the lists of sockets
271 # to pass to select().
272 # ==================================================
278 # The macintosh will select a listening socket for
279 # write if you let it. What might this mean?
281 return not self
.accepting
286 # ==================================================
287 # socket object methods.
288 # ==================================================
290 def listen (self
, num
):
292 if os
.name
== 'nt' and num
> 5:
294 return self
.socket
.listen (num
)
296 def bind (self
, addr
):
298 return self
.socket
.bind (addr
)
300 def connect (self
, address
):
303 self
.socket
.connect (address
)
304 except socket
.error
, why
:
305 if why
[0] in (EINPROGRESS
, EALREADY
, EWOULDBLOCK
):
308 raise socket
.error
, why
310 self
.handle_connect()
314 conn
, addr
= self
.socket
.accept()
316 except socket
.error
, why
:
317 if why
[0] == EWOULDBLOCK
:
320 raise socket
.error
, why
322 def send (self
, data
):
324 result
= self
.socket
.send (data
)
326 except socket
.error
, why
:
327 if why
[0] == EWOULDBLOCK
:
330 raise socket
.error
, why
333 def recv (self
, buffer_size
):
335 data
= self
.socket
.recv (buffer_size
)
337 # a closed connection is indicated by signaling
338 # a read condition, and having recv() return 0.
343 except socket
.error
, why
:
344 # winsock sometimes throws ENOTCONN
345 if why
[0] in [ECONNRESET
, ENOTCONN
, ESHUTDOWN
]:
349 raise socket
.error
, why
355 # cheap inheritance, used to pass all other attribute
356 # references to the underlying socket object.
357 def __getattr__ (self
, attr
):
358 return getattr (self
.socket
, attr
)
360 # log and log_info maybe overriden to provide more sophisitcated
361 # logging and warning methods. In general, log is for 'hit' logging
362 # and 'log_info' is for informational, warning and error logging.
364 def log (self
, message
):
365 sys
.stderr
.write ('log: %s\n' % str(message
))
367 def log_info (self
, message
, type='info'):
368 if __debug__
or type != 'info':
369 print '%s: %s' % (type, message
)
371 def handle_read_event (self
):
373 # for an accepting socket, getting a read implies
374 # that we are connected
375 if not self
.connected
:
378 elif not self
.connected
:
379 self
.handle_connect()
385 def handle_write_event (self
):
386 # getting a write implies that we are connected
387 if not self
.connected
:
388 self
.handle_connect()
392 def handle_expt_event (self
):
395 def handle_error (self
):
396 (file,fun
,line
), t
, v
, tbinfo
= compact_traceback()
398 # sometimes a user repr method will crash.
400 self_repr
= repr (self
)
402 self_repr
= '<__repr__ (self) failed for object at %0x>' % id(self
)
405 'uncaptured python exception, closing channel %s (%s:%s %s)' % (
415 def handle_expt (self
):
416 self
.log_info ('unhandled exception', 'warning')
418 def handle_read (self
):
419 self
.log_info ('unhandled read event', 'warning')
421 def handle_write (self
):
422 self
.log_info ('unhandled write event', 'warning')
424 def handle_connect (self
):
425 self
.log_info ('unhandled connect event', 'warning')
427 def handle_accept (self
):
428 self
.log_info ('unhandled accept event', 'warning')
430 def handle_close (self
):
431 self
.log_info ('unhandled close event', 'warning')
434 # ---------------------------------------------------------------------------
435 # adds simple buffered output capability, useful for simple clients.
436 # [for more sophisticated usage use asynchat.async_chat]
437 # ---------------------------------------------------------------------------
439 class dispatcher_with_send (dispatcher
):
440 def __init__ (self
, sock
=None):
441 dispatcher
.__init
__ (self
, sock
)
444 def initiate_send (self
):
446 num_sent
= dispatcher
.send (self
, self
.out_buffer
[:512])
447 self
.out_buffer
= self
.out_buffer
[num_sent
:]
449 def handle_write (self
):
453 return (not self
.connected
) or len(self
.out_buffer
)
455 def send (self
, data
):
457 self
.log_info ('sending %s' % repr(data
))
458 self
.out_buffer
= self
.out_buffer
+ data
461 # ---------------------------------------------------------------------------
462 # used for debugging.
463 # ---------------------------------------------------------------------------
465 def compact_traceback ():
466 t
,v
,tb
= sys
.exc_info()
470 tb
.tb_frame
.f_code
.co_filename
,
471 tb
.tb_frame
.f_code
.co_name
,
481 file, function
, line
= tbinfo
[-1]
482 info
= '[' + '] ['.join(map(lambda x
: '|'.join(x
), tbinfo
)) + ']'
483 return (file, function
, line
), t
, v
, info
485 def close_all (map=None):
488 for x
in map.values():
492 # Asynchronous File I/O:
494 # After a little research (reading man pages on various unixen, and
495 # digging through the linux kernel), I've determined that select()
496 # isn't meant for doing doing asynchronous file i/o.
497 # Heartening, though - reading linux/mm/filemap.c shows that linux
498 # supports asynchronous read-ahead. So _MOST_ of the time, the data
499 # will be sitting in memory for us already when we go to read it.
501 # What other OS's (besides NT) support async file i/o? [VMS?]
503 # Regardless, this is useful for pipes, and stdin/stdout...
506 if os
.name
== 'posix':
511 # here we override just enough to make a file
512 # look like a socket for the purposes of asyncore.
513 def __init__ (self
, fd
):
516 def recv (self
, *args
):
517 return apply (os
.read
, (self
.fd
,)+args
)
519 def send (self
, *args
):
520 return apply (os
.write
, (self
.fd
,)+args
)
526 return os
.close (self
.fd
)
531 class file_dispatcher (dispatcher
):
532 def __init__ (self
, fd
):
533 dispatcher
.__init
__ (self
)
535 # set it to non-blocking mode
536 flags
= fcntl
.fcntl (fd
, FCNTL
.F_GETFL
, 0)
537 flags
= flags | FCNTL
.O_NONBLOCK
538 fcntl
.fcntl (fd
, FCNTL
.F_SETFL
, flags
)
541 def set_file (self
, fd
):
543 self
.socket
= file_wrapper (fd
)