Python select IO多路复用
一、select介绍
Python的select()函数是底层操作系统实现的直接接口。它监视套接字,打开文件和管道(任何带有返回有效文件描述符的fileno()方法),直到它们变得可读或可写,或者发生通信错误。select()使得更容易同时监视多个连接,并且比使用套接字超时在Python中编写轮询循环更有效,因为监视发生在操作系统网络层而不是解释器。
二、使用select编写SocketServer
接下来通过socket server例子要以了解select 是如何通过单进程实现同时处理多个非阻塞的socket连接的
首先我们建立一个服务器端socket
import select
import socket
import sys
from queue import Queue
import queue # 创建套接字
server = socket.socket()
server.setblocking(False)
server.bind(('localhost',8800))
# 监听传入的连接
server.listen(5)
select()的参数是三个包含要监视的通信通道的列表。第一个是要检查要读取的传入数据的对象列表,第二个包含在缓冲区中有空间时将接收传出数据的对象,第三个包含可能有错误的对象(通常是错误的组合输入和输出通道对象)。服务器的下一步是设置包含要传递给select()输入源和输出目标的列表。
input = [server]
output = []
select.select(input,output,input)
所有客户端的进来的连接和数据将会被server的主循环程序放在上面的list中处理,我们现在的server端需要等待连接可写(writable)之后才能过来,然后接收数据并返回(因此不是在接收到数据之后就立刻返回),因为每个连接要把输入或输出的数据先缓存到queue里,然后再由select取出来再发出去。
# 对外发送数据的队列,记录到字典中
message_queues = {}
服务器程序的主要部分循环,调用select()来阻止并等待网络活动。
while 1:
#等待至少一个套接字准备好处理
print(sys.stderr, '\nwaiting for the next event')
"""
readable中的socket连接代表有数据可接受,可读取
writable list中存放着你可以对其进行发送(send)操作
exceptional 当连接通信出现error时会把error写到exceptional列表中
"""
readable, writable, exceptional = select.select(input, output, input)
readable列表中的socket可能会有3种可能状态,
1. 如果这个socket是server(它负责监听客户端的连接),那就表示server端已经收到一个新的连接
2. 客户端把数据发送过来了
3. 客户端和服务器端断开了连接,将客户端对象从 列表和队列中删除
for s in readable:
if s is server: # 第1种情况,表示有新的连接进来
connection,add = s.accept() # 接受新的连接
connection.setblocking(0) """
这个时候,为了不阻塞整个程序的运行,我们先将它放入input列表中。
下一次loop时,就会被select去监听,如果这个连接的客户端发来了数据
那么这个连接的fd在server端就会变成就绪的,select就会把这个连接返回到readable列表中
然后在 for s in readable中取出这个连接,开始接受数据
"""
input.append(connection)
message_queue[connection] = Queue() else: # 第2种情况就是,客户端把数据发送了过来
data = s.recv(1024) # 通过recv去接受数据
if data:
print(sys.stderr, 'received "%s" from %s' % (data, s.getpeername()))
message_queue[s].put(data) # 接受到的数据先放到队列中
if s not in output:
output.append(s) # 为了不影响处理与其他客户端的连接,这里不立刻返回数据给客户端
else: # 第3种情况 就是客户端断开了连接,这个时候recv()数据就是空,这个时候就可以跟客户端断开连接
if s in output:
"""
既然断开了连接,就没有必要给客户端发送数据了
如果客户端连接对象还在output中,就把他删除
"""
output.remove(s)
input.remove(s) # 在input列表中也删除掉
# 关闭连接,在队列中也删除
s.close()
del message_queue[s]
对于writable list中的socket,也有几种状态,如果这个客户端连接在跟它对应的queue里有数据,就把这个数据取出来再发回给这个客户端,否则就把这个连接从output list中移除,这样下一次循环select()调用时检测到outputs list中没有这个连接,那就会认为这个连接还处于非活动状态
for s in writable:
try:
next_msg = message_queue[s].get_nowait()
except queue.Empty as e:
output.remove(s)
else:
s.send(next_msg)
最后,exceptional 有异常产生。如果在跟某个socket连接通信过程中出了错误,就把这个连接对象在inputs\outputs\message_queue中都删除,再把连接关闭掉
三、完整代码
server端:
# 通过非阻塞io实现http请求
# select + 回调 + 事件循环 # 使用单线程完成高并发 import select
import socket
import sys
from queue import Queue
import queue # 创建套接字
server = socket.socket()
server.setblocking(False)
server.bind(('localhost',8800))
# 监听传入的连接
server.listen(5) """
第一个是要检查要读取的传入数据的对象列表,
第二个包含在缓冲区中有空间时将接收传出数据的对象,
第三个包含可能有错误的对象(通常是错误的组合输入和输出通道对象)。
"""
input = [server] # 从中读取数据
output = [] # 将数据发送出去
select.select(input,output,input) message_queue = {} # 消息队列 while input:
#等待至少一个套接字准备好处理
print(sys.stderr, '\nwaiting for the next event')
"""
readable中的socket连接代表有数据可接受,可读取
writable list中存放着你可以对其进行发送(send)操作
exceptional 当连接通信出现error时会把error写到exceptional列表中
"""
readable, writable, exceptional = select.select(input, output, input) """
readable列表中的socket可能会有3种可能状态,
1. 如果这个socket是server(它负责监听客户端的连接),那就表示server端已经收到一个新的连接
2. 客户端把数据发送过来了
3. 客户端和服务器端断开了连接,将客户端对象从 列表和队列中删除
"""
for s in readable:
if s is server: # 第一种情况,表示有新的连接进来
connection,add = s.accept() # 接受新的连接
connection.setblocking(0) """
这个时候,为了不阻塞整个程序的运行,我们先将它放入input列表中。
下一次loop时,就会被select去监听,如果这个连接的客户端发来了数据
那么这个连接的fd在server端就会变成就绪的,select就会把这个连接返回到readable列表中
然后在 for s in readable中取出这个连接,开始接受数据
"""
input.append(connection)
message_queue[connection] = Queue() else: # 第2种情况就是,客户端把数据发送了过来
data = s.recv(1024) # 通过recv去接受数据
if data:
print(sys.stderr, 'received "%s" from %s' % (data, s.getpeername()))
message_queue[s].put(data) # 接受到的数据先放到队列中
if s not in output:
output.append(s) # 为了不影响处理与其他客户端的连接,这里不立刻返回数据给客户端
else: # 第3种情况 就是客户端断开了连接,这个时候recv()数据就是空,这个时候就可以跟客户端断开连接
if s in output:
"""
既然断开了连接,就没有必要给客户端发送数据了
如果客户端连接对象还在output中,就把他删除
"""
output.remove(s)
input.remove(s) # 在input列表中也删除掉
# 关闭连接,在队列中也删除
s.close()
del message_queue[s] """
writable list也有几种状态,如果客户端连接在跟它对应的queue里有数据时,就把这个数据取出来再发给用户
否则就把这个连接从output中移除,这样下一次,select调用时检测到output列表中没有这个连接,就会认为这个连接处于非活动状态
"""
for s in writable:
try:
next_msg = message_queue[s].get_nowait()
except queue.Empty as e:
output.remove(s)
else:
s.send(next_msg) """
如果跟某个socket连接通信失败出现错误,就把这个连接对象从 各个列表中删除,再关闭连接
"""
for s in exceptional:
input.remove(s)
for s in output:
output.remove(s)
s.close()
del message_queue[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 select IO多路复用的更多相关文章
- 【python】-- IO多路复用(select、poll、epoll)介绍及实现
IO多路复用(select.poll.epoll)介绍及select.epoll的实现 IO多路复用中包括 select.pool.epoll,这些都属于同步,还不属于异步 一.IO多路复用介绍 1. ...
- {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) 五 ...
- gevent协程、select IO多路复用、socketserver模块 改造多用户FTP程序例子
原多线程版FTP程序:http://www.cnblogs.com/linzetong/p/8290378.html 只需要在原来的代码基础上稍作修改: 一.gevent协程版本 1. 导入geven ...
- 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 poll IO多路复用
一.poll介绍 poll本质上和select没有区别,只是没有了最大连接数(linux上默认1024个)的限制,原因是它基于链表存储的. 本人的另一篇博客讲了 python select : ht ...
- Python网络编程(http协议,IO多路复用、select内核监听)
前言: 什么是IO? 分为IO设备和IO接口两个部分 如Linux系统,I/O操作可以有多种方式 比如DIO(DirectI/O) AIO(AsynchronousI/O异步I/O) Memory-M ...
- IO多路复用(select、poll、epoll)介绍及select、epoll的实现
IO多路复用(select.poll.epoll)介绍及select.epoll的实现 IO多路复用中包括 select.pool.epoll,这些都属于同步,还不属于异步 一.IO多路复用介绍 1. ...
随机推荐
- Looper Handler Mssage
1. 一个Handler只有一个队列;2. 在调用Handler.post(Runnable runnable)方法时,会将runnable封装成一个Message;3. 在队列执行时,会判断当前的M ...
- ffmpeg 在ubuntu上编译环境搭建和开发
步骤如下: 1. 下载 官网永远是王道,呵呵:http://ffmpeg.org/download.html 或者 svn checkout svn://svn.mplayerhq.hu/ffmpeg ...
- 【SPOJ10628】Count on a tree
题目大意:给定一棵 N 个节点的树,点有点权,要求回答 M 个询问,每次询问点 u 到点 v 的简单路径(链)上权值第 K 小是多少. 题解:学习到了树上主席树. 主席树维护序列时,每次将后一个点的树 ...
- poj 1330(RMQ&LCA入门题)
传送门:Problem 1330 https://www.cnblogs.com/violet-acmer/p/9686774.html 参考资料: http://dongxicheng.org/st ...
- P4891 序列
P4891 序列 题目描述 给定两个长度为 n 的序列 A 和 B,定义序列 \(C_i=\max\limits_{j=1}^i A_j\) 定义当前的价值是 $\prod\limits_{i=1}^ ...
- MySQL开启远程连接的方法
默认情况下,mysql只允许本地登录,如果要开启远程连接,则需要修改/etc/mysql/my.conf文件. 一.修改/etc/mysql/my.conf找到bind-address = 127.0 ...
- dubbo注册服务和消费服务---入门篇
本文介绍如何用dubbo+zk来实现一个注册服务 + 消费服务的入门小demo 需要环境:zk服务器 两个maven项目,一个负责提供服务,一个负责消费服务. dubbo-service 服务端 po ...
- 神级程序员通过两句话带你完全掌握Python最难知识点——元类!
千万不要被所谓"元类是99%的python程序员不会用到的特性"这类的说辞吓住.因为 每个中国人,都是天生的元类使用者 学懂元类,你只需要知道两句话: 道生一,一生二,二生三,三生 ...
- 从面向对象的角度重新认识JS世界
一. 背景 距离上一篇JS文章已经20天,经重新总结发现,上一篇概况的有点浅显,适合初学js的入门了解,但对于已经学习js一段时间的人,或者是想系统的了解JS体系,接下来的文章可能会更有帮助. 该系 ...
- 008、Docker 组件如何协作(2018-12-25 周二)
参考https://www.cnblogs.com/CloudMan6/p/6774519.html 以httpd为例,介绍Docker组件间如何协作 root@docker-lab:~# d ...