自定义异步IO
一:http请求本质阻塞
import socket import select client=socket.socket() client.connect(('www.baidu.com',80))#IO阻塞 print("链接成功") client.send(b'GET / HTTP/1.0\r\nHost:www.baidu.com\r\n\r\n') data=client.recv(1024)#IO阻塞 print(data)
二:http请求本质阻塞
client=socket.socket() client.setblocking(false) client.connect(('www.baidu.com',80))#IO阻塞 print("链接成功") client.send(b'GET / HTTP/1.0\r\nHost:www.baidu.com\r\n\r\n') data=client.recv(1024)#IO阻塞 print(data)
回调函数:
你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。(by常溪玲)
三:异步IO请求
def f1(data): print('保存到数据库',data) def f2(data): print('保存到文件',data) class HttpRequest(object): def __init__(self,sk,host,callback): self.socket=sk self.host=host self.callback=callback def fileno(self):#自定fileno()方法 return self.socket.fileno()#返回socket的文件描述符 class AsyncRequest(object): def __init__(self): self.conn=[] self.connection=[] def addrequest(self,host,callback): try: sk=socket.socket() sk.setblocking(False) sk.connect((host,80)) except Exception as e: pass finally: sk=HttpRequest(sk,host,callback)#封装socket,本质还是调用socket的fileno函数,添加回调函数 self.conn.append(sk) self.connection.append(sk) def run(self): while True: #rlist表示有人发送数据 #wlist表示已经和别人链接成功 #elist表示链接有错误 rlist,wlist,elist=select.select(self.conn,self.connection,self.conn,0.05) #在windows中self.conn为空时,直接报错,但在linux当中仍会执行 for w in wlist: print('%s链接成功'%w.host) information='GET / HTTP/1.0\r\nHost:%s\r\n\r\n'%w.host w.socket.send(bytes(information,encoding='utf-8')) self.connection.remove(w) for r in rlist: data = b'' while True: try: chunk=r.socket.recv(2048) data+=chunk except Exception as e: break r.callback(data) r.socket.close() self.conn.remove(r) if len(self.conn)==0: break if __name__=="__main__":
#添加回调函数 urldict=[{'host':'www.baidu.com','function':f1},{'host':'www.baidu.com','function':f2},{'host':'www.163.com','function':f1}] request=AsyncRequest() for host in urldict: request.addrequest(host['host'],host['function']) request.run()
四:异步io请求
def f1(data): print('保存到数据库',data) def f2(data): print('保存到文件',data) #处理响应数据 class HttpResponse(object): def __init__(self,data): self.data=data self.headers=dict() self.body="" self.dealdata(data) def dealdata(self,data): headers,body=self.data.split(b'\r\n\r\n',1)#分割请求头和请求体 self.body=body headers_list=headers.split(b'\r\n')#将请求头逐个分开 for head in headers_list: tmp=head.decode('utf-8').split(':',1)#获取每个头的键值对 if len(tmp)==2: self.headers.setdefault(tmp[0],tmp[1]) #封装socket,添加host属性,实现filno()方法并返回,socket本身的文件对象描述符 class HttpRequest(object): def __init__(self,sk,host,callback): self.socket=sk self.host=host self.callback=callback def fileno(self):#自定fileno()方法 return self.socket.fileno()#返回socket的文件描述符 #使用select实现io多路复用,请求相应网址,拿到返回数据 class AsyncRequest(object): def __init__(self): self.conn=[] self.connection=[] def addrequest(self,host,callback): try: sk=socket.socket() sk.setblocking(False) sk.connect((host,80)) except Exception as e: pass finally: sk=HttpRequest(sk,host,callback) self.conn.append(sk) self.connection.append(sk) def run(self): while True: #rlist表示有人发送数据 #wlist表示已经和别人链接成功 #elist表示链接有错误 rlist,wlist,elist=select.select(self.conn,self.connection,self.conn,0.05) #在windows中self.conn为空时,直接报错,但在linux当中仍会执行 for w in wlist: print('%s链接成功'%w.host) information='GET / HTTP/1.0\r\nHost:%s\r\n\r\n'%w.host w.socket.send(bytes(information,encoding='utf-8')) self.connection.remove(w) for r in rlist: data = b'' while True: try: chunk=r.socket.recv(2048) data+=chunk except Exception as e: break tk=HttpResponse(data) # r.callback(data) r.socket.close() self.conn.remove(r) if len(self.conn)==0: break if __name__=="__main__": urldict=[{'host':'www.baidu.com','function':f1},{'host':'www.163.com','function':f1}] request=AsyncRequest() for host in urldict: request.addrequest(host['host'],host['function']) request.run()