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.
55 from errno
import EALREADY
, EINPROGRESS
, EWOULDBLOCK
, ECONNRESET
, \
56 ENOTCONN
, ESHUTDOWN
, EINTR
63 class ExitNow (exceptions
.Exception):
68 def poll (timeout
=0.0, map=None):
73 r
= []; w
= []; e
= []
74 for fd
, obj
in map.items():
80 r
,w
,e
= select
.select (r
,w
,e
, timeout
)
81 except select
.error
, err
:
92 obj
.handle_read_event()
104 obj
.handle_write_event()
112 def poll2 (timeout
=0.0, map=None):
116 # timeout is in milliseconds
117 timeout
= int(timeout
*1000)
120 for fd
, obj
in map.items():
125 flags
= flags | poll
.POLLOUT
127 l
.append ((fd
, flags
))
128 r
= poll
.poll (l
, timeout
)
133 if (flags
& poll
.POLLIN
):
134 obj
.handle_read_event()
135 if (flags
& poll
.POLLOUT
):
136 obj
.handle_write_event()
144 def poll3 (timeout
=0.0, map=None):
145 # Use the poll() support added to the select module in Python 2.0
148 # timeout is in milliseconds
149 timeout
= int(timeout
*1000)
150 pollster
= select
.poll()
153 for fd
, obj
in map.items():
156 flags
= select
.POLLIN
158 flags
= flags | select
.POLLOUT
160 pollster
.register(fd
, flags
)
162 r
= pollster
.poll (timeout
)
163 except select
.error
, err
:
171 if (flags
& select
.POLLIN
):
172 obj
.handle_read_event()
173 if (flags
& select
.POLLOUT
):
174 obj
.handle_write_event()
182 def loop (timeout
=30.0, use_poll
=0, map=None):
188 if hasattr (select
, 'poll'):
196 poll_fun (timeout
, map)
205 def __init__ (self
, sock
=None, map=None):
207 self
.set_socket (sock
, map)
208 # I think it should inherit this anyway
209 self
.socket
.setblocking (0)
215 if self
.accepting
and self
.addr
:
216 status
.append ('listening')
218 status
.append ('connected')
220 status
.append ('%s:%d' % self
.addr
)
221 return '<%s %s at %x>' % (
222 self
.__class
__.__name
__,
232 return '<__repr__ (self) failed for object at %x (addr=%s)>' % (id(self
),ar
)
234 def add_channel (self
, map=None):
235 #self.log_info ('adding channel %s' % self)
238 map [self
._fileno
] = self
240 def del_channel (self
, map=None):
245 #self.log_info ('closing channel %d:%s' % (fd, self))
248 def create_socket (self
, family
, type):
249 self
.family_and_type
= family
, type
250 self
.socket
= socket
.socket (family
, type)
251 self
.socket
.setblocking(0)
252 self
._fileno
= self
.socket
.fileno()
255 def set_socket (self
, sock
, map=None):
256 self
.__dict
__['socket'] = sock
257 self
._fileno
= sock
.fileno()
258 self
.add_channel (map)
260 def set_reuse_addr (self
):
261 # try to re-use a server port if possible
263 self
.socket
.setsockopt (
264 socket
.SOL_SOCKET
, socket
.SO_REUSEADDR
,
265 self
.socket
.getsockopt (socket
.SOL_SOCKET
,
266 socket
.SO_REUSEADDR
) |
1
271 # ==================================================
272 # predicates for select()
273 # these are used as filters for the lists of sockets
274 # to pass to select().
275 # ==================================================
281 # The macintosh will select a listening socket for
282 # write if you let it. What might this mean?
284 return not self
.accepting
289 # ==================================================
290 # socket object methods.
291 # ==================================================
293 def listen (self
, num
):
295 if os
.name
== 'nt' and num
> 5:
297 return self
.socket
.listen (num
)
299 def bind (self
, addr
):
301 return self
.socket
.bind (addr
)
303 def connect (self
, address
):
306 self
.socket
.connect (address
)
307 except socket
.error
, why
:
308 if why
[0] in (EINPROGRESS
, EALREADY
, EWOULDBLOCK
):
311 raise socket
.error
, why
313 self
.handle_connect()
317 conn
, addr
= self
.socket
.accept()
319 except socket
.error
, why
:
320 if why
[0] == EWOULDBLOCK
:
323 raise socket
.error
, why
325 def send (self
, data
):
327 result
= self
.socket
.send (data
)
329 except socket
.error
, why
:
330 if why
[0] == EWOULDBLOCK
:
333 raise socket
.error
, why
336 def recv (self
, buffer_size
):
338 data
= self
.socket
.recv (buffer_size
)
340 # a closed connection is indicated by signaling
341 # a read condition, and having recv() return 0.
346 except socket
.error
, why
:
347 # winsock sometimes throws ENOTCONN
348 if why
[0] in [ECONNRESET
, ENOTCONN
, ESHUTDOWN
]:
352 raise socket
.error
, why
358 # cheap inheritance, used to pass all other attribute
359 # references to the underlying socket object.
360 def __getattr__ (self
, attr
):
361 return getattr (self
.socket
, attr
)
363 # log and log_info maybe overriden to provide more sophisitcated
364 # logging and warning methods. In general, log is for 'hit' logging
365 # and 'log_info' is for informational, warning and error logging.
367 def log (self
, message
):
368 sys
.stderr
.write ('log: %s\n' % str(message
))
370 def log_info (self
, message
, type='info'):
371 if __debug__
or type != 'info':
372 print '%s: %s' % (type, message
)
374 def handle_read_event (self
):
376 # for an accepting socket, getting a read implies
377 # that we are connected
378 if not self
.connected
:
381 elif not self
.connected
:
382 self
.handle_connect()
388 def handle_write_event (self
):
389 # getting a write implies that we are connected
390 if not self
.connected
:
391 self
.handle_connect()
395 def handle_expt_event (self
):
398 def handle_error (self
):
399 nil
, t
, v
, tbinfo
= compact_traceback()
401 # sometimes a user repr method will crash.
403 self_repr
= repr (self
)
405 self_repr
= '<__repr__ (self) failed for object at %0x>' % id(self
)
408 'uncaptured python exception, closing channel %s (%s:%s %s)' % (
418 def handle_expt (self
):
419 self
.log_info ('unhandled exception', 'warning')
421 def handle_read (self
):
422 self
.log_info ('unhandled read event', 'warning')
424 def handle_write (self
):
425 self
.log_info ('unhandled write event', 'warning')
427 def handle_connect (self
):
428 self
.log_info ('unhandled connect event', 'warning')
430 def handle_accept (self
):
431 self
.log_info ('unhandled accept event', 'warning')
433 def handle_close (self
):
434 self
.log_info ('unhandled close event', 'warning')
437 # ---------------------------------------------------------------------------
438 # adds simple buffered output capability, useful for simple clients.
439 # [for more sophisticated usage use asynchat.async_chat]
440 # ---------------------------------------------------------------------------
442 class dispatcher_with_send (dispatcher
):
443 def __init__ (self
, sock
=None):
444 dispatcher
.__init
__ (self
, sock
)
447 def initiate_send (self
):
449 num_sent
= dispatcher
.send (self
, self
.out_buffer
[:512])
450 self
.out_buffer
= self
.out_buffer
[num_sent
:]
452 def handle_write (self
):
456 return (not self
.connected
) or len(self
.out_buffer
)
458 def send (self
, data
):
460 self
.log_info ('sending %s' % repr(data
))
461 self
.out_buffer
= self
.out_buffer
+ data
464 # ---------------------------------------------------------------------------
465 # used for debugging.
466 # ---------------------------------------------------------------------------
468 def compact_traceback ():
469 t
,v
,tb
= sys
.exc_info()
473 tb
.tb_frame
.f_code
.co_filename
,
474 tb
.tb_frame
.f_code
.co_name
,
484 file, function
, line
= tbinfo
[-1]
485 info
= '[' + '] ['.join(map(lambda x
: '|'.join(x
), tbinfo
)) + ']'
486 return (file, function
, line
), t
, v
, info
488 def close_all (map=None):
491 for x
in map.values():
495 # Asynchronous File I/O:
497 # After a little research (reading man pages on various unixen, and
498 # digging through the linux kernel), I've determined that select()
499 # isn't meant for doing doing asynchronous file i/o.
500 # Heartening, though - reading linux/mm/filemap.c shows that linux
501 # supports asynchronous read-ahead. So _MOST_ of the time, the data
502 # will be sitting in memory for us already when we go to read it.
504 # What other OS's (besides NT) support async file i/o? [VMS?]
506 # Regardless, this is useful for pipes, and stdin/stdout...
509 if os
.name
== 'posix':
514 # here we override just enough to make a file
515 # look like a socket for the purposes of asyncore.
516 def __init__ (self
, fd
):
519 def recv (self
, *args
):
520 return apply (os
.read
, (self
.fd
,)+args
)
522 def send (self
, *args
):
523 return apply (os
.write
, (self
.fd
,)+args
)
529 return os
.close (self
.fd
)
534 class file_dispatcher (dispatcher
):
535 def __init__ (self
, fd
):
536 dispatcher
.__init
__ (self
)
538 # set it to non-blocking mode
539 flags
= fcntl
.fcntl (fd
, FCNTL
.F_GETFL
, 0)
540 flags
= flags | FCNTL
.O_NONBLOCK
541 fcntl
.fcntl (fd
, FCNTL
.F_SETFL
, flags
)
544 def set_file (self
, fd
):
546 self
.socket
= file_wrapper (fd
)