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.dispatcherand 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.

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).

  • ac_out_buffer_size

  • 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 aNotImplementedError 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 a NotImplementedError exception. The buffered input datashould be available via an instance attribute.
  • async_chat.get_terminator()
  • Returns the current terminator for the channel.
  • 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. termmay 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.

  1. import asynchat
  2.  
  3. class http_request_handler(asynchat.async_chat):
  4.  
  5. def __init__(self, sock, addr, sessions, log):
  6. asynchat.async_chat.__init__(self, sock=sock)
  7. self.addr = addr
  8. self.sessions = sessions
  9. self.ibuffer = []
  10. self.obuffer = b""
  11. self.set_terminator(b"\r\n\r\n")
  12. self.reading_headers = True
  13. self.handling = False
  14. self.cgi_data = None
  15. self.log = log
  16.  
  17. def collect_incoming_data(self, data):
  18. """Buffer the data"""
  19. self.ibuffer.append(data)
  20.  
  21. def found_terminator(self):
  22. if self.reading_headers:
  23. self.reading_headers = False
  24. self.parse_headers(b"".join(self.ibuffer))
  25. self.ibuffer = []
  26. if self.op.upper() == b"POST":
  27. clen = self.headers.getheader("content-length")
  28. self.set_terminator(int(clen))
  29. else:
  30. self.handling = True
  31. self.set_terminator(None)
  32. self.handle_request()
  33. elif not self.handling:
  34. self.set_terminator(None) # browsers sometimes over-send
  35. self.cgi_data = parse(self.headers, b"".join(self.ibuffer))
  36. self.handling = True
  37. self.ibuffer = []
  38. self.handle_request()