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客戶端請求:客戶端