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


Posted in Python onApril 24, 2015

我打算将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异常)

Python 相关文章推荐
python list使用示例 list中找连续的数字
Jan 27 Python
python提取页面内url列表的方法
May 25 Python
Python学习笔记之解析json的方法分析
Apr 21 Python
对Python 语音识别框架详解
Dec 24 Python
pyqt5对用qt designer设计的窗体实现弹出子窗口的示例
Jun 19 Python
浅谈python之自动化运维(Paramiko)
Jan 31 Python
Windows下Anaconda安装、换源与更新的方法
Apr 17 Python
解决python 虚拟环境删除包无法加载的问题
Jul 13 Python
python+opencv实现车道线检测
Feb 19 Python
Python代码,能玩30多款童年游戏!这些有几个是你玩过的
Apr 27 Python
Python实现简单得递归下降Parser
May 02 Python
在python中读取和写入CSV文件详情
Jun 28 Python
Python程序中使用SQLAlchemy时出现乱码的解决方案
Apr 24 #Python
简单说明Python中的装饰器的用法
Apr 24 #Python
使用基于Python的Tornado框架的HTTP客户端的教程
Apr 24 #Python
简单介绍Python的Tornado框架中的协程异步实现原理
Apr 23 #Python
解决Python中由于logging模块误用导致的内存泄露
Apr 23 #Python
粗略分析Python中的内存泄漏
Apr 23 #Python
使用beaker让Facebook的Bottle框架支持session功能
Apr 23 #Python
You might like
Yii2汉字转拼音类的实例代码
2017/04/18 PHP
js验证模型自我实现的具体方法
2013/06/21 Javascript
js Select下拉列表框进行多选、移除、交换内容的具体实现方法
2013/08/13 Javascript
JavaScript中跨域调用Flash的方法
2014/08/11 Javascript
DWR中各种java方法的调用
2016/05/04 Javascript
JS中使用FormData上传文件、图片的方法
2016/08/07 Javascript
关于 jQuery Easyui异步加载tree的问题解析
2016/12/06 Javascript
jQuery 获取select选中值及清除选中状态
2016/12/13 Javascript
jQuery实现手势解锁密码特效
2017/08/14 jQuery
jQuery动态添加元素无法触发绑定事件的解决方法分析
2018/01/02 jQuery
关于HTML5的data-*自定义属性的总结
2018/05/05 Javascript
Vue+element-ui 实现表格的分页功能示例
2018/08/18 Javascript
nodejs中express入门和基础知识点学习
2018/09/13 NodeJs
微信接入之获取用户头像的方法步骤
2019/09/23 Javascript
Vue 组件复用多次自定义参数操作
2020/07/27 Javascript
[00:57]辉夜杯战队访谈宣传片—VG
2015/12/25 DOTA
Python程序员开发中常犯的10个错误
2014/07/07 Python
python如何在列表、字典中筛选数据
2018/03/19 Python
详解Python中的正则表达式
2018/07/08 Python
解决Shell执行python文件,传参空格引起的问题
2018/10/30 Python
python爬虫之自制英汉字典
2019/06/24 Python
Python 合并多个TXT文件并统计词频的实现
2019/08/23 Python
Django高并发负载均衡实现原理详解
2020/04/04 Python
解决python和pycharm安装gmpy2 出现ERROR的问题
2020/08/28 Python
html5使用canvas画三角形
2014/12/15 HTML / CSS
中国首家奢侈品O2O网购平台:第五大道奢侈品网
2017/12/14 全球购物
英国珠宝和手表专家:Pleasance & Harper
2020/10/21 全球购物
教师岗位职责范本
2013/12/29 职场文书
入党自荐书范文
2014/03/09 职场文书
电子商务求职信
2014/06/15 职场文书
先进事迹演讲稿
2014/09/01 职场文书
征用土地赔偿协议书
2014/09/26 职场文书
经典搞笑版检讨书
2015/02/19 职场文书
退税申请报告怎么写
2015/05/18 职场文书
Python-typing: 类型标注与支持 Any类型详解
2021/05/10 Python
python中 Flask Web 表单的使用方法
2022/05/20 Python