0. 非阻塞式IO
前面一篇博文解释了什么是阻塞式IO,你一定会发现这种IO存在的问题。在等待IO操作完成的过程中,进程会被投入睡眠,只能干等IO操作完成并返回。如果希望内核在进行IO操作的过程中进程继续运行,那么就需要用非阻塞式IO。
进程把一个套接字设置成非阻塞式,就是在通知内核,在你进行IO操作的过程中,不要把进程投入睡眠,IO操作没完成,返回一个错误即可。非阻塞IO的执行流程如下图所示。
这种IO在执行过程中,需要用户进程隔一段时间去调用一次系统调用,不断轮询,直到返回成功。下面看一个例子
1.例子
我们在上一篇博文的例子上做一下改变
服务端程序
import socket # 导入 socket 模块
import time
import errno
s = socket.socket() # 创建 socket 对象
host = socket.gethostname() # 获取本地主机名
port = 12345 # 设置端口
s.bind((host, port)) # 绑定端口
s.listen(5) # 等待客户端连接
c, addr = s.accept() # 建立客户端连接
c.setblocking(False) #设置与客户端的连接socket为非阻塞式
print("connected from :%s:%s"%(addr[0],addr[1]))
while True:print("wating for client input")try:data = c.recv(1024)except socket.error as e:err = e.args[0]if err == errno.EAGAIN or err == errno.EWOULDBLOCK:time.sleep(1)print("no data available")continueelse:print(e)c.close()breakelse:data = str(data,'utf-8')print(data)if data == '88':c.close()break
print("client closed connection")
s.close()
客户端程序
import socket # 导入 socket 模块s = socket.socket() # 创建 socket 对象
host = socket.gethostname() # 获取本地主机名
port = 12345 # 设置端口号s.connect((host, port))
print("connected to server")
while(True):data = input()s.send(bytes(data,'utf-8'))if data == '88':s.close()break
服务端建立与客户端的链接后,将socket设置为非阻塞式。对一个非阻塞式socket执行recv()函数,如果成功则返回读取到的数据,如果失败则抛出一个socket异常。
启动服务端程序
启动服务端程序后,客户端程序未启动,服务端阻塞在第9行,等待客户端连接。注意这个socket是服务端监听连接的socket,我们没有设置它非阻塞,默认是阻塞式的,因此程序在此处阻塞住。
然后,我们启动客户端程序
启动客户端程序后,连接成功,服务端读取客户端发来的数据。因为此时客户端没有写数据,因此服务器读取失败,recv()立即返回一个socket error,我们捕获这个error,隔一秒钟打印出"no data available"。
这个就是非阻塞式IO,系统调用在数据没准备好的时候,立即返回。这种IO需要用户程序不断去调用系统调用,这个过程称为轮询。