IO多路復用是一個系統層面的概念,讓我們先搞清楚為什麼使用IO多路復用:
由於進程的執行過程是線性的(也就是順序執行),當我們調用低速系統I/O(read,write,accept等等),進程可能阻塞,此時進程就阻塞在這個調用上,不能執行其他操作.阻塞很正常. 接下來考慮這麼一個問題:一個服務器進程和一個客戶端進程通信,服務器端read(sockfd1,bud,bufsize),此時客戶端進程沒有發送數據,那麼read(阻塞調用)將阻塞直到客戶端調用write(sockfd,but,size)發來數據. 在一個客戶和服務器通信時這沒什麼問題,當多個客戶與服務器通信時,若服務器阻塞於其中一個客戶sockfd1,當另一個客戶的數據到達套接sockfd2時,服務器不能處理,仍然阻塞在read(sockfd1,...)上;此時問題就出現了,不能及時處理另一個客戶的服務,咋麼辦?I/O多路復用來解決! IO多路復用本質上是在同一個線程或進程中,通過撥動開關的方式來執行多個IO操作。注意實際上每個IO操作都是獨立進行的。只是由原來的一對一變成了多對多。如下圖:比較坑爹的是epoll只用Linux提供支持,默認集成到了Linux內核中。
1 2 3 4 5 6Windows Python:
提供: select
Mac Python:
提供: select
Linux Python:
提供: select、poll、epoll
對於select操作:
1 2 3 4 5 6 7 8 9 10 11句柄列表
11
, 句柄列表
22
, 句柄列表
33
=
select.select(句柄序列
1
, 句柄序列
2
, 句柄序列
3
, 超時時間)
參數: 可接受四個參數(前三個必須)
返回值:三個列表
select方法用來監視文件句柄,如果句柄發生變化,則獲取該句柄。
1
、當 參數
1
序列中的句柄發生可讀時(accetp和read),則獲取發生變化的句柄並添加到 返回值
1
序列中
2
、當 參數
2
序列中含有句柄時,則將該序列中所有的句柄添加到 返回值
2
序列中
3
、當 參數
3
序列中的句柄發生錯誤時,則將該發生錯誤的句柄添加到 返回值
3
序列中
4
、當 超時時間 未設置,則select會一直阻塞,直到監聽的句柄發生變化
當 超時時間 =
1
時,那麼如果監聽的句柄均無任何變化,則select會阻塞
1
秒,之後返回三個空列表,如果監聽的句柄有變化,則直接執行。
#!/usr/bin/env python # -*- coding:utf-8 -*- import socket import select sk1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sk1.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sk1.bind(('127.0.0.1',8002)) sk1.listen(5) sk1.setblocking(0) inputs = [sk1,] while True: readable_list, writeable_list, error_list = select.select(inputs, [], inputs, 1) for r in readable_list: # 當客戶端第一次連接服務端時 if sk1 == r: print 'accept' request, address = r.accept() request.setblocking(0) inputs.append(request) # 當客戶端連接上服務端之後,再次發送數據時 else: received = r.recv(1024) # 當正常接收客戶端發送的數據時 if received: print 'received data:', received # 當客戶端關閉程序時 else: inputs.remove(r) sk1.close()利用select實現偽並發 服務端
#!/usr/bin/env python # -*- coding:utf-8 -*- import socket ip_port = ('127.0.0.1',8002) sk = socket.socket() sk.connect(ip_port) while True: inp = raw_input('please input:') sk.sendall(inp) sk.close() 利用select實現偽同時處理多個Socket客戶端請求:客戶端