This article mainly introduces how to use the Python Tornado framework to implement a simple WebQQ robot. Tornado's asynchronous feature can improve IO performance. if you need it, refer to my plan to run WebQQ separately, at first, I directly copied the mainloop of pyxmpp2, but there were a lot of problems running. so I studied Network Programming Using Tornado (here), so I gave up the mainloop of Pyxmpp2, rewrite using Tornado
First, release the project code.
Introduction
The WebQQ protocol is a set of HTTP-based QQ protocol, and it is too slow to use the Python urllib2 library for requests. because HTTP itself uses socket requests, I/O multiplexing is used instead, tornado is simple and efficient, so you can get started easily after reading the code. the platform has good compatibility, so Tornado is used as the network framework.
Principle
First, an HTTPStream class is implemented. its main interface is the add_request method, which accepts a required parameter: request is an urllib2.Request instance and an optional parameter: readback is a read function that accepts the Response parameter returned by urllib2.urlopen (request). The code is as follows:
Class HTTPStream (object): # omitting code def add_request (self, request, readback = None): if not isinstance (request, urllib2.Request): raise ValueError, "Not a invaid requset" # The timeout exception is easily triggered Here. the exception code sock, data = self is omitted. http_sock.make_http_sock_data (request) fd = sock. fileno () self. fd_map [fd] = sock self. fd_request_map [fd] = request callback = partial (self. _ handle_events, request, data, readback) self. ioloop. add_handler (fd, callback, IOLoop. WRITE)
HTTPStream. add_request parses the urllib2.Request instance into a socket and a data used for socket sending. tornado. ioloop. IOLoop. add_handler is used to register a socket. it requires three parameters: the file descriptor of the socket, the callback of the file descriptor and the event parameter, and the registered event.
The callback we use is HTTPStream. _ handle_events:
Class HTTPStream (object): # omitting code def _ handle_events (self, request, data, readback, fd, event): "used to process Tornado event Arguments: 'request'-urllib. request 'data'-the data to be written in the socket 'readback'-read the above parameters of the function should be encapsulated using partial and then use this method as IOLoop. add_handler callback 'fd '-IOLoop transmits the file descriptor 'event'-IOLoop to pass tornado "s = self. fd_map [fd] if event & IOLoop. READ: # omitting error handling resp = self. http_sock.make_response (s, request) args = readback (resp) s. setblocking (False) if args and len (args) = 3: t = threading. thread (target = self. add_delay_request, args = args) t. setDaemon (True) t. start () if args and len (args) = 2: self. add_request (* args) self. ioloop. remove_handler (fd) if event & IOLoop. WRITE: s. sendall (data) if readback: self. ioloop. update_handler (fd, IOLoop. READ) else: self. ioloop. remove_handler (fd) if event & IOLoop. ERROR: pass
The parameters it accepts are clearly annotated and not explained. Therefore, this method is passed through functools. partial encapsulation is passed to tornado as callback. ioloop. IOLoop. add_handler and register it as a write event to send an HTTP request.
HTTPStream. _ handle_events is used to process events. When an event is written, an HTTP request is sent (the data generated based on urllib2.Request is used for sending), and whether a read function exists. If yes, a read event is registered, when the event is read, a Response is constructed from the socket and passed to the read function. The Read function returns three values: the next request, the read function of the request (which can be None, if it is None, only requests are not read. the latency of the next request (optional, in seconds)
Determine the next request based on the three values returned by the read function and complete a series of requests. for more complete code, see the project code given at the beginning of the article.
HTTPStream. when http_sock.make_response is executed, the socket is set to blocked because httplib occurs if no blocking is set. badStatusLine exception. after reading the function, set the socket to non-blocking and remove the socket (although this is done, the QQ connection time will still trigger httplib. badStatusLine exception)