1. 程式人生 > >socket多路複用的技術

socket多路複用的技術

socket多路複用的技術

就像上一篇的思想一樣,socket多路複用就是實現在一個程序中抗併發.核心的思維就是使用中間快取

socket抗併發的原理

在系統核心中存在一個監控的系統,當伺服器啟動的時候,server物件就會在程式中建立,socket已經內建好了一個空間專門用來監控的.如果有客戶端進行伺服器連線,那麼server物件就會發生變化,socket核心就會監控這種變化,一旦發現某個被監控物件發生狀態變化,則直接將這個物件拿出來,進行執行對應的程式

如果是客戶端連線,那麼server就應該建立新物件,new_server,建立的這個新物件依然需要扔到socket中進行監控,因為這裡只是連線,如果客戶端給伺服器傳送資料,這也會導致new_server發生狀態改變,因此需要繼續監控

如果監控到發生狀態變化的不是server物件,而是new_server物件,這就表明客戶端已經發送了資料給服務端,那麼連線的物件new_server就應該去接收資料

我們功能做簡單點,當接收到資料後列印資料,然後再關閉連線,關閉連線後如果不把監控區的物件進行刪除,那麼始終在核心中還是要對其進行監控,就算以後再也不發生狀態變化,其也會佔用記憶體,非常浪費資源,所以需要進行刪除

總體思維還是上一篇的使用中間區域進行暫時儲存建立的連線物件,然後再進行遍歷,將物件進行挨個執行,只是socket使用的是阻塞模式,這就不會導致需要死迴圈來實現,cpu壓力就不大,然後socket核心能夠實現對這個監控區域的監控,一旦有物件發生狀態變化就把它取出來,自動解除阻塞,如果同時間有多個物件狀態發生變化,則全部取出來以列表的形式返回

下面是程式碼實現

from socket import *
import select


def main():
    # 建立物件
    server = socket(AF_INET, SOCK_STREAM)
    # 配置斷開釋放埠
    server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    # 掛起伺服器
    server.bind(('', 8888))
    # 設定成監聽模式
    server.listen()
    # 把server物件扔到列表中取
    save_list = [server]
while True: # 使用select進行監聽save_list中的物件是否發生改變 sockets, _, _ = select.select(save_list, [], []) # 監聽返回的列表中就是我們需要的物件,所以這裡了進行遍歷 for new_obj in sockets: # 如果是客戶端連線動作,則一定會有一個server發生改變,則上面select就會解除阻塞 if new_obj == server: # 如果改變的物件是server則表明這是一個客戶端連線動作,那麼需要建立新物件 new_server, client_info = new_obj.accept() print(f'使用者{client_info}已經連線') # 這個時候就把新物件儲存到save_list中 save_list.append(new_server) else: # 這裡除了第一個server是物件之外其他的元素都是元組,只要是元組就一定是server建立的連線 # 獲取資料 data = new_obj.recv(1024) # 列印資料 print(f'收到使用者的資料{data}') # 接收完成之後關閉連線 new_obj.close() # 從save_list中剔除 save_list.remove(new_obj) if __name__ == '__main__': main()

監控效率不高的原因

原生的socket抗併發實際上不是很理想

首先是對狀態改變的判斷這個動作,其執行的是一種

輪詢

的方式,所謂輪詢,可以簡單的形象理解就是挨個問,就像你進一個教室,想找出名字叫吊炸天的人,你又不認知班上任何一個人,那麼你就從第一排開始一個一個的去問:‘你是吊炸天麼’.

其次是其本身的抗併發能力有限,如果是32位的系統就是1024併發數,如果是64位的系統就是2048併發數

後面有poll這種技術,實質的輪詢問題沒有解決,只是把併發數改到了沒有限制

那麼socket和poll都是存在輪詢這個浪費時間的過程

沒辦法,下一篇介紹epoll技術能夠解決這個問題