1. 程式人生 > >IO模型之非阻塞IO

IO模型之非阻塞IO

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