• 十六、 IO多路复用,异步非阻塞


    总结:

    与实现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()
    View Code
  • 相关阅读:
    【Android 应用开发】 Android 相关代码规范 更新中 ...
    【IOS 开发】Object
    【Android 应用开发】 Android APK 反编译 混淆 反编译后重编译
    【IOS 开发】Object
    Unity3D 学习教程 14 C# 旋转镜头
    Unity3D 学习教程 13 C# 销毁炮弹
    Unity3D 学习教程 12 C# 发射炮弹
    Unity3D 学习教程 11 c#脚本控制摄像头
    Unity3D 学习教程 10 复制物体
    Unity3D 学习教程 9 旋转 放大 移动 物体
  • 原文地址:https://www.cnblogs.com/nuochengze/p/13375787.html
Copyright © 2020-2023  润新知