网络编程
位置:首页>> 网络编程>> Python编程>> 使用Python的Tornado框架实现一个简单的WebQQ机器人

使用Python的Tornado框架实现一个简单的WebQQ机器人

作者:C Wong  发布时间:2023-03-19 03:26:09 

标签:Python

我打算将WebQQ单独出来运行, 一开始直接拷贝了pyxmpp2的mainloop, 但是跑起来问题多多, 所以我又研究了利用Tornado进行网络编程(这里), 所以我放弃了Pyxmpp2的mainloop,使用Tornado进行重写

首先放出项目代码
引子

WebQQ协议是一套基于HTTP的QQ协议, 而用Python的urllib2库进行请求太慢, 因为HTTP本身就使用socket请求, 所以改用多路复用I/O模型, 而Tornado简单高效, 看过代码后可以轻松上手.平台兼容性很好, 所以选择Tornado作为网络框架.
原理

首先实现了一个 HTTPStream类, 其主要接口是add_request方法, 它接受一个必选参数:request 是一个 urllib2.Request的实例, 和一个可选参数:readback是一个接受一个urllib2.urlopen(request)返回的Response参数的读取函数, 代码如下:


class HTTPStream(object):
 # 省略若干代码
 def add_request(self, request, readback = None):
   if not isinstance(request, urllib2.Request):
     raise ValueError, "Not a invaid requset"

# 此处易触发timeout异常, 省略处理异常代码
   sock, data = self.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将urllib2.Request的实例解析出一个socket和一个用于socket发送的数据.前面文章介绍过了, tornado.ioloop.IOLoop.add_handler用于将注册socket, 其需要三个参数: socket的文件描述符, 接受文件描述符和事件参数的回调, 和注册的事件.

我们用到的回调是HTTPStream._handle_events:


class HTTPStream(object):
 # 省略若干代码
 def _handle_events(self, request, data, readback, fd, event):
   """ 用于处理Tornado事件
   Arguments:
     `request`  -  urllib.Request
     `data`   -  socket要写入的数据
     `readback` -  读取函数
     以上参数应当使用partial封装然后将此方法作为IOLoop.add_handler的callback
     `fd`    -  IOLoop传递 文件描述符
     `event`   -  IOLoop传递 tornado
   """
   s = self.fd_map[fd]

if event & IOLoop.READ:
     # 省略错误处理
     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

它接受的参数上面注释写的很清楚, 不做解释, 所以将此方法通过functools.partial封装做为callback传递给tornado.ioloop.IOLoop.add_handler, 并注册为写事件, 以便发送HTTP请求.

HTTPStream._handle_events用于处理事件, 当事件为写时就发送HTTP请求(根据urllib2.Request生成的用于发送的数据), 并判断是否有读取函数, 有则注册读事件, 当事件为读时就从socket中构建一个Response并传递给读取函数, 读取函数会返回3个值, 分别为: 下一个请求, 请求的读取函数(可为None, 为None则只请求不读取), 下一个请求的延迟(多长事件后添加此请求, 可选, 单位为秒)

依据读取函数返回的三个值来确定下一个请求, 并完成一系列的请求. 更加完整的代码请参见文章开头给出的项目代码

HTTPStream.http_sock.make_response执行时会将socket设为阻塞, 因为不设置阻塞会出现httplib.BadStatusLine异常.读取函数执行完毕,重新将socket设置为非阻塞, 并移除此socket(虽然做了这样的处理但是QQ连接时间稍长还是会触发httplib.BadStatusLine异常)

0
投稿

猜你喜欢

  • 看过 Vue 源码的同学可以知道,<keep-alive>、<transition>、<transition-
  • 要实现的SQL查询很原始:要求从第一个表进行查询得到第二个表格式的数据,上网查询之后竟然能写出下面的SQL:select * from us
  • 对于英文文本分句比较简单,只要根据终结符"."划分就好,中文文本分句看似很简单,但是实现时会遇到很多麻烦,尤其是处理社交
  • 同伪类的方式类似,伪元素通过对插人到文档中的虚构元素进行触发,从而达到某种效果。在CSS1里,有两个伪元素,即:first-letter和f
  • turtle库是一个很经典的绘图库,其最初来自于1967年创造的logo编程语言,之后被Python编写放到了Python的内置模块中。网络
  • 使用场景当项目越来越庞大之后,不可避免的要拆分成多个子模块,我们希望各个子模块有独立的版本管理,并且由专门的人去维护,这时候我们就要用到gi
  • 用dicompyler软件打开dicom图像,头文件如图所示:当然也可以直接读取:ds = dicom.read_file('H:\
  •  Azkaban是什么?Azkaban是由Linkedin公司推出的一个批量工作流任务调度器,主要用于在一个工作流内以一个特定的顺
  • php的引用(就是在变量或者函数、对象等前面加上&符号),在PHP 中引用的意思是:不同的名字访问同一个变量内容。与C语言中的指针是
  • int()是Python的一个内部函数 Python系统帮助里面是这么说的>>> help(int) Help
  • 来,考考大家一个问题,在 MySQL 中当某一列设置为 int(0) 时会发生什么 ?为了演示这个问题,我们先要创建一个表DROP TABL
  • 作者:Lachlan Hunt概要网络是不断的进化的. 新的和有创意的网站每天都在出现, 从各方面都在冲击着HTML的边界. HTML 4来
  • Python获取图片的大小了解过Pillow的都知道,Pillow是一个非常强大的图片处理器,这篇文章主要记录一下Pillow对图片信息的获
  • Go语言中strconv包实现了基本数据类型和其字符串表示的相互转换。strconv包实现了基本数据类型与其字符串表示的转换,主要有以下常用
  • Python是很好的爬虫工具不用再说了,它可以满足我们爬取网络内容的需求,那最简单的爬取网络上的图片,可以通过很简单的方法实现。只需导入正则
  • 1. 什么是404404是一个 http 错误代码,即请求的网页不存在。代码404的第一个“4”代表客户端的错误,如错误的网页位址;后两的数
  • 选择排序选择排序是一种简单的比较排序算法,它的算法思路是首先从数组中寻找最小(大)的元素,然后放到数组中的第一位,接下来继续从未排序的元素中
  • 本文详细分析了Yii配置文件的用法。分享给大家供大家参考。具体分析如下:Yii配置文件比ThinkPHP复杂多了,先把自己了解的配置记录下来
  • 在我们建立一个数据库时,并且想将分散在各处的不同类型的数据库分类汇总在这个新建的数据库中时,尤其是在进行数据检验、净化和转换时,将会面临很大
  • 背景今天在工作中,同事遇到一个上传图片的问题:系统要求的图片大小不能超过512KB。但是同事又有很多照片。这要是每一个照片都用ps压缩的话,
手机版 网络编程 asp之家 www.aspxhome.com