協程:gevent模塊,遇到i/o自動切換任務 038
協程 : gevent模塊,遇到io自動切換任務
from gevent import monkey;monkey.patch_all() # 寫在最上面 這樣後面的所有阻塞就全部能夠識別了 import gevent # 直接導入即可 from threading import current_thread import time def eat(name): print(‘%seat1‘% name) # gevent.sleep(2) time.sleep(2) # 加上monkey就能 print(‘%seat2‘% name) print(current_thread().getName()) def play(name): print(‘%splay1‘% name) # gevent.sleep(2) time.sleep(2) print(‘%splay2‘% name) print(current_thread().getName()) g1 = gevent.spawn(eat,‘egon‘) # 創建一個協程對象 spawn括號內第一個參數是函數名 g2 = gevent.spawn(play,‘egon‘) gevent.joinall([g1,g2]) # 將g1.join()和g2.join()合成一步寫了出來print(‘主‘) # 結果為 # egoneat1 # egonplay1 # egoneat2 # DummyThread-1 # 假線程 虛擬線程 其實都在一個線程裏面 # egonplay2 # DummyThread-2 # 主
gevent.sleep(2)模擬的是gevent可以識別的io阻塞,
而 time.sleep(2)或其他阻塞 是不能直接識別的 需要用下面一行代碼打補丁 就可以識別了
from gevent import monkey;monkey.patch_all() 放在time, socket之前 或者直接將其放在文件開頭.
協程是通過自己的程序實現切換 自己能夠控制 只有遇到協程模塊能夠識別io操作的時候, 程序才會進行任務切換 實現並發效果 , 如果所有的程序都沒有io操作 那麽久基本屬於串行執行了.
給大家看一個線程下(協程)來實現多個客戶聊天
io多路復用:模型(解決問題方案)
#服務端 from socket import * import select server = socket(AF_INET, SOCK_STREAM) server.bind((‘127.0.0.1‘,8093)) server.listen(5) # 設置為非阻塞 server.setblocking(False) # 將初始化服務端socket對象加入監聽列表 後面還要動態添加一些conn連接對象, 當accept的時候sk就有感應 , 當recv的時候conn就有動靜 rlist = [server,] rdata = {} # 存放客戶端發送過來的消息 wlist = [] # 等待寫對象 wdata = {} # 存放要返回客戶端的消息 print(‘準備監聽!‘) count = 0 # 寫著計數用的 為了看實驗效果用的 , 沒用 while 1: # 開始select監聽,對rlist中服務端server進行監聽 , select函數阻塞進程 , 直到rlist中套接字被觸發(在此例中,套接字接收到客戶端發來的握手信號 , 從而變得可讀 滿足select函數的可讀條件),被觸發的(有動靜的)套接字(服務器套接字)返回給了rl這個返回值裏面; rl,wl,xl = select.select(rlist,wlist,[],0.5) print(‘%s 次數>>‘%(count),wl) count = count + 1 # 對rl進行循環判斷是否有客戶端連接進來 , 當有客戶端連接進來時select將觸發 for sock in rl: # 判斷當前觸發的是不是socket對象, 當觸發的對象是socket對象時說明有新客戶端accept連接進來了 if sock == server: # 接收客戶端的連接, 獲取客戶端對象和客戶端地址信息 conn,addr = sock.accept() # 把新的客戶端連接加入到監聽列表中 當客戶端連接有接受消息的時候 , select將會被觸發 ,會知道這個鏈接有動靜 , 有消息 , 那麽返回geirl這個返回值列表裏面 rlist.append(conn) else: # 由於客戶端連接進來時socket接收客戶端請求 , 將客戶端連接加入到了監聽列表中(rlist),客戶端發送消息的時候這個鏈接將觸發 # 所以判斷是否是客戶端連接對象觸發 try: data = sock.recv(1024) # 沒有數據的時候,將這個連接關閉掉 , 並從監聽列表中移除 if not data: sock.close() rlist.remove(sock) continue print(‘received{0} from client{1}‘.format(data.decode(),sock)) # 將接收到的客戶端的消息保存下來 rdata[sock] = data.decode() # 將客戶端連接對象和這個對象接受到的消息加工成返回消息 , 並添加到wdata這個字典裏面 wdata[sock] = data.upper() # 要給這個客戶端回復消息的時候, 我們將這個鏈接添加到wlist監聽列表中 wlist.append(sock) # 如果這個連接出錯了,客戶端暴力斷開了(註意 , 還沒有接收到客戶端的消息 , 或者接收消息的過程中出錯了) except Exception: # 關閉這個連接 sock.close() # 在監聽列表中將他刪除 , 因為不管什麽原因 畢竟是斷開了 沒必要再監聽他了 rlist.remove(sock) # 如果現在沒有客戶端請求連接 也沒有客戶端發送消息時 , 開始對發送消息的列表進行處理 , 是否需要發送消息 for sock in wl: sock.send(wdata[sock]) wlist.remove(sock) wdata.pop(sock) # 將以此select監聽列表中有接收數據的conn對象所接收到的消息打印一下 # for k , v in rdata.items(): # print(k,‘發來的消息是‘,v) # # 清空接收到的消息 #rdata.clear()View Code
知識總結 :
1 cs架構 bs架構
2 網絡通信流程:
網卡 mac地址 子網掩碼 網關 dns服務器 dhcp nat(網絡地址轉換) 端口(表示電腦上某個應用程序) 路由器 交換機 集線器 廣播 單播 廣播風暴 ARP協議 路由協議
3 網絡網絡通信協議(**)
osi七層 應表會傳網數物
tcp\ip協議 應用層 網絡層 數據鏈路層 物理層
tcp(*****)
三次握手 客-->服--->客
四次揮手
tcp和udp區別(*****)
tcp: 面向連接 消息可靠 效率相對差 面向流的消息格式 無消息保護邊界
udp:面向無連接 不可靠 效率很高 面向包的消息格式 有消息保護邊界
4 socket
緩沖區:
粘包現象 : 1 連續發送小包並且間隔時間很短 就會發送兩個消息合並在一起的情況 (Nagel優化算法導致的) 2 一次發送數據過大 對方一次未接收完 下次接受的時候連同第一次剩下的消息一同接受了 導致粘包
解決粘包方案 都不知道對方發送的消息長度 1 發送消息之前先發送消息長度 收到對方確認信息後再次發送消息 2 通過struct 模塊 自定義報頭 將消息長度打包成4個字節長度的信息 連同你要發送的數據一並發過去
打包 pack(‘i‘,長度) 長度是個整數
協程:gevent模塊,遇到i/o自動切換任務 038