Python poll IO多路复用
一、poll介绍
poll本质上和select没有区别,只是没有了最大连接数(linux上默认1024个)的限制,原因是它基于链表存储的。
本人的另一篇博客讲了 python select : https://www.cnblogs.com/weihengblog/p/9830253.html
二、使用poll编写SocketServer(本博客代码需要在linux下运行)
首先我们建立一个服务器端的socket
import select
import socket
import sys
import queue
from queue import Queue # 创建一个socket连接
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(False) # 绑定IP地址和端口号
server_address = ('localhost', 8800)
server.bind(server_address)
print("服务器已启动http://localhost:8800/") # 监听连接数
server.listen(5) # 消息队列 用于记录客户端发来的消息
message_queues = {}
设置轮询的超时时间
如果不设置timeout,方法将会阻塞直到对应的poll对象有一个事件发生。
TIMEOUT = 1000 # 设置为1秒
poll的事件类型
POLLIN Input ready 有数据读取
POLLPRI Priority input ready 有紧急数据读取
POLLOUT Able to receive output 准备输出
POLLERR Error 某些错误
POLLHUP Channel closed 挂起
POLLNVAL Channel not open 无效请求,描述符无法打开
READ_ONLY = select.POLLIN | select.POLLPRI | select.POLLHUP | select.POLLERR
READ_WRITE = READ_ONLY | select.POLLOUT
注册要监听文件描述符
首先需要实例化一个poll对象,对要监听的句柄进行注册
poller = select.poll()
# 注册server端socket 要监听的事件类型为 读
poller.register(server, READ_ONLY)
文件描述符映射具体的套接字对象
"""
由于poll()返回包含套接字文件描述符和事件标志的元组列表,因此需要从文件描述符号到对象的映射才能从套接字中读取或写入该套接字。
"""
fd_to_socket = { server.fileno(): server,}
事件轮询
while True:
    """
    去检测已经注册的文件描述符,会返回一个(fd,event)元祖列表
    fd:文件描述符
    event:描述符可能会发生的事件
    如果返回为空的列表,则说明超时且没有文件描述符有事件发生
    """
    events = poller.poll(TIMEOUT) # 如果timeout为None,将会阻塞,知道有事件发生
    for fd, flag in events:
        # 从文件描述符中检索实际的套接字
        s = fd_to_socket[fd]
事件类型判断
        if flag & (select.POLLIN | select.POLLPRI): # 有数据可以读取
            if s is server: # 表示有新的连接
                # 可以读取数据
                connection, client_address = s.accept()
                print(sys.stderr, '新的连接来自:', client_address)
                connection.setblocking(0)
                fd_to_socket[connection.fileno()] = connection # 往fd字典中添加一个新的 文件描述符
                poller.register(connection, READ_ONLY)
                message_queues[connection] = Queue() # 为了防止等待客户端发来数据期间发生阻塞,分配一个队列用于保存数据
            else: # 表示客户端传来了消息
                data = s.recv(1024)
                if data: # 表明数据接受成功
                    print(sys.stderr, '接受数据 "%s" 来自 %s' % (data, s.getpeername()))
                    message_queues[s].put(data)
                    # 修改一个已经存在的fd,修改事件为写。这里表示服务器向客户端要发送数据
                    poller.modify(s, READ_WRITE)
                else:
                    # 如果没有接受到数据,表示要断开连接
                    print(sys.stderr, '关闭', client_address, '并未读取到数据')
                    # 停止监听连接上的输入
                    poller.unregister(s)
                    s.close()
                    # 将此链接从队列中删除
                    del message_queues[s]
        elif flag & select.POLLHUP:
            print(sys.stderr, '关闭', client_address, '收到HUP后')
            poller.unregister(s)
            s.close()
        elif flag & select.POLLOUT:
            try:
                next_msg = message_queues[s].get_nowait()
            except queue.Empty:
                print(sys.stderr, '队列', s.getpeername(), '为空')
                poller.modify(s, READ_ONLY)
            else:
                print(sys.stderr, '发送 "%s" 到 %s' % (next_msg, s.getpeername()))
                s.send(next_msg)
        elif flag & select.POLLERR:
            print(sys.stderr, '异常信息:', s.getpeername())
            poller.unregister(s)
            s.close()
            del message_queues[s]
三、完整代码示例
server端:
import select
import socket
import sys
import queue
from queue import Queue # 创建一个socket连接
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(False) # 绑定IP地址和端口号
server_address = ('localhost', 8800)
server.bind(server_address)
print("服务器已启动http://localhost:8800/") # 监听连接数
server.listen(5) # 消息队列
message_queues = {} """
POLLIN Input ready 有数据读取
POLLPRI Priority input ready 有紧急数据读取
POLLOUT Able to receive output 准备输出
POLLERR Error 某些错误
POLLHUP Channel closed 挂起
POLLNVAL Channel not open 无效请求,描述符无法打开
"""
# 常用的标识 代表你想检查的事件类型
READ_ONLY = select.POLLIN | select.POLLPRI | select.POLLHUP | select.POLLERR
READ_WRITE = READ_ONLY | select.POLLOUT TIMEOUT = 1000
poller = select.poll() # 创建一个poll对象,该对象可以注册或注销文件描述符 # 注册一个文件描述符,可以通过poll()方法来检查是否有对应的IO事件发生
# 接受两个参数, fd 和 eventmask
poller.register(server,READ_ONLY)
fd_to_socket = { server.fileno(): server,}
# 服务器的循环调用poll(),然后通过查找套接字并根据事件中的标志采取行动来处理返回的“事件”。
while True: """
去检测已经注册的文件描述符,会返回一个(fd,event)元祖列表
fd:文件描述符
event:描述符可能会发生的事件
如果返回为空的列表,则说明超时且没有文件描述符有事件发生
"""
events = poller.poll(TIMEOUT) # 如果timeout为None,将会阻塞,知道有事件发生
for fd, flag in events:
# 从文件描述符中检索实际的套接字
s = fd_to_socket[fd] if flag & (select.POLLIN | select.POLLPRI): # 有数据可以读取 if s is server: # 表示有新的连接
# 可以读取数据
connection, client_address = s.accept()
print(sys.stderr, '新的连接来自:', client_address)
connection.setblocking(0)
fd_to_socket[connection.fileno()] = connection # 往fd字典中添加一个新的 文件描述符
poller.register(connection, READ_ONLY) message_queues[connection] = Queue() # 为了防止等待客户端发来数据期间发生阻塞,分配一个队列用于保存数据
else: # 表示客户端传来了消息 data = s.recv(1024)
if data: # 表明数据接受成功 print(sys.stderr, '接受数据 "%s" 来自 %s' % (data, s.getpeername()))
message_queues[s].put(data)
# 修改一个已经存在的fd,修改事件为写。这里表示服务器向客户端要发送数据
poller.modify(s, READ_WRITE)
else:
# 如果没有接受到数据,表示要断开连接
print(sys.stderr, '关闭', client_address, '并未读取到数据')
# 停止监听连接上的输入
poller.unregister(s)
s.close() # 将此链接从队列中删除
del message_queues[s] elif flag & select.POLLHUP:
print(sys.stderr, '关闭', client_address, '收到HUP后')
poller.unregister(s)
s.close() elif flag & select.POLLOUT:
try:
next_msg = message_queues[s].get_nowait()
except queue.Empty:
print(sys.stderr, '队列', s.getpeername(), '为空')
poller.modify(s, READ_ONLY)
else:
print(sys.stderr, '发送 "%s" 到 %s' % (next_msg, s.getpeername()))
s.send(next_msg) elif flag & select.POLLERR:
print(sys.stderr, '异常信息:', s.getpeername())
poller.unregister(s)
s.close()
del message_queues[s]
client端:
import socket client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('localhost',8800)) while True:
msg = bytes(input("<<<"),encoding='utf-8')
client.sendall(msg) data = client.recv(1024) print("{}".format(data))
Python poll IO多路复用的更多相关文章
- {python之IO多路复用}  IO模型介绍   阻塞IO(blocking IO)   非阻塞IO(non-blocking IO)   多路复用IO(IO multiplexing)  异步IO(Asynchronous I/O)  IO模型比较分析  selectors模块
		
python之IO多路复用 阅读目录 一 IO模型介绍 二 阻塞IO(blocking IO) 三 非阻塞IO(non-blocking IO) 四 多路复用IO(IO multiplexing) 五 ...
 - 【python】--  IO多路复用(select、poll、epoll)介绍及实现
		
IO多路复用(select.poll.epoll)介绍及select.epoll的实现 IO多路复用中包括 select.pool.epoll,这些都属于同步,还不属于异步 一.IO多路复用介绍 1. ...
 - python之IO多路复用
		
在python的网络编程里,socetserver是个重要的内置模块,其在内部其实就是利用了I/O多路复用.多线程和多进程技术,实现了并发通信.与多进程和多线程相比,I/O多路复用的系统开销小,系统不 ...
 - python中IO多路复用、协程
		
一.IO多路复用 IO多路复用:检测多个socket是否已经发生变化(是否已经连接成功/是否已经获取数据)(可读/可写) import socket def get_data(key): client ...
 - 09 Python之IO多路复用
		
四种常见IO模型 阻塞IO(blocking IO).非阻塞IO(nonblocking IO).IO多路复用(IOmultiplexing).异步IO(asynchronous IO) IO发生时涉 ...
 - Python select IO多路复用
		
一.select介绍 Python的select()函数是底层操作系统实现的直接接口.它监视套接字,打开文件和管道(任何带有返回有效文件描述符的fileno()方法),直到它们变得可读或可写,或者发生 ...
 - Python进程、线程、协程及IO多路复用
		
详情戳击下方链接 Python之进程.线程.协程 python之IO多路复用
 - 网络编程基础【day10】:IO多路复用
		
这些名词比较绕口,理解涵义就好.一个epoll场景:一个酒吧服务员(一个线程),前面趴了一群醉汉,突然一个吼一声“倒酒”(事件),你小跑过去给他倒一杯,然后随他去吧,突然又一个要倒酒,你又过去倒上,就 ...
 - 《Linux/UNIX系统编程手册》第63章 IO多路复用、信号驱动IO以及epoll
		
关键词:fasync_helper.kill_async.sigsuspend.sigaction.fcntl.F_SETOWN_EX.F_SETSIG.select().poll().poll_wa ...
 
随机推荐
- CF438E The Child and Binary Tree(生成函数,NTT)
			
题目链接:洛谷 CF原网 题目大意:有 $n$ 个互不相同的正整数 $c_i$.问对于每一个 $1\le i\le m$,有多少个不同形态(考虑结构和点权)的二叉树满足每个点权都在 $c$ 中出现过, ...
 - JVM复习总结
			
运行时数据区域 图中深色区域为,由所有线程共享的数据区域,其他为线程隔离的数据区. 程序计数器 程序计数器可以看作是当前线程执行的字节码的行号指示器. 虚拟机栈 虚拟机栈描述的是Java方法执行的内存 ...
 - 【POJ1734】Sightseeing Trip 无向图最小环
			
题目大意:给定一个 N 个顶点的无向图,边有边权,如果存在,求出该无向图的最小环,即:边权和最小的环,并输出路径. 题解:由于无向图,且节点数较少,考虑 Floyd 算法,在最外层刚开始遍历到第 K ...
 - random模块(十九)
			
1 ).random() 返回0<=n<1之间的随机实数n: 2 ).choice(seq) 从序列seq中返回随机的元素: 3 ).getrandbits(n) 以长整型形式返回n个随机 ...
 - Hadoop生态圈-使用Ganglia监控flume中间件
			
Hadoop生态圈-使用Ganglia监控flume中间件 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Ganglia监控简介 加州伯克利大学千禧计划的其中一个开源项目.是一 ...
 - 仅当使用了列列表并且 IDENTITY_INSERT 为 ON 时,才能为表'Address'中的标识列指
			
在有自增长的SQL表格里面插入指定ID的数据的时候,会禁止你操作,提示如题目,解决办法: set identity_insert address on ,,,) set identity_insert ...
 - 什么是cap
			
cap理论是分布式系统中非常重要的一个理念 什么是cap理论: Consistency一致性 Availability可用性 Partition-tolerance分区容忍性 CP: 高一致性C和分区 ...
 - Study 1 —— HTML5概述
			
HTML5概述HTML是一种超文本标记语言,主要用于描述超文本中内容的显示方式.标记语言经过浏览器的解释和编译,虽然它本身不能显示在浏览器中,但在浏览器中可以正确显示HTML标记的内容.HTML5是一 ...
 - WebStrom设置默认浏览器
 - MyBatis参数传递
			
一.单个参数: public List<XXBean> getXXBeanList(String xxCode); <select id="getXXXBeanList&q ...