题外话
在驱动select运行的过程中发现了一个有意思的代码,所以先仿造着写了一个demo,主要是下面的那个循环,从对象A中拿到一个属性,当然这个属性得到的输出是一个函数B,然后把对象A传给B,得到结果。总觉得这种操作很NB(sao)
from collections import namedtuple
SelectorKey = namedtuple('SelectorKey', ['fd', 'data'])
sk1 = SelectorKey('123', lambda x: print(x.fd))
sk2 = SelectorKey('456', lambda x: print(x.fd+'456'))
sk_list = [[sk1, Ellipsis], [sk2, Ellipsis]]
if __name__ == '__main__':
for sl, _ in sk_list:
callback = sl.data
callback(sl)
select、poll、epoll的使用
import socket
from selectors import DefaultSelector, EVENT_READ, EVENT_WRITE
selector = DefaultSelector()
class Crawl:
def connect(self, key):
"""
当文件描述符可写的时候, key会被select传进来,它是一个SelectorKey对象
:param key: SelectorKey = namedtuple('SelectorKey', ['fileobj', 'fd', 'events', 'data'])
:return:
"""
# 此时不需要监听这个套接字的write方法了
selector.unregister(key.fd)
self.client.send('GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n'.format('/', self.host).encode('utf8'))
selector.register(self.client.fileno(), EVENT_READ, self.read)
def read(self, key):
d = self.client.recv(1024)
"""
# 注意这里不能这么写,因为recv会被阻塞住,导致出错
while d:
self.data += d
d = self.client.recv(1024)
else:
selector.unregister(key.fd)
print(self.data)
"""
if d:
self.data += d
else:
selector.unregister(key.fd)
# print(self.data.decode('utf8'))
def get_url(self):
self.client = socket.socket()
self.client.setblocking(False)
self.host = 'www.baidu.com'
self.data = b''
try:
self.client.connect((self.host, 80))
except BlockingIOError as e:
pass
# 因为下一步是self.client.send,所以是注册可写的状态
# self.connect是文件描述符可写的时候会调用的方法
print(self.client.fileno(), 'self.client.fileno()')
selector.register(self.client.fileno(), EVENT_WRITE, self.connect)
"""
# 这样是无法运行的,因为还需要一个机制去驱动selector的运行
fetcher = Fetcher()
fetcher.get_url()
"""
def loop_forever():
# 事件循环
while True:
# 得到可以操作的socket
# ready.append((key, events & key.events))-->这个是源码中用于返回的值
# 从结构可以看出,第一个值为描述符,第二个值表示这个描述符是可读还是可写
ready = selector.select()
for key, mask in ready:
callback = key.data # key是SelectorKey,表示一个namedtuple,里面存储了函数以及函数需要的参数
callback(key)
if __name__ == '__main__':
crawl = Crawl()
crawl.get_url()
loop_forever()
套接字可读可写的理解
更准确的理解应该参考《UNIX 网络编程卷1》中的《第6章 I/O复用》,相关博客
若有理解错误,望指出,万分感谢。