協程 : 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操做 那麼久基本屬於串行執行了.併發
給你們看一個線程下(協程)來實現多個客戶聊天app
io多路複用:模型(解決問題方案)socket
#服務端 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()
知識總結 : tcp
1 cs架構 bs架構ide
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',長度) 長度是個整數