Python學習day40
阿新 • • 發佈:2018-11-14
今天學習了網路傳輸中的兩個階段、阻塞IO、非阻塞IO和多路複用
一、網路傳輸中的兩個階段
分別是 waitdata 和 copydata
send就是copydata
recv是waitdata和copydata
二、阻塞 IO
無論是執行緒 程序 還是執行緒 程序池 統統都是阻塞IO
三、非阻塞IO
最直接體現 所有和讀寫相關的函式 都不會阻塞
意味著 在讀寫時 並不能確定目前是否可以讀寫 一旦不能讀寫就丟擲異常
只能使用 try except 來判斷是否可以讀寫
必須不斷的執行系統呼叫 CPU佔用特別高 當沒有任何資料要處理的時候簡直就是病毒
#非阻塞IO模型 客戶端 import socket c = socket.socket() c.connect(('127.0.0.1',9999)) while True: msg = input('>>>:') if not msg:continue c.send(msg.encode('utf-8')) data = c.recv(1024) print(data.decode('utf-8'))
#非阻塞IO模型 伺服器 import socket server = socket.socket() server.setsocket(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) server.bind(('127.0.0.1',9999)) server.listen(5) server.setblocking(False) def data_handler(conn): print('一個新連線..') while True: data = conn.recv(1024) conn.send(data.upper()) clients = [] send_datas = [] del_datas = [] closed_cs = [] while True: try: conn,addr = server.accept() clients.append(conn)except BlockingIOError: for c in clients: try: data = c.recv(1024) if not data: c.close() closed_cs.append(c) continue print('收到%s'%data.decode('utf-8')) send_datas.append((c,data)) except BlockingIOError: pass except ConnectionResetError: c.close() closed_cs.append(c) for data in send_datas: try: data[0].send(data[1].upper()) del_datas.append(data) except BlockingIOError: continue except ConnectionResetError: data[0].close() closed_cs.append(data[0]) del_datas.append(data) for d in del_datas: send_datas.remove(d) del_datas.clear() for c in closed_cs: clients.remove(c) closed_cs.clear()
四、多路複用
核心函式select
幫你檢測所有的連線 找出可以被處理(可以讀寫)的連線
作為處理資料的一方 不再需要重複去向系統詢問 select給你誰 你就處理誰 沒給就不處理
#多路複用模型 客戶端 import socket c = socket.socket() c.connect(('127.0.0.1',9999)) while True: msg = input('>>>:') if not msg:continue c.send(msg.encode('utf-8')) data = c.recv(1024) print(data.decode('utf-8'))
#多路複用模型 伺服器 import socket import select #select幫你從一堆連線中找出來需要被處理的連線 server = socket.socket() #重用埠 server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) server.bind(('127.0.0.1',9999)) server.listen(5) #設定是否為阻塞 預設阻塞 server.setblocking(False) #需要檢測是 是否可讀取的列表 (recv就是一個讀取操作) rlist = [server,] #需要檢測的 是否寫入的列表 (send就是寫入操作) wlist = [] #需要傳送的資料 目前是因為 我們要把接受的資料再發回去 所以搞了這個東西 正常沒有這種需求 #目前客戶端與伺服器端 互動 是必須客戶端傳送資料 服務端才能返回資料 正常沒有這種需求 dic = {} while True: #用於檢測需要處理的連線 需要不斷檢測 所以迴圈 #rl目前可讀的客戶端列表 wl目前可寫的客戶端列表 rl,wl,xl = select.select(rlist,wlist,[]) #select預設阻塞 阻塞到任意一個連線可以被處理 print(len(rl)) #處理可讀的socket for c in rl: #無論是客戶端還是服務端只要可讀就會執行到這裡 if c == server: #接受客戶端的連線請求(一個讀操作) conn,addr = c.accept() #將新連線也交給select來檢測 rlist.append(conn) else: #不是伺服器 就是客戶端 客戶端可讀 可以執行recv try: data = c.recv(1024) if not data: c.close() rlist.remove(c) print('%s 傳送 %s'%(c,data.decode('utf-8'))) #給客戶端傳送資料前要保證目前可以傳送 將客戶端加入檢測列表 wlist.append(c) #正常開發中 不可能必須客戶端傳送資料過來後才能給客戶端傳送 #可以這個新增到檢測列表的操作 應該建立連線後立即執行 #要傳送的資料 dic[c] = data except ConnectionResetError: #客戶端關閉連線 c.close() rlist.remove(c) #處理可寫的socket for c in wl: print(c) try: c.send(dic[c].upper()) #刪除資料 dic.pop(c) #從檢測列表中刪除已傳送完成的客戶端 wlist.remove(c) except ConnectionResetError: c.close() #關閉連線 dic.pop(c) #刪除要傳送的資料 wlist.remove(c) #從待檢測的列表中刪除 except BlockingIOError: #可能快取滿了 發不了 pass