• 〖Python〗-- socket + select 实现构建异步非阻塞模块


    【socket + select 实现构建异步非阻塞模块】

    对于异步IO请求的本质则是【非阻塞Socket】+【IO多路复用】,那我们是否可以自定义异步IO模块,作为客户端,用于发送请求呢?答案那是肯定的!

    补充说明:select可操作任何对象,但对象内必须是有fileno方法,可以看作是一个数值,给系统提供(文件句柄),其内部执行的是socket对象fileno方法的返回值!

    自定义一个插件,模块如下:

    import socket
    import select
    
    class Request(object):
        """
        select监听的是有变化的对象,当多个socket通信存在的话,当发生变化时,无法确定哪个是哪个。此时就需要唯一指定哪个通信处理的是什么数据!
        我们想到的办法是,再另创建一个类,用于接收当前的socket和信息,实例化唯一的对象!
        巧妙之处就在于唯一化的处理,生成的每个对象都是唯一的!!!
        """
        def __init__(self,sock,info):
        #初始化
            self.sock = sock   通信
            self.info = info   信息
    
        def fileno(self):  #转换成fileno对象
            return self.sock.fileno()
    
    
    class NoBlockIO(object):
        def __init__(self):
            self.sock_list = []
            self.conns = []
    
        def add_request(self,req_info):
            """
            创建请求
             假设:req_info = {'host': 'www.baidu.com', 'port': 80, 'path': '/'},
            :return:
            """
            sock = socket.socket()  #创建socket对象
            sock.setblocking(False) #设置为非阻塞
            
            #建立连接,因为是非阻塞状态,连接可能会出异常所以需要做异常处理。
            #此处一定要注意,不管报不报错,连接的请求肯定会发出!
            try:
                sock.connect((req_info['host'],req_info['port']))
            except BlockingIOError as e:
                pass
            #实例化唯一的通信对象
            obj = Request(sock,req_info) #将生成的sock对象和信息列表传递到Request类,然后获取对应的文件句柄,信息保留到
            self.sock_list.append(obj) #将得到的对象添加到sock_list 通信列表中
            self.conns.append(obj)    #将得到的对象添加到conns 连接列表中
    
        def run(self):
            """
            开始事件循环,检测:连接成功?数据是否返回?
            注意此处操作的就全是Request类生成的对象!!!
            :return:
            """
            while True:
                # select.select([request对象,])
                r,w,e = select.select(self.sock_list,self.conns,[],0.05)
                
                # w,是否连接成功
                for obj in w:
                    # 检查obj:request对象是谁,此时就需要唯一判断了!利用类可以封装对象的特性解决!
                    # socket, {'host': 'www.baidu.com', 'port': 80, 'path': '/'},
                    data = "GET %s http/1.1
    host:%s
    
    " %(obj.info['path'],obj.info['host']) #发送GET请求的信息
                    obj.sock.send(data.encode('utf-8')) #连接成功,发送字节数据
                    self.conns.remove(obj) #然后在连接列表中删除这个对象,防止发送两次,排除循环通信的可能
                
                # 数据返回,接收到数据
                for obj in r:
                    response = obj.sock.recv(8096)  #成功接收到数据,接收返回的信息
                    #单个回调函数
                    obj.info['callback'](response) #指定对应对象执行回调函数,参数为返回的信息
                    
                    #多个回调函数的情况!
                    #for func in obj.info['callback'] #指定对应对象执行回调函数,参数为返回的信息
                    #    func(response)
                    
                    self.sock_list.remove(obj)  #在监听列表中移除这个对象
    
                # 所有请求已经返回
                if not self.sock_list: #所有请求都处理完,就退出循环
                    break
    NBIO

    调用这个插件,使用插件里的方法:

    from .NBIO import NoBlockIO
    
    #自定义定义回调函数,对返回的信息进行处理(执行完毕之后,对数据进行什么操作)
    def done1(response):
        print(response)
    
    def done2(response):
        print(response)
    
    #准备执行的信息,域名,端口号,回调函数
    url_list = [
        {'host': 'www.baidu.com', 'port': 80, 'path': '/','callback': done1},
        {'host': 'www.cnblogs.com', 'port': 80, 'path': '/index.html','callback': done2},
        {'host': 'www.bing.com', 'port': 80, 'path': '/','callback': done2},
    ]
    
    #实例化一个对象
    noblockObj = NoBlockIO()
    
    #循环要处理的信息
    for item in url_list:
        noblockObj.add_request(item)  #为条信息创建请求
    
    noblockObj.run() #执行
    #谁先执行完就返回谁,相对独立,互不干扰!
    执行脚本

    异步是相对而言,需要时间循环去检测!
    以上是简单的模块定制,更多的操作,例如:分割响应头,响应体;or 保存文件这些操作后续需要,再进行补充!

    慎重说明:必须知道和理解整个执行的流程!!!

  • 相关阅读:
    position之属性
    Grid网格布局
    position 属性指定了元素的定位类型
    注册表单
    简单页面
    自我介绍
    正则表达式
    Dom和Bom
    颜色和单位
    伪类和伪元素的区别
  • 原文地址:https://www.cnblogs.com/SHENGXIN/p/7684481.html
Copyright © 2020-2023  润新知