最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python中IO多路復(fù)用模塊selector的用法詳解

 更新時(shí)間:2024年02月20日 09:00:21   作者:金色旭光  
selector?是一個(gè)實(shí)現(xiàn)了IO復(fù)用模型的python包,實(shí)現(xiàn)了IO多路復(fù)用模型的?select、poll?和?epoll?等函數(shù),下面就跟隨小編一起來(lái)學(xué)習(xí)一下它的具體使用吧

selector 簡(jiǎn)介

selector 是一個(gè)實(shí)現(xiàn)了IO復(fù)用模型的python包,實(shí)現(xiàn)了IO多路復(fù)用模型的 select、poll 和 epoll 等函數(shù)。它允許程序同時(shí)監(jiān)聽(tīng)多個(gè)文件描述符(例如套接字),并在其中任何一個(gè)就緒時(shí)進(jìn)行相應(yīng)的操作。這樣可以有效地管理并發(fā) I/O 操作,提高程序的性能和資源利用率。

本篇主要講解selector編程示例,以socket編程為主題,首先分析阻塞IO模型的網(wǎng)絡(luò)編程,然后對(duì)比selector實(shí)現(xiàn)的IO多路復(fù)用模型的網(wǎng)絡(luò)編程。

阻塞IO模型下的 socket 網(wǎng)絡(luò)編程

通過(guò)socket實(shí)現(xiàn)最簡(jiǎn)單的客戶(hù)端和服務(wù)端通信的功能,阻塞IO模型的特點(diǎn)就是在文件IO或網(wǎng)絡(luò)IO時(shí)獲取數(shù)據(jù)的函數(shù)會(huì)一直阻塞,直到數(shù)據(jù)到來(lái)。服務(wù)端:server.py

import socket

# 創(chuàng)建TCP套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 綁定IP地址和端口號(hào)
server_address = ('0.0.0.0', 12346)
server_socket.bind(server_address)

# 監(jiān)聽(tīng)連接
server_socket.listen()

# 接受客戶(hù)端連接請(qǐng)求
print("服務(wù)器已啟動(dòng),等待客戶(hù)端連接...")
client_socket, client_address = server_socket.accept()
print(f"與客戶(hù)端 {client_address} 建立連接")

# 向客戶(hù)端發(fā)送消息
message = "歡迎連接到服務(wù)器!"
client_socket.sendall(message.encode())

while True:

    # 從客戶(hù)端接收消息
    data = client_socket.recv(1024).decode()
    print(f"客戶(hù)端消息:{data}")
    client_socket.sendall(f"服務(wù)器收到消息:{data}".encode())
    if data == "close":
        # 關(guān)閉客戶(hù)端套接字
        client_socket.close()

客戶(hù)端:client.py

import socket

# 創(chuàng)建TCP套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 服務(wù)器地址和端口號(hào)
server_address = ('localhost', 12346)

# 連接服務(wù)器
client_socket.connect(server_address)

# 接收服務(wù)器的消息
data = client_socket.recv(1024).decode()
print(f"已連接到服務(wù)器, 服務(wù)器消息:{data}")

while True:
    # 向服務(wù)器發(fā)送消息
    message = input()
    if message == "end":
        break
    client_socket.sendall(message.encode())
    print(client_socket.recv(1024).decode())

# 關(guān)閉客戶(hù)端套接字
client_socket.close()

啟動(dòng)服務(wù)端,未啟動(dòng)客戶(hù)端

啟動(dòng)服務(wù)端,代碼執(zhí)行到client_socket, client_address = server_socket.accept()暫停,accept 是一個(gè)阻塞函數(shù)。

函數(shù)說(shuō)明:

client_socket, client_address = server_socket.accept()

阻塞接口,等待客戶(hù)端的連接,沒(méi)有客戶(hù)端連接時(shí)阻塞等待,有客戶(hù)端連接時(shí)返回新的聊天socket,用于后續(xù)發(fā)送和接收消息

繼續(xù)啟動(dòng)客戶(hù)端

啟動(dòng)客戶(hù)端之后,客戶(hù)端連接服務(wù)端,服務(wù)端代碼執(zhí)行到data = client_socket.recv(1024).decode(), recv 是一個(gè)阻塞函數(shù)。

函數(shù)說(shuō)明:

data = client_socket.recv(1024).decode()

阻塞接口,等待緩沖區(qū)有消息到來(lái)。沒(méi)有消息時(shí)阻塞等待,有消息到來(lái)返回消息內(nèi)容

客戶(hù)端發(fā)送消息

客戶(hù)端發(fā)送消息,data = client_socket.recv(1024).decode()收到消息,從網(wǎng)絡(luò)協(xié)議棧中獲取消息,并返回。繼續(xù)whie True 循環(huán)的下一輪循環(huán),阻塞在相同地方。

IO多路復(fù)用模型下的socket 網(wǎng)絡(luò)編程 selector

selector是實(shí)現(xiàn)IO多路復(fù)用模型的模塊,首先回憶一下IO多路復(fù)用。

IO多路復(fù)用是通過(guò)select、poll、epoll監(jiān)聽(tīng)文件句柄,當(dāng)有文件句柄處于就緒狀態(tài),就通知對(duì)應(yīng)的應(yīng)用程序處理。

服務(wù)端:server.py

import selectors
import socket

# 選擇一個(gè)當(dāng)前平臺(tái)最優(yōu)的IO多路復(fù)用模型
sel = selectors.DefaultSelector()


def accept(server_socket):
    conn, addr = server_socket.accept()
    print(f"與客戶(hù)端 {addr} 建立連接")

    conn.setblocking(False)  # 設(shè)定非阻塞
    # 注冊(cè)conn對(duì)象到selector中,當(dāng)conn可讀時(shí),返回conn和回調(diào)函數(shù)read
    sel.register(conn, selectors.EVENT_READ, read)


def read(conn):
    data = conn.recv(1024).decode('utf-8')
    print(f"客戶(hù)端消息:{data}")
    if data == "close":
        sel.unregister(conn)
        conn.close()
    else:
        conn.sendall(f"服務(wù)器收到消息:{data}".encode())


if __name__ == "__main__":
    sock = socket.socket()
    sock.bind(("0.0.0.0", 9999))
    sock.listen()
    sock.setblocking(False)  # 設(shè)置sock非阻塞
    # 將sock注冊(cè)到selector中,當(dāng)sock可讀時(shí),返回sock和回調(diào)函數(shù)accept
    sel.register(sock, selectors.EVENT_READ, accept)

    print("創(chuàng)建事件循環(huán)")
    while True:
        events = sel.select()  # 阻塞運(yùn)行,有就緒的事件返回就緒事件列表
        for key, _ in events:
            print(key)
            # key.data: 注冊(cè)的回調(diào)函數(shù)  key.fileobj: 注冊(cè)的文件句柄
            callback = key.data  # 注冊(cè)的回調(diào)函數(shù)
            callback(key.fileobj)

主要的函數(shù):

一、自動(dòng)選擇文件IO模型selectors.DefaultSelector()

選擇一個(gè)當(dāng)前平臺(tái)最優(yōu)的IO模型,一般來(lái)說(shuō)是epoll或kqueue。存在的可選項(xiàng)包括:

  • SelectSelector
  • PollSelector
  • EpollSelector
  • DevpollSelector
  • KqueueSelector

DefaultSelector 是一個(gè)指向當(dāng)前平臺(tái)上可用的最高效實(shí)現(xiàn)的別名,當(dāng)選擇epoll時(shí),可以認(rèn)為 sel = EpollSelector。返回:一個(gè)select對(duì)象

二、文件注冊(cè) sel.register(sock, selectors.EVENT_READ, accept)

函數(shù)原型:

register(fileobj, events, data=None)

注冊(cè)一個(gè)用于選擇的文件對(duì)象,在其上監(jiān)視 I/O 事件。

fileobj 是要監(jiān)視的文件對(duì)象。它可以是整數(shù)形式的文件描述符或者具有 fileno() 方法的對(duì)象。

events 是要監(jiān)視的事件的位掩碼。

data 是一個(gè)任意對(duì)象或變量。

返回:

這將返回一個(gè)新的 SelectorKey 實(shí)例,實(shí)例具體內(nèi)容見(jiàn)下一個(gè)函數(shù)的key

三、獲取就緒文件events = sel.select()

函數(shù)原型:

select(timeout=None)

可用于遍歷獲取狀態(tài)變?yōu)榫途w注冊(cè)的文件,如果設(shè)置超時(shí)時(shí)間則可能會(huì)拋出超時(shí)異常。返回:一個(gè)(key, events)的元組,

  • key: 一個(gè)SelectorKey類(lèi)的實(shí)例,包括
  • fileobj: 已注冊(cè)的文件對(duì)象。
  • fd: 下層的文件描述符

events: 必須在此文件對(duì)象上被等待的事件。

events:文件句柄可讀還是可寫(xiě)的標(biāo)識(shí)。為EVENT_READ或EVENT_WRITE,或者二者的組合

client.py

import socket

client = socket.socket(family=socket. AF_INET, type=socket.SOCK_STREAM)
host = socket.gethostname()
client.connect((host, 9999))

while True:
    data = input("客戶(hù)端發(fā)送數(shù)據(jù):").strip()
    client.send(data.encode())
    if data == "end" or data == "":
        client.close()
        break
    print(client.recv(1024).decode("utf-8"))

服務(wù)端啟動(dòng),客戶(hù)端未啟動(dòng)

if __name__ == "__main__":
    sock = socket.socket()
    sock.bind(("0.0.0.0", 9999))
    sock.listen()
    sock.setblocking(False)  # 設(shè)置sock非阻塞
    # 將sock注冊(cè)到selector中,當(dāng)sock可讀時(shí),返回sock和回調(diào)函數(shù)accept
    sel.register(sock, selectors.EVENT_READ, accept)

    print("創(chuàng)建事件循環(huán)")
    while True:
        events = sel.select()  # 阻塞運(yùn)行,有就緒的事件返回就緒事件列表

服務(wù)端啟動(dòng),代碼完成的功能包括:

  • 創(chuàng)建一個(gè)socket,并綁定IP,監(jiān)聽(tīng)端口
  • 設(shè)置socket為非阻塞,否則超時(shí)會(huì)報(bào)錯(cuò)
  • 將socket注冊(cè)到 selector 中,等待socket就緒,綁定就緒之后的回調(diào)函數(shù)accept
  • 進(jìn)入while True循環(huán),訪問(wèn)select返回的就緒列表。這個(gè)阻塞函數(shù),沒(méi)有文件讀寫(xiě)就緒就會(huì)阻塞。

繼續(xù)啟動(dòng)客戶(hù)端

啟動(dòng)一個(gè)客戶(hù)端,客戶(hù)端連接到服務(wù)端,socket文件句柄有連接請(qǐng)求,select返回可讀狀態(tài)的socket。返回的events是一個(gè)列表,當(dāng)中只有一個(gè)就緒的文件句柄。

key.data拿到注冊(cè)的回調(diào)函數(shù)也就是accept函數(shù),key.fileobj拿到文件句柄的socket對(duì)象。調(diào)用accept函數(shù),傳入socket對(duì)象。

def accept(server_socket):
    conn, addr = server_socket.accept()
    print(f"與客戶(hù)端 {addr} 建立連接")

    conn.setblocking(False)  # 設(shè)定非阻塞
    # 注冊(cè)conn對(duì)象到selector中,當(dāng)conn可讀時(shí),返回conn和回調(diào)函數(shù)read
    sel.register(conn, selectors.EVENT_READ, read)

accept中先通過(guò)accept接收連接,返回通信使用的文件句柄conn,然后設(shè)置conn為非阻塞,最后將conn阻塞到selector中,傳入回調(diào)函數(shù)read。等conn文件句柄可讀時(shí),就表示有數(shù)據(jù)發(fā)送過(guò)來(lái),就可以調(diào)用read函數(shù)讀取內(nèi)容了。

客戶(hù)端發(fā)送消息

客戶(hù)端發(fā)送消息時(shí),selector會(huì)返回可讀狀態(tài)的conn文件句柄,從返回對(duì)象中獲取回調(diào)函數(shù),調(diào)用回調(diào)函數(shù)read,傳入文件句柄。

def read(conn):
    data = conn.recv(1024).decode('utf-8')
    print(f"客戶(hù)端消息:{data}")
    if data == "close":
        sel.unregister(conn)
        conn.close()
    else:
        conn.sendall(f"服務(wù)器收到消息:{data}".encode())

在read函數(shù)中,首先獲取了網(wǎng)絡(luò)協(xié)議棧中的消息內(nèi)容,然后判斷消息是否為關(guān)閉連接。如果不是則發(fā)送一條消息給對(duì)方。

整個(gè)基于IO多路復(fù)用模型的網(wǎng)絡(luò)編程流程就是這樣。

selector 原理分析

selector是操作系統(tǒng)的IO多路復(fù)用模型的一種實(shí)現(xiàn)。通過(guò)selectpollepoll監(jiān)聽(tīng)文件句柄,在文件句柄可讀的狀態(tài)下,會(huì)返回就緒的文件句柄。

返回就緒狀態(tài)文件句柄

sel = selectors.DefaultSelector()
while True:
    events = sel.select()

循環(huán)中訪問(wèn)sel.select()就是監(jiān)聽(tīng)文件句柄狀態(tài)的函數(shù),一個(gè)阻塞函數(shù)。應(yīng)用程序調(diào)用該函數(shù)后會(huì)等待,直到有數(shù)據(jù)到來(lái),數(shù)據(jù)從設(shè)備發(fā)送到內(nèi)核空間,在socket編程中就是數(shù)據(jù)流從網(wǎng)卡到內(nèi)核空間中。當(dāng)數(shù)據(jù)到達(dá)內(nèi)核空間中,該函數(shù)返回文件句柄相關(guān)的內(nèi)容。

數(shù)據(jù)拷貝

當(dāng)文件句柄就緒之后,就可以從文件句柄里讀取數(shù)據(jù)了。

在selector中相關(guān)的函數(shù)是

  • conn, addr = server_socket.accept()
  • data = conn.recv(1024).decode('utf-8')

總結(jié)

一個(gè)完整的IO多路復(fù)用模型就是由兩個(gè)部分組成,分別是

  • 返回就緒狀態(tài)文件句柄
  • 數(shù)據(jù)拷貝

asyncio  和  selector 的關(guān)系

selectors 則是 asyncio 的底層實(shí)現(xiàn)之一。asyncio實(shí)現(xiàn)的協(xié)程是由事件循環(huán)任務(wù)組成的,而selector就是事件循環(huán)的重要依賴(lài)模塊。

asyncio 使用了 selectors 模塊來(lái)實(shí)現(xiàn)底層的并發(fā) I/O 操作。通過(guò)將 selectors 的功能封裝為 asyncio 提供的事件循環(huán)(Event Loop)和其他協(xié)程相關(guān)的工具。

回顧一下事件循環(huán)的機(jī)制

任務(wù)列表 = [ 任務(wù)1, 任務(wù)2, 任務(wù)3,... ]

while True:
    可執(zhí)行的任務(wù)列表,已完成的任務(wù)列表 = 去任務(wù)列表中檢查所有的任務(wù),將'可執(zhí)行'和'已完成'的任務(wù)返回
    
    for 就緒任務(wù) in 已準(zhǔn)備就緒的任務(wù)列表:
        執(zhí)行已就緒的任務(wù)
        
    for 已完成的任務(wù) in 已完成的任務(wù)列表:
        在任務(wù)列表中移除 已完成的任務(wù)

    如果 任務(wù)列表 中的任務(wù)都已完成,則終止循環(huán)

事件循環(huán)就是一個(gè)while True的循環(huán),循環(huán)中做的事情有三個(gè):

  • 獲取就緒狀態(tài)的任務(wù)和已完成的任務(wù)
  • 執(zhí)行就緒狀態(tài)的任務(wù)
  • 移除已完成的任務(wù)

那么selector的功能在事件循環(huán)中的功能就非常明顯了,就是負(fù)責(zé)返回IO相關(guān)的就緒任務(wù)。

asyncio 庫(kù)使用了底層的 selectors 模塊來(lái)監(jiān)聽(tīng)和管理文件描述符的狀態(tài)變化,并在合適的時(shí)候?qū)⒖刂茩?quán)交給其他的協(xié)程。這樣可以實(shí)現(xiàn)非阻塞的 I/O 操作,并支持高并發(fā)和并行執(zhí)行。

selectors 提供了底層的 I/O 多路復(fù)用機(jī)制,而 asyncio 在其之上提供了更高級(jí)的異步編程框架。

附錄asyncio模塊事件循環(huán)核心模塊

def run_forever(self):
    """Run until stop() is called."""
    self._check_closed()
    self._check_running()
    self._set_coroutine_origin_tracking(self._debug)

    old_agen_hooks = sys.get_asyncgen_hooks()
    try:
        self._thread_id = threading.get_ident()
        sys.set_asyncgen_hooks(firstiter=self._asyncgen_firstiter_hook,
                               finalizer=self._asyncgen_finalizer_hook)

        events._set_running_loop(self)
        while True:
            self._run_once()
            if self._stopping:
                break
    finally:
        self._stopping = False
        self._thread_id = None
        events._set_running_loop(None)
        self._set_coroutine_origin_tracking(False)
        sys.set_asyncgen_hooks(*old_agen_hooks)

到此這篇關(guān)于Python中IO多路復(fù)用模塊selector的用法詳解的文章就介紹到這了,更多相關(guān)Python多路復(fù)用模塊selector內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • python和C++共享內(nèi)存?zhèn)鬏攬D像的示例

    python和C++共享內(nèi)存?zhèn)鬏攬D像的示例

    這篇文章主要介紹了python和C++共享內(nèi)存?zhèn)鬏攬D像的示例,幫助大家利用python處理圖片,感興趣的朋友可以了解下
    2020-10-10
  • 使用pytorch和torchtext進(jìn)行文本分類(lèi)的實(shí)例

    使用pytorch和torchtext進(jìn)行文本分類(lèi)的實(shí)例

    今天小編就為大家分享一篇使用pytorch和torchtext進(jìn)行文本分類(lèi)的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-01-01
  • 基于Python構(gòu)建智能圖像增強(qiáng)系統(tǒng)

    基于Python構(gòu)建智能圖像增強(qiáng)系統(tǒng)

    在數(shù)字影像處理領(lǐng)域,圖像增強(qiáng)技術(shù)正經(jīng)歷從傳統(tǒng)算法到深度學(xué)習(xí)模型的革命性轉(zhuǎn)變,下面小編就來(lái)和大家簡(jiǎn)單講講如何使用Python構(gòu)建智能圖像增強(qiáng)系統(tǒng)吧
    2025-07-07
  • python中pip的安裝與使用教程

    python中pip的安裝與使用教程

    在安裝pip前,請(qǐng)確認(rèn)win系統(tǒng)中已經(jīng)安裝好了python,和easy_install工具,下面腳本之家小編給大家詳細(xì)介紹python中pip的安裝與使用教程,感興趣的朋友一起看看吧
    2018-08-08
  • Python paramiko模塊使用解析(實(shí)現(xiàn)ssh)

    Python paramiko模塊使用解析(實(shí)現(xiàn)ssh)

    這篇文章主要介紹了Python paramiko模塊使用解析(實(shí)現(xiàn)ssh),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-08-08
  • 淺談Keras中shuffle和validation_split的順序

    淺談Keras中shuffle和validation_split的順序

    這篇文章主要介紹了淺談Keras中shuffle和validation_split的順序,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-06-06
  • Python實(shí)現(xiàn)將目錄中TXT合并成一個(gè)大TXT文件的方法

    Python實(shí)現(xiàn)將目錄中TXT合并成一個(gè)大TXT文件的方法

    這篇文章主要介紹了Python實(shí)現(xiàn)將目錄中TXT合并成一個(gè)大TXT文件的方法,涉及Python針對(duì)目錄下文本文件的遍歷、讀取及寫(xiě)入等技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-07-07
  • Python max函數(shù)中key的用法及原理解析

    Python max函數(shù)中key的用法及原理解析

    最近有童鞋向小編求助怎么樣找到字符串中出現(xiàn)字?jǐn)?shù)最多的字符呢,其實(shí)最簡(jiǎn)單的處理方法是使用max函數(shù),max()函數(shù)用于獲得給定的可迭代對(duì)象中的最大值,關(guān)于Python max函數(shù)key用法跟隨小編一起通過(guò)本文學(xué)習(xí)下吧
    2021-06-06
  • Python中對(duì)象的比較操作==和is區(qū)別詳析

    Python中對(duì)象的比較操作==和is區(qū)別詳析

    這篇文章主要給大家介紹了關(guān)于Python中對(duì)象的比較操作==和is區(qū)別的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • python二維碼操作:對(duì)QRCode和MyQR入門(mén)詳解

    python二維碼操作:對(duì)QRCode和MyQR入門(mén)詳解

    今天小編就為大家分享一篇python二維碼操作:對(duì)QRCode和MyQR入門(mén)詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-06-06

最新評(píng)論

上思县| 华宁县| 开远市| 新泰市| 成安县| 崇明县| 日土县| 绍兴县| 镇坪县| 东安县| 长子县| 彭州市| 屏东县| 天长市| 灵川县| 宽甸| 蓬莱市| 克东县| 新建县| 马关县| 肃南| 滨州市| 德阳市| 黔西| 茌平县| 崇州市| 巴青县| 博客| 承德市| 尉氏县| 罗定市| 镇巴县| 荃湾区| 凤山市| 山丹县| 平陆县| 汉沽区| 本溪| 彰化县| 留坝县| 屯门区|