
Python的Asyncore异步Socket模块及实现端口转发的例子
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对象是监听、处理与服务端的通信。
数据分析咨询请扫描二维码
若不方便扫码,搜微信号:CDAshujufenxi
在人工智能领域,“大模型” 已成为近年来的热点标签:从参数超 1750 亿的 GPT-3,到万亿级参数的 PaLM,再到多模态大模型 GPT-4 ...
2025-10-22在 MySQL 数据库的日常运维与开发中,“更新数据是否会影响读数据” 是一个高频疑问。这个问题的答案并非简单的 “是” 或 “否 ...
2025-10-22在企业数据分析中,“数据孤岛” 是制约分析深度的核心瓶颈 —— 用户数据散落在注册系统、APP 日志、客服记录中,订单数据分散 ...
2025-10-22在神经网络设计中,“隐藏层个数” 是决定模型能力的关键参数 —— 太少会导致 “欠拟合”(模型无法捕捉复杂数据规律,如用单隐 ...
2025-10-21在特征工程流程中,“单变量筛选” 是承上启下的关键步骤 —— 它通过分析单个特征与目标变量的关联强度,剔除无意义、冗余的特 ...
2025-10-21在数据分析全流程中,“数据读取” 常被误解为 “简单的文件打开”—— 双击 Excel、执行基础 SQL 查询即可完成。但对 CDA(Cert ...
2025-10-21在实际业务数据分析中,我们遇到的大多数数据并非理想的正态分布 —— 电商平台的用户消费金额(少数用户单次消费上万元,多数集 ...
2025-10-20在数字化交互中,用户的每一次操作 —— 从电商平台的 “浏览商品→加入购物车→查看评价→放弃下单”,到内容 APP 的 “点击短 ...
2025-10-20在数据分析的全流程中,“数据采集” 是最基础也最关键的环节 —— 如同烹饪前需备好新鲜食材,若采集的数据不完整、不准确或不 ...
2025-10-20在数据成为新时代“石油”的今天,几乎每个职场人都在焦虑: “为什么别人能用数据驱动决策、升职加薪,而我面对Excel表格却无从 ...
2025-10-18数据清洗是 “数据价值挖掘的前置关卡”—— 其核心目标是 “去除噪声、修正错误、规范格式”,但前提是不破坏数据的真实业务含 ...
2025-10-17在数据汇总分析中,透视表凭借灵活的字段重组能力成为核心工具,但原始透视表仅能呈现数值结果,缺乏对数据背景、异常原因或业务 ...
2025-10-17在企业管理中,“凭经验定策略” 的传统模式正逐渐失效 —— 金融机构靠 “研究员主观判断” 选股可能错失收益,电商靠 “运营拍 ...
2025-10-17在数据库日常操作中,INSERT INTO SELECT是实现 “批量数据迁移” 的核心 SQL 语句 —— 它能直接将一个表(或查询结果集)的数 ...
2025-10-16在机器学习建模中,“参数” 是决定模型效果的关键变量 —— 无论是线性回归的系数、随机森林的树深度,还是神经网络的权重,这 ...
2025-10-16在数字化浪潮中,“数据” 已从 “辅助决策的工具” 升级为 “驱动业务的核心资产”—— 电商平台靠用户行为数据优化推荐算法, ...
2025-10-16在大模型从实验室走向生产环境的过程中,“稳定性” 是决定其能否实用的关键 —— 一个在单轮测试中表现优异的模型,若在高并发 ...
2025-10-15在机器学习入门领域,“鸢尾花数据集(Iris Dataset)” 是理解 “特征值” 与 “目标值” 的最佳案例 —— 它结构清晰、维度适 ...
2025-10-15在数据驱动的业务场景中,零散的指标(如 “GMV”“复购率”)就像 “散落的零件”,无法支撑系统性决策;而科学的指标体系,则 ...
2025-10-15在神经网络模型设计中,“隐藏层层数” 是决定模型能力与效率的核心参数之一 —— 层数过少,模型可能 “欠拟合”(无法捕捉数据 ...
2025-10-14