一.数据流
是一组有序,有起点和终点的字节的数据序列。是只能被读取一次或少数几次的点的有序序列。其包括输入流和输出流。
1.输入流
只能读不能写,从键盘中获取文件
2.输出流
只能写不能读,像显示器,打印机或文件中传输数据
二.IO( I:input(输入),O:output(输出))
1.解释
(1)I/O操作
在信息的交换过程中,对这些流进行数据的收发操作,例如:输入与输出。
2.交互
三.基本IO模型
1.分类
(1)阻塞IO
只会专门等待一件事物完成后才会进行下一项任务。(例如:专门等鱼上钩,等的时候不做其他事情,鱼上钩才会结束等的动作,做其他事情,开始烧水)
优点:进程阻塞挂起,不消耗CPU资源,能够及时响应每个操作且难度较低
缺点:进程阻塞的时候不能进行其他操作,不适合并发量大的应用开发
import socket
# 得到服务端套接字
server = socket.socket()
# 绑定IP和端口
server.bind(('127.0.0.1',9090))
# 最大监听数
server.listen(10)
all_conn = []
for i in range(2):
# conn,对等连接套接字,addr 客户端信息
conn,addr = server.accept()
all_conn.append(conn)
for conn in all_conn:
data = conn.recv(1024)
if data:
print(data)
else:
conn.close()
all_conn.remove(conn)
server.close()
import socket
client = socket.socket()
client.connect(('127.0.0.1',9090))
client.send(b'ok')
client.close()
(2)非阻塞IO
在进行一个任务的时候,可以做别的事,但必须每隔一段时间就查看任务的进程。(例如:不需要把时间都花费在钓鱼上,等到鱼上钩的时候做其他事情,烧水,每隔一段时间回来看下鱼上钩没有,把鱼掉上来,轮询。)
优点:在线程等待的时候去做其他任务,效率高
缺点:消耗大,而轮询对于CPU来说有较大的浪费,消耗。延迟,每隔一段时间才去轮询一次操作,而任务可能就在中间任意时间完成,数据响应不及时。
服务端:
import socket
from time import sleep
# 得到服务端套接字
server = socket.socket()
#设置为非阻塞套接字,需要在其他操作之前设置
server.setblocking(False)
# 绑定IP和端口
server.bind(('127.0.0.1',2020))
# 最大监听数
server.listen(10)
all_conn = []
while True:
try:
conn,addr = server.accept()
conn.setblocking(False)
all_conn.append(conn)
except BlockingIOError:
pass
except Exception as e:
print(f'发生异常{e}')
for conn in all_conn:
try:
data = conn.recv(1024)
if data:
print(data)
conn.send(data)
else:
conn.close()
all_conn.remove(conn)
except BlockingIOError:
pass
except Exception as e:
print(f'发生异常{e}')
客户端:
import socket
from time import sleep #导入休眠模块
client = socket.socket()
client.connect(('127.0.0.1',9090))
for i in range(100):
client.send(b'liangge')
data = client.recv(1024)
print(data)
sleep(1)
client.close()
(3)IO多路复用(事件驱动IO)
利用一个任务去查看另一个任务的进展(例如:雇佣一个人专门看鱼有没有上钩,不断轮询看这个鱼竿,如果有鱼上钩就通知把鱼钓上来,烧水。)
优点:占用资源少,消耗cpu少
缺点:需要两个系统调用
原理:select/epoll 这个 function 会不断的轮询所负责的 socket ,当某个 socket 有数据到达了,就通过用户
epoll是最佳打工仔,是 Linux 最好的 IO 多路复用器,且只有 linux 有。epoll 是一个惰性事件回调,用户自己去调用回调过程,操作系统进行通知
服务端:
import socket
import selectors # IO多路复用选择器模块
epoll_select = selectors.EpollSelector() # 实例化
# selectors.EpollSelector() # 默认选择器,自动根据不同的操作系统进行自动选择
server = socket.socket()
server.bind(('127.0.0.1',9090))
server.listen(10)
def f_recv(conn):
data = conn.recv(1024)
if data:
print(data)
conn.send(data)
else:
conn.close()
def f_accept(server):
conn,addr = server.accept()
epoll_select.register(conn, selectors.EVENT_READ,f_recv)
# selectors.EVENT_READ 有事件发生的时候,调用回调函数,第三个参数
epoll_select.register(server,selectors.EVENT_READ,f_accept)
while True:
events = epoll_select.select() # 查询
for key,mask in events:
func = key.data # 函数体
conn = key.fileobj # 已经注册的套接字
func(conn) # f_accept(conn)
print(events)
客户端:
import socket
from time import sleep #导入休眠模块
client = socket.socket()
client.connect(('127.0.0.1',9090))
for i in range(100):
client.send(b'liangge')
data = client.recv(1024)
print(data)
sleep(1)
client.close()
四.并发与并行
1.并发
同一时间段发生多件事情
同一时刻只能有一条指令执行,但是快速的切换执行,宏观看起来就是一起执行,但微观并不是同时执行的。
2.并行
同一时间点发生多件事情
无论从微观上来说还是宏观都是一起执行的(而目前 python 只能实现并发,向并行靠拢)
附(今日份学习):
阻塞IO:
非阻塞 IO
IO 多路复用