1. 程式人生 > >Python學習day40

Python學習day40

  今天學習了網路傳輸中的兩個階段、阻塞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