標籤:server 系統 添加 報錯 output NPU 監聽 非同步io 直接
詳細帶圖
第一種IO模型
阻塞
第二種IO模型
非阻塞IO
原理:由阻塞改為非阻塞,每隔一段時間回來看看(每每看一次,核心態會發送一次系統調用),若沒有幹其他事情(適用於代碼量小),進程主動輪詢
當伺服器端通訊端被setblocking(false),通訊端為非阻塞通訊端,當接收不到用戶端連結時,直接觸發異常(證明現階段CPU空閑,可以做其他事情)。所以可以在異常處理處理其他任務。
缺點
發送太多次系統調用
資料得不到及時處理
第三種IO模型
IO多工(單線程下實現並發,原理就是利用IO空閑時間實現並發)(select ,poll,epoll)
select
select函數是系統調用介面。並且select也是會阻塞,
但其好處是同時可監控多個連結,這是決定了為何如此流行.
講到這裡還是莫名其妙,但是可以把socket.accpet()返回的conn添加至監聽列表裡,就明白了。因為監聽那麼多,總有一個是核心態有資料的,也就是說總會可以操作的
注意的是:預設,若核心態的資料沒有取至使用者態,則返回的可讀列表裡會一直存在該通訊端。還有監聽數不能超過1024
# select 類比一個socket server,注意socket必須在非阻塞情況下才能實現IO多工。# 接下來通過例子瞭解select 是如何通過單進程實現同時處理多個非阻塞的socket串連的。#server端import selectimport socketimport queueserver = socket.socket()server.bind((‘localhost‘,9000))server.listen(1000)server.setblocking(False) # 設定成非阻塞模式,accept和recv都非阻塞# 這裡如果直接 server.accept() ,如果沒有串連會報錯,所以有資料才調他們# BlockIOError:[WinError 10035] 無法立即完成一個非阻塞性通訊端操作。msg_dic = {}inputs = [server,] # 交給核心、select檢測的列表。# 必須有一個值,讓select檢測,否則報錯提供無效參數。# 沒有其他串連之前,自己就是個socket,自己就是個串連,檢測自己。活動了說明有連結outputs = [] # 你往裡面放什麼,下一次就出來了while True: readable, writeable, exceptional = select.select(inputs, outputs, inputs) # 定義檢測 #新來串連 檢測列表 異常(斷開) # 異常的也是inputs是: 檢測那些串連的存在異常 print(readable,writeable,exceptional) for r in readable: if r is server: # 有資料,代表來了一個新串連 conn, addr = server.accept() print("來了個新串連",addr) inputs.append(conn) # 把串連加到檢測列表裡,如果這個串連活動了,就說明資料來了 # inputs = [server.conn] # 【conn】只返回活動的串連,但怎麼確定是誰活動了 # 如果server活動,則來了新串連,conn活動則來資料 msg_dic[conn] = queue.Queue() # 初始化一個隊列,後面存要返回給這個用戶端的資料 else: try : data = r.recv(1024) # 注意這裡是r,而不是conn,多個串連的情況 print("收到資料",data) # r.send(data) # 不能直接發,如果用戶端不收,資料就沒了 msg_dic[r].put(data) # 往裡面放資料 outputs.append(r) # 放入返回的串連隊列裡 except ConnectionResetError as e: print("用戶端斷開了",r) if r in outputs: outputs.remove(r) #清理已斷開的串連 inputs.remove(r) #清理已斷開的串連 del msg_dic[r] ##清理已斷開的串連 for w in writeable: # 要返回給用戶端的串連列表 data_to_client = msg_dic[w].get() # 在字典裡取資料 w.send(data_to_client) # 返回給用戶端 outputs.remove(w) # 刪除這個資料,確保下次迴圈的時候不返回這個已經處理完的串連了。 for e in exceptional: # 如果串連斷開,刪除串連相關資料 if e in outputs: outputs.remove(e) inputs.remove(e) del msg_dic[e]#*************************clientimport socketclient = socket.socket()client.connect((‘localhost‘, 9000))while True: cmd = input(‘>>> ‘).strip() if len(cmd) == 0 : continue client.send(cmd.encode(‘utf-8‘)) data = client.recv(1024) print(data.decode())client.close()
poll
相比select,就是一點,就是把最大連結數提高了
epoll
epoll實現原理跟select一樣(原理圖一樣),但是實現機制不同(也就是,資料來了,是怎麼找到是指定通訊端的)
select內部原理是採取輪詢,就好比,在一間教室裡老師聽到有一學生拍了下桌子,但不知道具體誰拍的,
只有一個一個問。所以很多時間都消耗在輪詢問上面了(是每一次有資料到來時候,都要輪詢)
epoll內部原理是自報家門,就好比,學生不拍桌子,直接站起來我是誰,我要反了
Ngexi伺服器底層就是epoll,多進程多線程開得不多,但是一個線程裡面通過epoll實現多串連
非同步IO
非同步是整個過程中一點阻塞都沒有。你發完請求你就去做其他事情,等到核心把資料收到且複製到使用者態,再發給你一個訊號。
所以說以上IO多工,不是非同步。
同步IO
在IO操作完成之前存在阻塞。而非同步IO是毫無阻塞。所以多工都屬於同步IO
python IO模型