Python的Asyncore异步Socket模块及实现端口转发的例子


Posted in Python onJune 14, 2016

Asyncore模块提供了以异步的方式写入套接字服务客户端和服务器的基础结构。

只有两种方式使一个程序在单处理器上实现“同时做不止一件事”。多线程编程是最简单和最流行的方式,但是有另一种很不一样的技术,可以使得我们保持多线程的几乎所有优势,却不用真正使用多线程。 如果你的程序主要是受I/O限制的,这是唯一可行的方式。如果你的程序是受处理器限制的,则先发制人的调度线程可能是你真正需要的。但是,很少网络服务器是受处理器限制的。

如果您的操作系统支持在其I / O库的 select() 系统调用(几乎所有系统都支持),那么你可以用它一次处理多个通信信道;当你的I/O在后台忙碌时处理其他工作。虽然这一策略似乎很奇怪很复杂,尤其是最开始的时候,这在很多方面比多线程编程更容易理解和控制。asyncore 模块为你解决了很多困难,使你能快速构建复杂的高性能网络服务器和客户端。对于会话应用程序和协议, asynchat 模块是非常有用的。

两个模块背后的想法就是创建一个或者多个网络 通道, 及 asyncore.dispatcher 和 asynchat.async_chat 类的实例. 如果你没有提供自己的映射的话,创建通道会把这两个实例加到由 loop() 函数使用的全局映射中。

一旦初始化通道被创建,调用 loop() 函数会激活通道服务,这会持续到最后一个通道(包括所有在异步服务中被加到映射中的通道)被关闭。
该模块文件包含一个loop()函数和一个dispatcher基类,其中loop()函数是全局函数,负责检查一个保存着dispatcher实例的dict,也被称为channel。
每一个继承dispatcher类的对象,都可以看作需要处理的socket,因此使用时我们只需定义一个继承dispatcher的类,然后重写一些方法就行,一般都是以handle_开头的方法。

端口转发的示例
如果你的程序想在同一时间做一件一上的事情,多线程是最快也最普遍的方式,但还有一个方式,在I/O流量很大的时候特别实用。如果你的操作系统支持select函数,你就可以让I/O在后台读写。这个模块听起来很复杂,但实际上有很多方式可以理解它,这个文档帮你解决了这些问题。
我感觉这个模块应该是一个以事件驱动的异步I/O,跟C++的事件选择模型类似。每当发生了读、写事件后,会交由我们重写的事件函数进行处理。
我这里有一个使用asyncore模块编写端口转发脚本,从这个脚本可以大概了解asyncore的基本使用。
在文章中,所说的客户端就是我们的电脑,服务端是转发到的地址。也就是客户端发送到这个脚本的信息,这个脚本转发到服务端上。
首先,定义一个forwarder类:

class forwarder(asyncore.dispatcher):
  def __init__(self, ip, port, remoteip,remoteport,backlog=5):
    asyncore.dispatcher.__init__(self)
    self.remoteip=remoteip
    self.remoteport=remoteport
    self.create_socket(socket.AF_INET,socket.SOCK_STREAM)
    self.set_reuse_addr()
    self.bind((ip,port))
    self.listen(backlog)

  def handle_accept(self):
    conn, addr = self.accept()
    # print '--- Connect --- '
    sender(receiver(conn),self.remoteip,self.remoteport)

这个类继承自asyncore模块的dispatcher类(它就是我们的主要的类,其中包括了一些之后要重载的函数),构造函数获得5个参数,第1、2个参数是脚本监听的本地IP和端口,第3、4个参数是服务端的IP和端口。第5个参数是listen函数的参数,等待队列最大长度。
如何使用这个类,只需要如下新建一个对象,把相应IP和端口传入,再进入loop即可:

forwarder(options.local_ip,options.local_port,options.remote_ip,options.remote_port)
asyncore.loop()

进入loop后相当于开启了一个守护线程,在后台一直运行着,等待socket事件的发生。
因为我们这个脚本是端口转发工具,所以实际上运行的过程是:客户端连接这个脚本的端口,让后发送给这个端口的数据脚本自动转发到服务端地址和端口。所以,首先接收到的应该是连接消息(accept事件)。
那么,当accept事件发生后,就进入了handle_accept函数中。所以我们看到,handle_accept函数实际上就是调用了accept函数接收了客户端连接对象和地址。获得了之后又新建了一个sender类对象,这个对象定义如下:

class sender(asyncore.dispatcher):
  def __init__(self, receiver, remoteaddr,remoteport):
    asyncore.dispatcher.__init__(self)
    self.receiver=receiver
    receiver.sender=self
    self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
    self.connect((remoteaddr, remoteport))

  def handle_connect(self):
    pass

  def handle_read(self):
    read = self.recv(4096)
    # print '<-- %04i'%len(read)
    self.receiver.to_remote_buffer += read

  def writable(self):
    return (len(self.receiver.from_remote_buffer) > 0)

  def handle_write(self):
    sent = self.send(self.receiver.from_remote_buffer)
    # print '--> %04i'%sent
    self.receiver.from_remote_buffer = self.receiver.from_remote_buffer[sent:]

  def handle_close(self):
    self.close()
    self.receiver.close()

这个类也是继承自asyncore.dispatcher,它的构造函数接收3个参数,分别是recv对象(这个之后说到),远端地址,对应端口。
函数中又新建了一个socket,这个socket就是和服务端端口通信的socket,然后调用connect连接这个端口。
之后其实也是进入了一个等待消息的过程,因为我们发送了一个connect,所以下一次接收到的消息应该是connect,而handle_connect是一个pass掉的函数。没有执行任何内容。
在连接完成后,我们就相当于建立好了一个端口转发的通道。当客户端向这个脚本监听的端口发送数据包时,它就会自动转发到服务端端口上。服务端端口返回的数据包,会自动转发到客户端上。
回到构造函数的第1个参数,我们在forwarder类函数中可以看到,传入的是一个receiver(conn)对象,receiver也是一个类,我们来看看这个类的定义:

class receiver(asyncore.dispatcher):
  def __init__(self,conn):
    asyncore.dispatcher.__init__(self,conn)
    self.from_remote_buffer=''
    self.to_remote_buffer=''
    self.sender=None

  def handle_connect(self):
    pass

  def handle_read(self):
    read = self.recv(4096)
    # print '%04i -->'%len(read)
    self.from_remote_buffer += read

  def writable(self):
    return (len(self.to_remote_buffer) > 0)

  def handle_write(self):
    sent = self.send(self.to_remote_buffer)
    # print '%04i <--'%sent
    self.to_remote_buffer = self.to_remote_buffer[sent:]

  def handle_close(self):
    self.close()
    if self.sender:
      self.sender.close()

它也是继承了asyncore.dispatcher,构造函数只接收一个参数,就是connect的返回值,一个连接对象。
实际上这个对象它就是监听、处理与客户端的通信,而之前说的sender对象是监听、处理与服务端的通信。                   

Python 相关文章推荐
python监控linux内存并写入mongodb(推荐)
Sep 11 Python
Python实现的选择排序算法示例
Nov 29 Python
攻击者是如何将PHP Phar包伪装成图像以绕过文件类型检测的(推荐)
Oct 11 Python
python2.7 安装pip的方法步骤(管用)
May 05 Python
python中对数据进行各种排序的方法
Jul 02 Python
Python 获取ftp服务器文件时间的方法
Jul 02 Python
Python序列化与反序列化pickle用法实例
Nov 11 Python
Python大数据之从网页上爬取数据的方法详解
Nov 16 Python
Python线程指南分享
Nov 19 Python
推荐技术人员一款Python开源库(造数据神器)
Jul 08 Python
python获取本周、上周、本月、上月及本季的时间代码实例
Sep 08 Python
Python爬虫分析微博热搜关键词的实现代码
Feb 22 Python
Python实现TCP/IP协议下的端口转发及重定向示例
Jun 14 #Python
Python实现TCP协议下的端口映射功能的脚本程序示例
Jun 14 #Python
浅谈python新手中常见的疑惑及解答
Jun 14 #Python
Python中死锁的形成示例及死锁情况的防止
Jun 14 #Python
实例探究Python以并发方式编写高性能端口扫描器的方法
Jun 14 #Python
Python使用dis模块把Python反编译为字节码的用法详解
Jun 14 #Python
Python的Flask框架中使用Flask-Migrate扩展迁移数据库的教程
Jun 14 #Python
You might like
第六节 访问属性和方法 [6]
2006/10/09 PHP
PHP读取大文件的几种方法介绍
2016/10/27 PHP
PHP设计模式之适配器模式原理与用法分析
2018/04/25 PHP
让iframe框架网页在任何浏览器下自动伸缩
2006/08/18 Javascript
使用隐藏的new来创建对象
2011/03/29 Javascript
NODE.JS加密模块CRYPTO常用方法介绍
2014/06/05 Javascript
AMD异步模块定义介绍和Require.js中使用jQuery及jQuery插件的方法
2014/06/06 Javascript
用JS生成UUID的方法实例
2016/03/30 Javascript
JS创建事件的三种方法(实例代码)
2016/05/12 Javascript
深究AngularJS之ui-router详解
2017/06/13 Javascript
原生js FileReader对象实现图片上传本地预览效果
2020/03/27 Javascript
使用 Vue 绑定单个或多个 Class 名的实例代码
2018/01/08 Javascript
layer.open关闭父窗口 以及调用父页面的方法
2018/08/17 Javascript
angularjs下ng-repeat点击元素改变样式的实现方法
2018/09/12 Javascript
微信小程序自定义toast组件的方法详解【含动画】
2019/05/11 Javascript
深入剖析JavaScript instanceof 运算符
2019/06/14 Javascript
Layui给switch添加响应事件的例子
2019/09/03 Javascript
javascript简单实现深浅拷贝过程详解
2019/10/08 Javascript
在 Vue 中编写 SVG 图标组件的方法
2020/02/24 Javascript
JavaScript实现捕获鼠标坐标
2020/04/12 Javascript
javascript全局自定义鼠标右键菜单
2020/12/08 Javascript
一个简单的python爬虫程序 爬取豆瓣热度Top100以内的电影信息
2018/04/17 Python
python hook监听事件详解
2018/10/25 Python
通过PYTHON来实现图像分割详解
2019/06/26 Python
django2.2安装错误最全的解决方案(小结)
2019/09/24 Python
JD Sports比利时官网:英国领先的运动鞋和运动服饰零售商
2018/10/10 全球购物
美国儿童服装、家具和玩具精品店:Maisonette
2019/11/24 全球购物
法院实习人员自我鉴定
2013/09/26 职场文书
幼儿园中秋节活动方案
2014/02/06 职场文书
学校招生宣传广告词
2014/03/19 职场文书
授权收款委托书范本
2014/10/10 职场文书
离婚协议书怎么写的
2014/12/14 职场文书
工程部经理岗位职责
2015/02/02 职场文书
Python 数据可视化之Seaborn详解
2021/11/02 Python
【海涛教你打DOTA】死灵飞龙第一视角解说
2022/04/01 DOTA
Mysql表数据比较大情况下修改添加字段的方法实例
2022/06/28 MySQL