总结:
与实现twisted或tornado的原理类似,通过理解这个代码,能实现其他异步框架的理解
参考:
IO模型:https://www.cnblogs.com/nuochengze/p/13372747.html
socket在爬虫中的表层应用:https://www.cnblogs.com/nuochengze/p/13345532.html
代码
import socket import select class Request(object): def __init__(self, sock, info): """ sock::socket """ self.sock = sock self.info = info def fileno(self): # Request对象原本没有fileno方法,但是可以调用传递进来的sock对象的fileno方法 return self.sock.fileno() class AsyncioNoBlocking(object): def __init__(self): self.socket_list = list() self.connect_list = list() def add_request(self, req_info): """ 创建请求 req_info::dict ::{"host": 'www.baidu.com', 'port': 80, path: '/'}, """ sock = socket.socket() sock.setblocking(False) try: sock.connect((req_info['host'], req_info['port'])) except BlockingIOError as e: pass sock = Request(sock, req_info) # 将sock对象进行封装了 self.socket_list.append(sock) self.connect_list.append(sock) def run(self): """ 开始事件循环,检测是否链接成功及是否数据返回 """ while True: # select.select()中的对象,可以是除socket对象,其他的任何对象,但是这个其他对象需要有fileno方法, 即`对象.fileno()` r_list, w_list, e_list = select.select(self.socket_list, self.connect_list, [], 0.05) # 此时select.select([obj])中的obj是已经封装好的Request(自定义)对象 # w_list,当其内有值时,表示链接成功 for w in w_list: # 检查w是哪一个Request对象 # 此时Request对象中包含url的信息 data = "GET %s http/1.1 host:%s " % (w.info['path'], w.info['host']) w.sock.send(data.encode('utf8')) # 发送数据 self.connect_list.remove(w) # 避免发送两次请求,需要将已发送的请求移除 # r_list,当其内有值时,表示有返回的数据 for r in r_list: response_str = str() while True: response = r.sock.recv(1024) if len(response) == 0: break response_str += response # print(r.info['host'], response_str) # 打印内容,后续可通过return返回数据 # 也可通过回调函数,实现对数据的自定义处理 for func in r.info['callback']: """ 对返回的数据,如何处理,都将通过自己定义的回调函数,来进行处理 """ func(response) self.socket_list.remove(r) # 移除socket对象,避免重复接受数据 if not r_list: """ 结束循环,需要进行判断 w_list表示成功链接的请求,可能链接成功,但是没有返回数据,所以不能作为判断结束条件 r_list表示链接返回的数据,当为空的时候,表示该Request对象不再有成功链接,表示请求结束了,可退出循环 """ break def done1(response): # 对response进行操作 pass def done2(response): # 对response进行操作 pass url_list = [ {"host": 'www.baidu.com', 'port': 80, "path": '/', "callback": [done1]}, {"host": 'www.bing.com', 'port': 80, "path": '/', "callback": [done2]}, ] if __name__ == "__main__": obj = AsyncioNoBlocking() for item in url_list: # 遍历列表,发送请求 obj.add_request(item) # 开始事件循环 obj.run()