python selectors模块使用

本文介绍Python中selectors模块的使用方法,包括SelectorKey对象的属性及Selector类的方法,并通过一个HTTP客户端实例展示了如何利用I/O多路复用技术实现单线程并发。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

selectors — High-level I/O multiplexing

SelectorKeys对象

用于关联file object和其底层fd的namedtuple,如:

SelectorKey(fileobj=<socket.socket fd=5, family=AddressFamily.AF_INET, type=2049, proto=0, laddr=('192.168.18.129', 35990), raddr=('23.23.154.131', 80)>, fd=5, events=2, data=<bound method Select.connected of <__main__.Select object at 0x7f4477694d30>>)

对象属性

  • 1 fileobj:被注册的文件对象
  • 2 fd : 文件对象的基础文件描述符,整型。等同fileobj.fileno()
  • 3 events: 需要等待的fileobj的事件(读或写事件)
  • 4 data: 和fileobj关联的opaque object,一半是函数名。主要用于回调

该对象可由BaseSelector的几个方法返回:

  • 1 register(fileobj, events, data=None)
  • 2 unregister(fileobj)
  • 3 select()
  • 4 get_key(fileobj)

Select 对象

method说明

  • 1 register(fileobj, events, data=None):
    注册fileobj,监控fileobje的I/O事件;返回与fileobje关联的SelectorKey对象
  • 2 unregister(fileobj):
    注销一个filobj,取消对它的监控。注销fileobj之前应先close();返回返回与fileobje关联的SelectorKey对象
  • 3 select():

    等待注册的fileobje的读或写事件准备好,returns a list of (key, events) tuples, one for each ready file object
  • 4 fileno(): returns the file descriptor of selector,selector对象本身也是file object
  • 5 get_map():返回file object对象和其关联的SelectorKey 的映射,key:file object的fd; value: 关联的SelectorKey

使用selector的I/O multiplexing 实现单线程并发

什么是I/O multiplexing ?

首先,网络上称它为“多路复用I/O”,但我认为I/O多路转接更好。
selctor本身是一个file object,它存储了多个已经注册为读或写事件的文件句柄,一旦某个文件句柄准备就绪,就可通知用户程序执行I/O操作。这种利用selector一个文件对象转换多个文件句柄的I/O操作的方式就是某种意义上的“复用”

协程的使用

#!/usr/bin/python3

import socket
from urllib.parse import urlparse
# DefaultSelector默认选择器,使用当前平台上可用的最高效的实现(select/poll/epoll)
from selectors import DefaultSelector, EVENT_READ, EVENT_WRITE


class Client(object):
    '''负责连接、 发送和接受'''
    def __init__(self, url):
        url = urlparse(url)
        self.host, self.path = url.netloc, [url.path, '/'][url.path == '']
        self.sock = socket.socket()
        self.sock.setblocking(False)
        self.data = b''
        try:
            self.sock.connect((self.host, 80))
        except BlockingIOError as ret:  # noqa
            pass

    def send_request(self):
        self.sock.send(
            "GET {} HTTP/1.1\r\nHost: {}\r\nConnection:close\r\n\r\n".format(
                self.path, self.host).encode('utf-8'))

    def recv_response(self):
        rev = self.sock.recv(1024)
        if rev:
            self.data += rev
            return True
        return False

    def end(self):
        html_body = self.data.split(b'\r\n\r\n')[1]
        self.sock.close()
        print(html_body)
        print('\n')
        return html_body


'''select + callback + eventLoop:单线程并发
只处理准备好socket事件,无需等待I/O;省去了线程切换的开销
回调模式:底层调用高层'''


class Select(object):
    '''创建selector,负责注册和取消注册socket读写事件、'''

    def __init__(self):
        self.selector = DefaultSelector()
        self.clients = {}

    def startListening(self, client):
        # add client,monitor socked wtite event
        self.clients[client.sock] = client
        # 注册socket写事件
        self.selector.register(client.sock, EVENT_WRITE, self.connected)

    def connected(self, key):
        # connect host,and monitor socket read event
        self.selector.unregister(key.fileobj)
        sock = key.fileobj
        self.clients[sock].send_request()
        self.selector.register(sock, EVENT_READ, self.do_read)

    def do_read(self, key):
        # read data ,if data is empty, then unregister socket and end
        sock = key.fileobj
        client = self.clients[sock]
        if client.recv_response():
            return
        self.selector.unregister(sock)
        client.end()


def event_loop():
    while True:
        events = sel.selector.select()
        for key, mask in events:
            print(key, mask)
            callback = key.data
            callback(key)


if __name__ == '__main__':
    urls = ['http://www.baidu.com', 'http://httpbin.org/']
    sel = Select()
    for url in urls:
        sel.startListening(Client(url))
    event_loop()
    sel.selector.close()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值