1 # -*- Mode: Python; tab-width: 4 -*-
2 # Id: asyncore.py,v 2.40 1999/05/27 04:08:25 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
67 def poll (timeout
=0.0):
69 r
= []; w
= []; e
= []
70 for s
in socket_map
.keys():
76 (r
,w
,e
) = select
.select (r
,w
,e
, timeout
)
85 x
.handle_write_event()
89 def poll2 (timeout
=0.0):
91 # timeout is in milliseconds
92 timeout
= int(timeout
*1000)
95 for s
in socket_map
.keys():
96 fd_map
[s
.fileno()] = s
98 for fd
, s
in fd_map
.items():
103 flags
= flags | poll
.POLLOUT
105 l
.append ((fd
, flags
))
106 r
= poll
.poll (l
, timeout
)
110 if (flags
& poll
.POLLIN
):
111 s
.handle_read_event()
112 if (flags
& poll
.POLLOUT
):
113 s
.handle_write_event()
114 if (flags
& poll
.POLLERR
):
115 s
.handle_expt_event()
120 def loop (timeout
=30.0, use_poll
=0):
137 def __init__ (self
, sock
=None):
139 self
.set_socket (sock
)
140 # I think it should inherit this anyway
141 self
.socket
.setblocking (0)
147 if self
.accepting
and self
.addr
:
148 status
.append ('listening')
150 status
.append ('connected')
152 status
.append ('%s:%d' % self
.addr
)
153 return '<%s %s at %x>' % (
154 self
.__class
__.__name
__,
155 string
.join (status
, ' '),
164 return '<__repr__ (self) failed for object at %x (addr=%s)>' % (id(self
),ar
)
166 def add_channel (self
):
168 self
.log ('adding channel %s' % self
)
169 socket_map
[self
] = 1
171 def del_channel (self
):
172 if socket_map
.has_key (self
):
174 self
.log ('closing channel %d:%s' % (self
.fileno(), self
))
175 del socket_map
[self
]
177 def create_socket (self
, family
, type):
178 self
.family_and_type
= family
, type
179 self
.socket
= socket
.socket (family
, type)
180 self
.socket
.setblocking(0)
183 def set_socket (self
, socket
):
184 # This is done so we can be called safely from __init__
185 self
.__dict
__['socket'] = socket
188 def set_reuse_addr (self
):
189 # try to re-use a server port if possible
191 self
.socket
.setsockopt (
192 socket
.SOL_SOCKET
, socket
.SO_REUSEADDR
,
193 self
.socket
.getsockopt (socket
.SOL_SOCKET
, socket
.SO_REUSEADDR
) |
1
198 # ==================================================
199 # predicates for select()
200 # these are used as filters for the lists of sockets
201 # to pass to select().
202 # ==================================================
208 # The macintosh will select a listening socket for
209 # write if you let it. What might this mean?
211 return not self
.accepting
216 # ==================================================
217 # socket object methods.
218 # ==================================================
220 def listen (self
, num
):
222 if os
.name
== 'nt' and num
> 5:
224 return self
.socket
.listen (num
)
226 def bind (self
, addr
):
228 return self
.socket
.bind (addr
)
230 def connect (self
, address
):
233 self
.socket
.connect (address
)
234 except socket
.error
, why
:
235 if why
[0] in (EINPROGRESS
, EALREADY
, EWOULDBLOCK
):
238 raise socket
.error
, why
240 self
.handle_connect()
244 conn
, addr
= self
.socket
.accept()
246 except socket
.error
, why
:
247 if why
[0] == EWOULDBLOCK
:
250 raise socket
.error
, why
252 def send (self
, data
):
254 result
= self
.socket
.send (data
)
256 except socket
.error
, why
:
257 if why
[0] == EWOULDBLOCK
:
260 raise socket
.error
, why
263 def recv (self
, buffer_size
):
265 data
= self
.socket
.recv (buffer_size
)
267 # a closed connection is indicated by signaling
268 # a read condition, and having recv() return 0.
273 except socket
.error
, why
:
274 # winsock sometimes throws ENOTCONN
275 if why
[0] in [ECONNRESET
, ENOTCONN
, ESHUTDOWN
]:
279 raise socket
.error
, why
285 # cheap inheritance, used to pass all other attribute
286 # references to the underlying socket object.
287 # NOTE: this may be removed soon for performance reasons.
288 def __getattr__ (self
, attr
):
289 return getattr (self
.socket
, attr
)
291 def log (self
, message
):
292 print 'log:', message
294 def handle_read_event (self
):
296 # for an accepting socket, getting a read implies
297 # that we are connected
298 if not self
.connected
:
301 elif not self
.connected
:
302 self
.handle_connect()
308 def handle_write_event (self
):
309 # getting a write implies that we are connected
310 if not self
.connected
:
311 self
.handle_connect()
315 def handle_expt_event (self
):
318 def handle_error (self
):
319 (file,fun
,line
), t
, v
, tbinfo
= compact_traceback()
321 # sometimes a user repr method will crash.
323 self_repr
= repr (self
)
325 self_repr
= '<__repr__ (self) failed for object at %0x>' % id(self
)
328 'uncaptured python exception, closing channel %s (%s:%s %s)' % (
337 def handle_expt (self
):
339 self
.log ('unhandled exception')
341 def handle_read (self
):
343 self
.log ('unhandled read event')
345 def handle_write (self
):
347 self
.log ('unhandled write event')
349 def handle_connect (self
):
351 self
.log ('unhandled connect event')
353 def handle_accept (self
):
355 self
.log ('unhandled accept event')
357 def handle_close (self
):
359 self
.log ('unhandled close event')
362 # ---------------------------------------------------------------------------
363 # adds simple buffered output capability, useful for simple clients.
364 # [for more sophisticated usage use asynchat.async_chat]
365 # ---------------------------------------------------------------------------
367 class dispatcher_with_send (dispatcher
):
368 def __init__ (self
, sock
=None):
369 dispatcher
.__init
__ (self
, sock
)
372 def initiate_send (self
):
374 num_sent
= dispatcher
.send (self
, self
.out_buffer
[:512])
375 self
.out_buffer
= self
.out_buffer
[num_sent
:]
377 def handle_write (self
):
381 return (not self
.connected
) or len(self
.out_buffer
)
383 def send (self
, data
):
385 self
.log ('sending %s' % repr(data
))
386 self
.out_buffer
= self
.out_buffer
+ data
389 # ---------------------------------------------------------------------------
390 # used for debugging.
391 # ---------------------------------------------------------------------------
393 def compact_traceback ():
394 t
,v
,tb
= sys
.exc_info()
398 tb
.tb_frame
.f_code
.co_filename
,
399 tb
.tb_frame
.f_code
.co_name
,
409 file, function
, line
= tbinfo
[-1]
410 info
= '[' + string
.join (
412 lambda x
: string
.join (x
, '|'),
417 return (file, function
, line
), t
, v
, info
421 for x
in socket_map
.keys():
425 # Asynchronous File I/O:
427 # After a little research (reading man pages on various unixen, and
428 # digging through the linux kernel), I've determined that select()
429 # isn't meant for doing doing asynchronous file i/o.
430 # Heartening, though - reading linux/mm/filemap.c shows that linux
431 # supports asynchronous read-ahead. So _MOST_ of the time, the data
432 # will be sitting in memory for us already when we go to read it.
434 # What other OS's (besides NT) support async file i/o? [VMS?]
436 # Regardless, this is useful for pipes, and stdin/stdout...
439 if os
.name
== 'posix':
444 # here we override just enough to make a file
445 # look like a socket for the purposes of asyncore.
446 def __init__ (self
, fd
):
449 def recv (self
, *args
):
450 return apply (os
.read
, (self
.fd
,)+args
)
452 def write (self
, *args
):
453 return apply (os
.write
, (self
.fd
,)+args
)
456 return os
.close (self
.fd
)
461 class file_dispatcher (dispatcher
):
462 def __init__ (self
, fd
):
463 dispatcher
.__init
__ (self
)
465 # set it to non-blocking mode
466 flags
= fcntl
.fcntl (fd
, FCNTL
.F_GETFL
, 0)
467 flags
= flags | FCNTL
.O_NONBLOCK
468 fcntl
.fcntl (fd
, FCNTL
.F_SETFL
, flags
)
471 def set_file (self
, fd
):
472 self
.socket
= file_wrapper (fd
)