IO多路复用
1.事件驱动模型
上一篇写的协程仅仅是切换,本身不能实现并发,什么时候切换也不知道
那么什么时候切回去呢?怎么确定IO操作完了?通过回调函数
对于事件驱动型程序模型,它的流程大致如下: 开始--->初始化--->等待
事件驱动程序在启动之后,就在那等待,等待什么呢?等待被事件触发。传统编程下也有“等待”的时候,比如在代码块D中,你定义了一个input(),需要用户输入数据。但这与下面的等待不同,传统编程的“等待”,比如input(),你作为程序编写者是知道或者强制用户输入某个东西的,或许是数字,或许是文件名称,如果用户输入错误,你还需要提醒他,并请他重新输入。事件驱动程序的等待则是完全不知道,也不强制用户输入或者干什么。只要某一事件发生,那程序就会做出相应的“反应”。这些事件包括:输入信息、鼠标、敲击键盘上某个键还有系统内部定时器触发。
比如socketserver,多个客户端连接,单线程下实现并发效果,就叫多路复用。
文件描述符(File descriptor)是计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念。
文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。
# 在linux的IO多路复用中有水平触发,边缘触发两种模式,这两种模式的区别如下:
#
# 水平触发:如果文件描述符已经就绪可以非阻塞的执行IO操作了,此时会触发通知.允许在任意时刻重复检测IO的状态,
# 没有必要每次描述符就绪后尽可能多的执行IO.select,poll就属于水平触发.
#
# 边缘触发:如果文件描述符自上次状态改变后有新的IO活动到来,此时会触发通知.在收到一个IO事件通知后要尽可能
# 多的执行IO操作,因为如果在一次通知中没有执行完IO那么就需要等到下一次新的IO活动到来才能获取到就绪的描述
# 符.信号驱动式IO就属于边缘触发.
select调用是内核级别的,select轮询相对非阻塞的轮询的区别在于—前者可以等待多个socket,能实现同时对多个IO端口进行监听,当其中任何一个socket的数据准好了,就能返回进行可读,然后进程再进行recvfrom系统调用,将数据由内核拷贝到用户进程,当然这个过程是阻塞的。
server端
#__author: greg
#date: 2017/9/24 21:52
import socket sk1=socket.socket()
sk1.bind(('127.0.0.1',8001))
sk1.listen() sk2=socket.socket()
sk2.bind(('127.0.0.1',8002))
sk2.listen() sk3=socket.socket()
sk3.bind(('127.0.0.1',8003))
sk3.listen() # inputs=[sk1,sk2,sk3,]
inputs=[sk1,] import select
while True:
# [sk1, sk2,sk3,],select内部自动监听sk1,sk2,sk3三个对象,一旦某个句柄发生变化
# 如果有人链接sk1
# r_list=[sk1] #如果有人第一次连接,,sk1发生变化
#句柄列表11, 句柄列表22, 句柄列表33 = select.select(句柄序列1, 句柄序列2, 句柄序列3, 超时时间)
r_list,w_list,e_list=select.select(inputs,[],[],1)
print(r_list)
for sk in r_list:
#conn每一个连接对象
conn,address=sk.accept()
conn.sendall(bytes('hello',encoding='utf8'))
conn.close() # while True:
# conn, address = sk.accept()
# while True:
# content_bytes=conn.recv(1024)
# content_str=str(content_bytes,encoding='utf8')
# conn.sendall(bytes(content_bytes+'好',encoding='utf8'))#sendall就是用while循环调用send
# conn.close()
client端
#__author: greg
#date: 2017/9/24 22:30
import socket obj=socket.socket()
obj.connect(('127.0.0.1',8001))
content=str(obj.recv(1024),encoding='utf8')
print(content)
obj.close()
实例,并发聊天
sk=socket.socket()
sk.bind(("127.0.0.1",8801))
sk.listen(5)
inputs=[sk,]
while True:
r,w,e=select.select(inputs,[],[],5)
print(len(r)) for obj in r:
if obj==sk:
conn,add=obj.accept()
print(conn)
#[<socket.socket fd=420, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8801), raddr=('127.0.0.1', 55504)>]
inputs.append(conn)
else:
data_byte=obj.recv(1024)
print(str(data_byte,'utf8'))
inp=input('回答%s号客户>>>'%inputs.index(obj))
obj.sendall(bytes(inp,'utf8')) print('>>',r)
文件描述符其实就是咱们平时说的句柄,只不过文件描述符是linux中的概念。注意,我们的accept或recv调用时即向系统发出recvfrom请求
(1) 如果内核缓冲区没有数据--->等待--->数据到了内核缓冲区,转到用户进程缓冲区;
(2) 如果先用select监听到某个文件描述符对应的内核缓冲区有了数据,当我们再调用accept或recv时,直接将数据转到用户缓冲区。
如何在某一个client端退出后,不影响server端和其它客户端正常交流
try:
data_byte=obj.recv(1024)
print(str(data_byte,'utf8'))
inp=input('回答%s号客户>>>'%inputs.index(obj))
obj.sendall(bytes(inp,'utf8'))
except Exception:
inputs.remove(obj)
select.seclect的四个参数
select(rlist, wlist, xlist[, timeout]) -> (rlist, wlist, xlist) Wait until one or more file descriptors are ready for some kind of I/O.
The first three arguments are sequences of file descriptors to be waited for:
rlist -- wait until ready for reading
wlist -- wait until ready for writing
xlist -- wait for an ``exceptional condition''
If only one kind of condition is required, pass [] for the other lists.
A file descriptor is either a socket or file object, or a small integer
gotten from a fileno() method call on one of those. The optional 4th argument specifies a timeout in seconds; it may be
a floating point number to specify fractions of seconds. If it is absent
or None, the call will never time out. The return value is a tuple of three lists corresponding to the first three
arguments; each contains the subset of the corresponding file descriptors
that are ready.
等待一个或多个文件描述符准备好进行某种I / O操作。前三个参数是等待文件描述符的序列:
rlist - 等待直到准备好阅读
wlist - 等待,直到准备好写作
xlist - 等待一个“例外条件”
如果只需要一种条件,则为其他列表传递[]。 文件描述符可以是套接字或文件对象,也可以是其中一个fileno()方法调用获得的小整数。可选的第四个参数指定以秒为单位的超时时间; 它可能是一个浮点数来指定几分之一秒。 如果不存在或无,则调用永远不会超时。返回值是与前三个参数对应的三个列表的元组; 每个都包含准备好的相应文件描述符的子集。
server端
#__author: greg
#date: 2017/9/24 22:58
import socket
import select sk1=socket.socket()
sk1.bind(('127.0.0.1',8001))
sk1.listen() # inputs=[sk1,sk2,sk3,]
inputs=[sk1,]
outputs=[] message_dict={} while True:
# [sk1, sk2,sk3,],select内部自动监听sk1,sk2,sk3三个对象,一旦某个句柄发生变化
# 如果有人链接sk1
# r_list=[sk1] #如果有人第一次连接,,sk1发生变化 # select内部自动监听socket对象,一旦socket变换感知到
r_list,w_list,e_list=select.select(inputs,outputs,inputs,1)
print('正在监听的socket对象%d'%len(inputs))
print(r_list)
for sk_or_conn in r_list:
#conn每一个连接对象
if sk_or_conn==sk1:
#表示有新用户来连接
conn,address=sk_or_conn.accept()
inputs.append(conn)
message_dict[conn]=[]
else:
#有老用户发消息
try:
data_bytes=sk_or_conn.recv(1024)
# data_str=str(data_bytes,encoding='utf8')
# sk_or_conn.sendall(bytes(data_str+'好',encoding='utf8'))
except Exception as e:
inputs.remove(sk_or_conn)
else:
#用户正常发送消息
data_str=str(data_bytes,encoding='utf8')
print(data_str)
# sk_or_conn.sendall(bytes(data_str+'好',encoding='utf8'))
message_dict[sk_or_conn].append(data_str)
outputs.append(sk_or_conn) #w_list仅仅保存了谁给我发过消息
for conn in w_list:
recv_str=message_dict[conn][0]
del message_dict[conn][0]
conn.sendall(bytes(recv_str+'好',encoding='utf8'))
outputs.remove(conn)
for sk in e_list:
inputs.remove(sk)
客户端
#__author: greg
#date: 2017/9/24 22:20
import socket obj=socket.socket()
obj.connect(('127.0.0.1',8001))
# content=str(obj.recv(1024),encoding='utf8')
# print(content)
while True:
inp=input('>>>')
obj.sendall(bytes(inp,encoding='utf8'))
ret=str(obj.recv(1024),encoding='utf8')
print(ret)
obj.close()
IO多路复用的更多相关文章
- Python(七)Socket编程、IO多路复用、SocketServer
本章内容: Socket IO多路复用(select) SocketServer 模块(ThreadingTCPServer源码剖析) Socket socket通常也称作"套接字" ...
- IO多路复用概念性
sellect.poll.epoll三者的区别 先来了解一下什么是进程切换 为了控制进程的执行,内核必须有能力挂起正在CPU上运行的进程,并恢复以前挂起的某个进程的执行,这种行为为进程的切换,任务切换 ...
- IO多路复用之select总结
1.基本概念 IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程.IO多路复用适用如下场合: (1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/ ...
- IO多路复用之poll总结
1.基本知识 poll的机制与select类似,与select在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是poll没有最大文件描述符数量的限制.poll和selec ...
- IO多路复用之epoll总结
1.基本知识 epoll是在2.6内核中提出的,是之前的select和poll的增强版本.相对于select和poll来说,epoll更加灵活,没有描述符限制.epoll使用一个文件描述符管理多个描述 ...
- python中的IO多路复用
在python的网络编程里,socetserver是个重要的内置模块,其在内部其实就是利用了I/O多路复用.多线程和多进程技术,实现了并发通信.与多进程和多线程相比,I/O多路复用的系统开销小,系统不 ...
- socket的IO多路复用
IO 多路复用 I/O多路复用指:通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作. Linux Linux中的 select,poll, ...
- IO多路复用及ThreadingTCPServer源码阅读
IO多路复用 socket模块是阻塞的,通过socket建立的服务端可以接收多个请求,但只能同时处理一个请求,其他请求都被阻塞.可以通过IO多路复用解决这个问题,socketserver内部使用的就是 ...
- 【知乎网】Linux IO 多路复用 是什么意思?
提问一: Linux IO多路复用有 epoll, poll, select,知道epoll性能比其他几者要好.也在网上查了一下这几者的区别,表示没有弄明白. IO多路复用是什么意思,在实际的应用中是 ...
- Python自动化之IO多路复用
单线程.多线程和异步对比图 灰色的是阻塞 IO多路复用 用户空间与内核空间 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方).操作系统的核心 ...
随机推荐
- Android - include属性用法
include属性用法 本文地址: http://blog.csdn.net/caroline_wendy Android的layout中, 能够使用include属性样式, 这样能够把不同的layo ...
- PAT 1004 To Fill or Not to Fill (25)
题目描写叙述 With highways available, driving a car from Hangzhou to any other city is easy. But since the ...
- 原来你是这样的JAVA[01]-基础一瞥
1.Java是什么呢? Java不仅是一门语言,而且是一个完整的平台,有一个庞大的库,其中包含了很多可重用的代码和一个提供安全性.跨操作系统的可移植性以及自动垃圾收集等服务的执行环境. 2.JDK 和 ...
- C#基础知识 简单说明泛型的优点
有关泛型的优缺点在网上有很多篇文章,也足以说明问题,我就不去复制粘贴了(而且内容有些多),由于记性不太好,所以自己做个简单明了的总结. 泛型的优点主要有两个: "性能" " ...
- Intellij IDEA 使用小结
快捷键 核心快捷键 IntelliJ IDEA 作为一个以快捷键为中心的 IDE,为大多数操作建议了键盘快捷键.在这个主题中,您可以找到最不可缺少的列表,使 IntelliJ IDEA 轻松实现第一步 ...
- 使用MyBatis缓存
(1).为什么需要使用缓存:: MyBatis是一个持久层(数据库层)映射框架,在所有访问数据库的操作中,无疑数据查询是最耗费数据库资源的操作了,因为你一次可能需要查询成千上百万条记录(如果你不加限制 ...
- 修改Intellij IDEA中工程对应的Java SDK、Scala SDK
如果编译Scala工程时,遇到如下异常: can't expand macros compiled by previous versions of Scala 很可能是工程的scala版本,和依赖的包 ...
- go基础之--函数和map
在整理函数之前先整理一下关于指针 指针 普通类型变量存的就是值,也叫值类型.指针类型存的是地址,即指针的值是一个变量的地址.一个指针指示值所保存的位置,不是所有的值都有地址,但是所有的变量都有.使用指 ...
- 《重构——改善既有代码的设计》【PDF】下载
<重构--改善既有代码的设计>[PDF]下载链接: https://u253469.ctfile.com/fs/253469-231196358 编辑推荐 重构,一言以蔽之,就是在不改变外 ...
- DotNetCore跨平台~System.DrawingCore部署Linux需要注意的
回到目录 你在windows上使用图像组件没有任务问题,但部署到linux之后,将注意以下几点: 安装nuget包ZKWeb.System.Drawing 项目里还是引用System.DrawingC ...