1. 程式人生 > >協程:gevent模塊,遇到i/o自動切換任務 038

協程:gevent模塊,遇到i/o自動切換任務 038

條件 ear 出錯 打印 close 模塊 gif 應用程序 在一起

協程 : 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