0.IO多路复用


IO多路复用的模型如上图所示,有了IO多路复用,我们就可以调用select或者poll,用户程序阻塞在这两个系统调用的某一个之上,而不是阻塞在真正的IO系统调用之上。IO多路复用的优点在于,他可以同时等待多个描述符就绪。下面看一个例子

1. select实例

服务端程序如下:

import select
import socket
import time
s = socket.socket()         # 创建 socket 对象
host = socket.gethostname() # 获取本地主机名
port = 12345                # 设置端口
s.bind((host, port))        # 绑定端口
s.listen(5)                 # 监听,等待客户端连接
s.setblocking(False)    #设置监听socket为非阻塞
inputs = [s]
outputs = []

while inputs:
    print ('waiting for the next event')
    readable, writable, exceptional = select.select(inputs, outputs, inputs)
    for v in readable:
        if v is s:
            connection, client_addr = v.accept()
            print ('connection from', client_addr)
            connection.setblocking(0)				#设置连接socket为非阻塞
            inputs.append(connection)              #添加socket至input列表
        else :
            data = v.recv(1024)
            data = str(data,'utf-8')
            print(data)
            if data == '88':
                inputs.remove(v)
                v.close()

select函数有四个参数,第一个是需要监听的读描述符列表,第二个是写描述符列表,第三个是异常描述符列表,第四个是超时时间。第四个参数是可选的,如果没有设置,则永不超时。select返回三个列表,分别是读就绪的描述符、写就绪的描述符和异常描述符。
客户端程序

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

首先启动服务端程序,程序打印一行信息后,阻塞在select调用。

启动一个客户端程序后,此时监听描述符变为可读,进入for循环,调用accept()函数,将返回的连接描述符设置为非阻塞,写入inputs列表。同时打印出客户端的地址和端口号

再启动一个客户端程序,重复上面的过程

这样selec函数就监听了3个描述符。遍历返回的可读列表就可以分别对就绪的描述符进行相应的操作。