面试题答案
一键面试-
利用
fileno
属性实现高效多路复用的方法- 以下是使用
selectors
模块结合fileno
属性的示例代码:
import selectors import socket # 创建默认的选择器对象 sel = selectors.DefaultSelector() def accept(sock, mask): conn, addr = sock.accept() # 接受新连接 print('accepted', conn, 'from', addr) conn.setblocking(False) # 注册新连接的socket对象,监听读事件 sel.register(conn, selectors.EVENT_READ, read) def read(conn, mask): data = conn.recv(1024) # 读取数据 if data: print('echoing', repr(data), 'to', conn) conn.send(data) # 回显数据 else: print('closing', conn) sel.unregister(conn) conn.close() # 创建服务器socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('localhost', 12345)) sock.listen(100) sock.setblocking(False) # 注册服务器socket,监听读事件,回调函数为accept sel.register(sock, selectors.EVENT_READ, accept) try: while True: events = sel.select() # 阻塞等待事件发生 for key, mask in events: callback = key.data callback(key.fileobj, mask) except KeyboardInterrupt: print('caught keyboard interrupt, exiting') finally: sel.close()
- 在上述代码中,
sel.register(sock, selectors.EVENT_READ, accept)
这一步用到了fileno
属性。selectors
模块的register
方法实际上是通过调用socket对象的fileno
方法获取文件描述符,然后将其加入到多路复用的监控集合中。例如对于服务器sock
,sel.register(sock, selectors.EVENT_READ, accept)
,selectors
模块内部会调用sock.fileno()
获取其文件描述符,以便在select
调用时能对该socket进行监控。
- 以下是使用
-
原理
- 多路复用概念:多路复用技术允许应用程序在单线程内同时监控多个I/O事件,避免了为每个socket创建一个单独线程或进程所带来的资源开销。
fileno
属性的作用:在操作系统层面,I/O操作是基于文件描述符的。在Python中,socket对象通过fileno
属性提供了与操作系统交互的接口,返回一个整数类型的文件描述符。selectors
模块(其底层依赖操作系统的select
、poll
或epoll
等系统调用,具体取决于操作系统)通过这些文件描述符来监控socket的I/O状态。当调用sel.select()
时,操作系统会检查这些文件描述符对应的socket是否有可读、可写或错误等事件发生。如果有事件发生,select
调用会返回对应的文件描述符集合(在Python中通过events
变量获取),应用程序就可以对这些发生事件的socket进行相应处理。这样,通过fileno
属性将Python的socket对象与操作系统的I/O多路复用机制联系起来,实现了高效的多路复用。