IO 的模型:
了解概念:
阻塞 I/O:应用程序执行 I/O 操作后,如果没有获得响应,就会阻塞当 前线程,自然就不能执行其他任务
非阻塞 I/O:应用程序执行 I/O 操作后,不会阻塞当前的线程,可以继续执 行其他的任务,随后再通过轮询或者事件通知的形式,获取调用的结果
同步 I/O:应用程序执行 I/O 操作后,要一直等到整个 I/O 完成后,才能获 得 I/O 响应
异步 I/O:应用程序执行 I/O 操作后,不用等待完成和完成后的响应,而是 继续执行就可以
阻塞 IO(blocking IO)
在 Linux 中,默认情况下所有的 socket 都是 blocking,一个典型的读操作流程大概是这样:
552 x 331
当用户进程调用了 recvfrom 这个系统调用,kernel 就开始了 IO 的第一个阶段:准备数据。对于 network io 来说,很多时候数据在一开始还没有到达(比如,还没有收到一个完整的 udp 包),这个时候 kernel 就要等待足够的数据到来
而在用户进程这边,整个进程会被阻塞,当 kernel 一直等到数据准备好了,它就会将数据从 kernel 中拷贝到用户内存
然后 kernel 返回结果,用户进程才解除 block 的状态,重新运行起来
所以,blocking IO 的特点就是在 IO 执行的两个阶段(等待数据和拷贝数据两个阶段)都被 block 了
411 x 431
实际上,除非特别指定,几乎所有的 IO 接口(包括 socket 接口)都是阻塞型的。这给网络编程带来了一个很大的问题。如在调用 recv(1024)的同时,线程将被阻塞,在此期间,线程将无法执行任何运算或响应任何的网络请求。
下面使用 python 代码来演示:
服务端:
from socket import *
from threading import Thread
def communicate(conn):
while True:
try: data = conn.recv(1024) if not data: break conn.send(data.upper()) except ConnectionResetError: break
conn.close() server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1', 8080))
server.listen(5)
while True:
print('starting...')
conn,addr = server.accept()
print(addr)
t = Thread(target=communicate, args=(conn,))
t.start() server.close()
客户端:
from socket import *
client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1', 8080))
while True:
msg = input("请输入数据:").strip()
if not msg:
continue
client.send(msg.encode('utf-8'))
data = client.recv(1024)
print(data.decode('utf-8')) client.close()
打开多个客户端,进行发送消息,多发送几次就会出现堵塞的情况:
728 x 297 791 x 323
客户端发送的信息,服务器没有及时返回结果
本文由柠檬班学员库里原创,转载需注明出处!
想了解更多咨询的同学扫描下方二维码,可以加Q群领取学习资料:753665853 备注: