asynchat —- 异步 socket 指令/响应 处理器
Source code:Lib/asynchat.py
3.6 版后已移除: 请使用 asyncio
替代。
注解
该模块仅为提供向后兼容。我们推荐在新代码中使用 asyncio
。
This module builds on the asyncore
infrastructure, simplifyingasynchronous clients and servers and making it easier to handle protocolswhose elements are terminated by arbitrary strings, or are of variable length.asynchat
defines the abstract class async_chat
that yousubclass, providing implementations of the collect_incoming_data()
andfound_terminator()
methods. It uses the same asynchronous loop asasyncore
, and the two types of channel, asyncore.dispatcher
and asynchat.async_chat
, can freely be mixed in the channel map.Typically an asyncore.dispatcher
server channel generates newasynchat.async_chat
channel objects as it receives incomingconnection requests.
- class
asynchat.
async_chat
- This class is an abstract subclass of
asyncore.dispatcher
. To makepractical use of the code you must subclassasync_chat
, providingmeaningfulcollect_incoming_data()
andfound_terminator()
methods.Theasyncore.dispatcher
methods can be used, although not all makesense in a message/response context.
Like asyncore.dispatcher
, async_chat
defines a set ofevents that are generated by an analysis of socket conditions after aselect()
call. Once the polling loop has been started theasync_chat
object's methods are called by the event-processingframework with no action on the part of the programmer.
Two class attributes can be modified, to improve performance, or possiblyeven to conserve memory.
ac_in_buffer_size
The asynchronous input buffer size (default
4096
).- The asynchronous output buffer size (default
4096
).
Unlike asyncore.dispatcher
, async_chat
allows you todefine a FIFO queue of producers. A producer needhave only one method, more()
, which should return data to betransmitted on the channel.The producer indicates exhaustion (i.e. that it contains no more data) byhaving its more()
method return the empty bytes object. At this pointthe async_chat
object removes the producer from the queue and startsusing the next producer, if any. When the producer queue is empty thehandle_write()
method does nothing. You use the channel object'sset_terminator()
method to describe how to recognize the end of, oran important breakpoint in, an incoming transmission from the remoteendpoint.
To build a functioning async_chat
subclass your input methodscollect_incoming_data()
and found_terminator()
must handle thedata that the channel receives asynchronously. The methods are describedbelow.
async_chat.
close_when_done
()- Pushes a
None
on to the producer queue. When this producer is popped offthe queue it causes the channel to be closed.
asyncchat.
collect_incoming_data
(_data)- Called with data holding an arbitrary amount of received data. Thedefault method, which must be overridden, raises a
NotImplementedError
exception.
async_chat.
discard_buffers
()- In emergencies this method will discard any data held in the input and/oroutput buffers and the producer queue.
async_chat.
found_terminator
()- Called when the incoming data stream matches the termination condition setby
set_terminator()
. The default method, which must be overridden,raises aNotImplementedError
exception. The buffered input datashould be available via an instance attribute.
asyncchat.
push
(_data)- Pushes data on to the channel's queue to ensure its transmission.This is all you need to do to have the channel write the data out to thenetwork, although it is possible to use your own producers in more complexschemes to implement encryption and chunking, for example.
asyncchat.
push_with_producer
(_producer)- Takes a producer object and adds it to the producer queue associated withthe channel. When all currently-pushed producers have been exhausted thechannel will consume this producer's data by calling its
more()
method and send the data to the remote endpoint.
asyncchat.
set_terminator
(_term)- Sets the terminating condition to be recognized on the channel.
term
may be any of three types of value, corresponding to three different waysto handle incoming protocol data.
term
描述
string
Will call found_terminator()
when thestring is found in the input stream
integer
Will call found_terminator()
when theindicated number of characters have beenreceived
None
The channel continues to collect dataforever
Note that any data following the terminator will be available for readingby the channel after found_terminator()
is called.
asynchat Example
The following partial example shows how HTTP requests can be read withasync_chat
. A web server might create anhttp_request_handler
object for each incoming client connection.Notice that initially the channel terminator is set to match the blank line atthe end of the HTTP headers, and a flag indicates that the headers are beingread.
Once the headers have been read, if the request is of type POST (indicatingthat further data are present in the input stream) then theContent-Length:
header is used to set a numeric terminator to read theright amount of data from the channel.
The handle_request()
method is called once all relevant input has beenmarshalled, after setting the channel terminator to None
to ensure thatany extraneous data sent by the web client are ignored.
- import asynchat
- class http_request_handler(asynchat.async_chat):
- def __init__(self, sock, addr, sessions, log):
- asynchat.async_chat.__init__(self, sock=sock)
- self.addr = addr
- self.sessions = sessions
- self.ibuffer = []
- self.obuffer = b""
- self.set_terminator(b"\r\n\r\n")
- self.reading_headers = True
- self.handling = False
- self.cgi_data = None
- self.log = log
- def collect_incoming_data(self, data):
- """Buffer the data"""
- self.ibuffer.append(data)
- def found_terminator(self):
- if self.reading_headers:
- self.reading_headers = False
- self.parse_headers(b"".join(self.ibuffer))
- self.ibuffer = []
- if self.op.upper() == b"POST":
- clen = self.headers.getheader("content-length")
- self.set_terminator(int(clen))
- else:
- self.handling = True
- self.set_terminator(None)
- self.handle_request()
- elif not self.handling:
- self.set_terminator(None) # browsers sometimes over-send
- self.cgi_data = parse(self.headers, b"".join(self.ibuffer))
- self.handling = True
- self.ibuffer = []
- self.handle_request()