IO模型之非阻塞IO
阿新 • • 發佈:2018-05-14
accept soc apt book nec sets asset python 輪詢
1. IO模型非阻塞 IO
Linux下,可以通過設置socket使其變為 non-blocking。當對一個non-blocking socket執行讀操作時,流程是這個樣子:
從圖中可以看出,當用戶進程發出read操作時,如果kernel中的數據還沒有準備好,那麽它並不會block用戶進程,而是立刻返回一個error。從用戶進程角度講 ,它發起一個read操作後,並不需要等待,而是馬上就得到了一個結果。用戶進程判斷結果是一個error時,它就知道數據還沒有準備好,於是用戶就可以在本次到下次再發起read詢問的時間間隔內做其他事情,或者直接再次發送read操作。一旦kernel中的數據準備好了,並且又再次收到了用戶進程的system call,那麽它馬上就將數據拷貝到了用戶內存(這一階段仍然是阻塞的),然後返回。
也就是說非阻塞的 recvform 系統調用調用之後,進程並沒有被阻塞,內核馬上返回給進程,如果數據還沒準備好,
此時會返回一個 error 。進程在返回之後,可以幹點別的事情,然後再發起 recvform 系統調用。重復上面的過程,循環往復的進行 recvform 系統調用。這個過程通常被稱之為輪詢。輪詢檢查內核數據,直到數據準備好,再拷貝數據到進程,進行數據處理。需要註意,拷貝數據整個過程,進程仍然是屬於阻塞的狀態。
所以,在非阻塞式 IO 中,用戶進程其實是需要不斷的主動詢問 kernel 數據準備好了沒有
非阻塞 IO 示例
服務端
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1", 8088))
server.listen(5)
server.setblocking(False)
conn_List = [] # 用來存儲接受到的客戶端連接
while True: # 死循環一直監聽
try:
conn, addr = server.accept() # 連接循環
conn_List.append(conn) # 收到了就客戶端的反應就加到 conn_List 列表裏
print(conn_List)
except BlockingIOError: # 沒收到就會拋出異常
conn_del_list = []
conn_send_list = []# 生成兩個列表 一個是需要刪除的客戶端連接 一個是 接受一個要發送數據的連接 還有需要發送的數據
for conn in conn_List: # 循環已經加到 conn_List 列表裏的客戶端連接
try:
data = conn.recv(1024) # 接收客戶端的消息
conn_send_list.append((conn, data))
if not data: # 沒有收到就說明客戶端連接斷開 Linux
conn_del_list.append(conn) # 把沒收到的連接放進要刪除連接的列表裏 註意不能在這裏直接remove 因為循環列表過程中改變列表內元素的值 容易造成意外,所以先加到 conn_del_list 裏面。
continue
except BlockingIOError: # 這個recv的動作是一個IO操作 所以 可能會卡主 設置False之後會報錯 需要捕獲異常 接著循環下一個 知道有客戶端反應
pass
except ConnectionResetError: # window 上的客戶端斷開異常
conn_del_list.append(conn)
conn_send_del_list = [] # 定義一個發送完數據給客戶端需要刪除的列表
for conn_send in conn_send_list: # 循環剛剛 收到消息的列表 以及要發送數據的那個元素元素
conn, data = conn_send
try:
conn.send(data.upper()) # send 可能會卡主 需要捕獲異常
conn_send_del_list.append(conn_send) 發完消息的conn 加到要刪除的列表裏
except BlockingIOError:
continue
for conn_del in conn_del_list: # 刪除剛才再循環中不能刪除的conn
conn_List.remove(conn_del)
for conn_send_del in conn_send_del_list:
conn_send_list.remove(conn_send_del)
客戶端:
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("127.0.0.1", 8081))
while True:
msg = input("msg>>:")
client.send(msg.encode("utf8"))
data = client.recv(1024)
print(data.decode("utf8"))
IO模型之非阻塞IO