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.
56 from errno
import EALREADY
, EINPROGRESS
, EWOULDBLOCK
, ECONNRESET
, \
64 class ExitNow (exceptions
.Exception):
69 def poll (timeout
=0.0, map=None):
74 r
= []; w
= []; e
= []
75 for fd
, obj
in map.items():
80 r
,w
,e
= select
.select (r
,w
,e
, timeout
)
89 obj
.handle_read_event()
101 obj
.handle_write_event()
109 def poll2 (timeout
=0.0, map=None):
113 # timeout is in milliseconds
114 timeout
= int(timeout
*1000)
117 for fd
, obj
in map.items():
122 flags
= flags | poll
.POLLOUT
124 l
.append ((fd
, flags
))
125 r
= poll
.poll (l
, timeout
)
130 if (flags
& poll
.POLLIN
):
131 obj
.handle_read_event()
132 if (flags
& poll
.POLLOUT
):
133 obj
.handle_write_event()
141 def poll3 (timeout
=0.0, map=None):
142 # Use the poll() support added to the select module in Python 2.0
145 # timeout is in milliseconds
146 timeout
= int(timeout
*1000)
147 pollster
= select
.poll()
150 for fd
, obj
in map.items():
153 flags
= select
.POLLIN
155 flags
= flags | select
.POLLOUT
157 pollster
.register(fd
, flags
)
158 r
= pollster
.poll (timeout
)
163 if (flags
& select
.POLLIN
):
164 obj
.handle_read_event()
165 if (flags
& select
.POLLOUT
):
166 obj
.handle_write_event()
174 def loop (timeout
=30.0, use_poll
=0, map=None):
180 if hasattr (select
, 'poll'):
188 poll_fun (timeout
, map)
197 def __init__ (self
, sock
=None, map=None):
199 self
.set_socket (sock
, map)
200 # I think it should inherit this anyway
201 self
.socket
.setblocking (0)
207 if self
.accepting
and self
.addr
:
208 status
.append ('listening')
210 status
.append ('connected')
212 if self
.addr
== types
.TupleType
:
213 status
.append ('%s:%d' % self
.addr
)
215 status
.append (self
.addr
)
216 return '<%s %s at %x>' % (self
.__class
__.__name
__,
217 ' '.join (status
), id (self
))
222 ar
= repr (self
.addr
)
223 except AttributeError:
226 return '<__repr__() failed for %s instance at %x (addr=%s)>' % \
227 (self
.__class
__.__name
__, id (self
), ar
)
229 def add_channel (self
, map=None):
230 #self.log_info ('adding channel %s' % self)
233 map [self
._fileno
] = self
235 def del_channel (self
, map=None):
240 #self.log_info ('closing channel %d:%s' % (fd, self))
243 def create_socket (self
, family
, type):
244 self
.family_and_type
= family
, type
245 self
.socket
= socket
.socket (family
, type)
246 self
.socket
.setblocking(0)
247 self
._fileno
= self
.socket
.fileno()
250 def set_socket (self
, sock
, map=None):
251 self
.__dict
__['socket'] = sock
252 self
._fileno
= sock
.fileno()
253 self
.add_channel (map)
255 def set_reuse_addr (self
):
256 # try to re-use a server port if possible
258 self
.socket
.setsockopt (
259 socket
.SOL_SOCKET
, socket
.SO_REUSEADDR
,
260 self
.socket
.getsockopt (socket
.SOL_SOCKET
,
261 socket
.SO_REUSEADDR
) |
1
266 # ==================================================
267 # predicates for select()
268 # these are used as filters for the lists of sockets
269 # to pass to select().
270 # ==================================================
276 # The macintosh will select a listening socket for
277 # write if you let it. What might this mean?
279 return not self
.accepting
284 # ==================================================
285 # socket object methods.
286 # ==================================================
288 def listen (self
, num
):
290 if os
.name
== 'nt' and num
> 5:
292 return self
.socket
.listen (num
)
294 def bind (self
, addr
):
296 return self
.socket
.bind (addr
)
298 def connect (self
, address
):
300 # XXX why not use connect_ex?
302 self
.socket
.connect (address
)
303 except socket
.error
, why
:
304 if why
[0] in (EINPROGRESS
, EALREADY
, EWOULDBLOCK
):
307 raise socket
.error
, why
309 self
.handle_connect()
313 conn
, addr
= self
.socket
.accept()
315 except socket
.error
, why
:
316 if why
[0] == EWOULDBLOCK
:
319 raise socket
.error
, why
321 def send (self
, data
):
323 result
= self
.socket
.send (data
)
325 except socket
.error
, why
:
326 if why
[0] == EWOULDBLOCK
:
329 raise socket
.error
, why
332 def recv (self
, buffer_size
):
334 data
= self
.socket
.recv (buffer_size
)
336 # a closed connection is indicated by signaling
337 # a read condition, and having recv() return 0.
342 except socket
.error
, why
:
343 # winsock sometimes throws ENOTCONN
344 if why
[0] in [ECONNRESET
, ENOTCONN
, ESHUTDOWN
]:
348 raise socket
.error
, why
354 # cheap inheritance, used to pass all other attribute
355 # references to the underlying socket object.
356 def __getattr__ (self
, attr
):
357 return getattr (self
.socket
, attr
)
359 # log and log_info maybe overriden to provide more sophisitcated
360 # logging and warning methods. In general, log is for 'hit' logging
361 # and 'log_info' is for informational, warning and error logging.
363 def log (self
, message
):
364 sys
.stderr
.write ('log: %s\n' % str(message
))
366 def log_info (self
, message
, type='info'):
367 if __debug__
or type != 'info':
368 print '%s: %s' % (type, message
)
370 def handle_read_event (self
):
372 # for an accepting socket, getting a read implies
373 # that we are connected
374 if not self
.connected
:
377 elif not self
.connected
:
378 self
.handle_connect()
384 def handle_write_event (self
):
385 # getting a write implies that we are connected
386 if not self
.connected
:
387 self
.handle_connect()
391 def handle_expt_event (self
):
394 def handle_error (self
):
395 nil
, t
, v
, tbinfo
= compact_traceback()
397 # sometimes a user repr method will crash.
399 self_repr
= repr (self
)
401 self_repr
= '<__repr__ (self) failed for object at %0x>' % id(self
)
404 'uncaptured python exception, closing channel %s (%s:%s %s)' % (
414 def handle_expt (self
):
415 self
.log_info ('unhandled exception', 'warning')
417 def handle_read (self
):
418 self
.log_info ('unhandled read event', 'warning')
420 def handle_write (self
):
421 self
.log_info ('unhandled write event', 'warning')
423 def handle_connect (self
):
424 self
.log_info ('unhandled connect event', 'warning')
426 def handle_accept (self
):
427 self
.log_info ('unhandled accept event', 'warning')
429 def handle_close (self
):
430 self
.log_info ('unhandled close event', 'warning')
433 # ---------------------------------------------------------------------------
434 # adds simple buffered output capability, useful for simple clients.
435 # [for more sophisticated usage use asynchat.async_chat]
436 # ---------------------------------------------------------------------------
438 class dispatcher_with_send (dispatcher
):
439 def __init__ (self
, sock
=None):
440 dispatcher
.__init
__ (self
, sock
)
443 def initiate_send (self
):
445 num_sent
= dispatcher
.send (self
, self
.out_buffer
[:512])
446 self
.out_buffer
= self
.out_buffer
[num_sent
:]
448 def handle_write (self
):
452 return (not self
.connected
) or len(self
.out_buffer
)
454 def send (self
, data
):
456 self
.log_info ('sending %s' % repr(data
))
457 self
.out_buffer
= self
.out_buffer
+ data
460 # ---------------------------------------------------------------------------
461 # used for debugging.
462 # ---------------------------------------------------------------------------
464 def compact_traceback ():
465 t
,v
,tb
= sys
.exc_info()
469 tb
.tb_frame
.f_code
.co_filename
,
470 tb
.tb_frame
.f_code
.co_name
,
480 file, function
, line
= tbinfo
[-1]
481 info
= '[' + '] ['.join(map(lambda x
: '|'.join(x
), tbinfo
)) + ']'
482 return (file, function
, line
), t
, v
, info
484 def close_all (map=None):
487 for x
in map.values():
491 # Asynchronous File I/O:
493 # After a little research (reading man pages on various unixen, and
494 # digging through the linux kernel), I've determined that select()
495 # isn't meant for doing doing asynchronous file i/o.
496 # Heartening, though - reading linux/mm/filemap.c shows that linux
497 # supports asynchronous read-ahead. So _MOST_ of the time, the data
498 # will be sitting in memory for us already when we go to read it.
500 # What other OS's (besides NT) support async file i/o? [VMS?]
502 # Regardless, this is useful for pipes, and stdin/stdout...
505 if os
.name
== 'posix':
509 # here we override just enough to make a file
510 # look like a socket for the purposes of asyncore.
511 def __init__ (self
, fd
):
514 def recv (self
, *args
):
515 return apply (os
.read
, (self
.fd
,)+args
)
517 def send (self
, *args
):
518 return apply (os
.write
, (self
.fd
,)+args
)
524 return os
.close (self
.fd
)
529 class file_dispatcher (dispatcher
):
530 def __init__ (self
, fd
):
531 dispatcher
.__init
__ (self
)
533 # set it to non-blocking mode
534 flags
= fcntl
.fcntl (fd
, fcntl
.F_GETFL
, 0)
535 flags
= flags | os
.O_NONBLOCK
536 fcntl
.fcntl (fd
, fcntl
.F_SETFL
, flags
)
539 def set_file (self
, fd
):
541 self
.socket
= file_wrapper (fd
)