Python 网络编程 自定义实现异步IO

it2024-03-30  55

一.客户端 1.发送HTTP请求的本质:

import select,socket sk=socket.socket() #1.连接: sk.connect(("www.baidu.com",80))#IO阻塞 #80端口是通常默认向HTTP开放的端口,一般使用该端口;如果使用其他端口,需要确保服务器打开了相应的端口 print("连接成功") #2.发送请求: sk.send(b"GET / HTTP/1.0\r\nHOST:www.baidu.com\r\n\r\n")#自定义封装HTTP数据包 #sk.send(b"POST / HTTP/1.0\r\nHOST:www.baidu.com\r\n\r\nk1=v1&k2=v2") #3.等待响应: data=sk.recv(8096)#IO阻塞 print(data) #4.断开连接: sk.close()

2.实现异步IO:

本质上就是IO多路复用(监听多个Socket对象)+非阻塞Socket import select,socket class HttpRequest: def __init__(self,sk,host,callback): self.socket=sk self.host=host self.callback=callback def fileno(self): return self.socket.fileno() class HttpResponse: def __init__(self,recv_data): self.recv_data=recv_data self.headers_dict={} self.body=None self.initialize() def initialize(self): headers,body=self.recv_data.split(b"\r\n\r\n",1) #分割请求头和请求体,因为请求体中可能还有"\r\n\r\n",故限制只使用第1个进行分割 self.body=body headers_list=headers.split(b"\r\n") for h in headers_list: h_str=str(h,encoding="utf-8") v=h_str.split(":",1)#分割请求头中键值对的键和值 if len(v)==2: self.headers_dict[v[0]]=v[1] elif len(v)==1: self.headers_dict["method"]=h_str class AsyncRequest: def __init__(self): self.conn=[]#所有HttpRequest对象 self.connection=[]#未连接成功的HttpRequest对象 def add_request(self,host,callback): try: sk=socket.socket() sk.setblocking(0)#0也可以换成False #无数据(连接无响应或数据未返回)就立刻报错(BlockingIOError),不等待服务器响应 sk.connect((host,80)) except BlockingIOError as e: print(e) finally: request=HttpRequest(sk,host,callback) self.conn.append(request) self.connection.append(request) def run(self): while True: rlist,wlist,elist=select.select(self.conn,self.connection,self.conn,0.05) for w in wlist:#在wlist中表示可写,此时可以发送信息;w是自定义的HttpRequest对象 print(w.host,"连接成功") tpl="GET / HTTP/1.0\r\nHOST:%s\r\n\r\n"%w.host w.socket.send(bytes(tpl,encoding='utf-8')) self.connection.remove(w)#每次循环向1个服务器发送请求 for r in rlist:#在rlist中表示可读,说明收到信息;r也是自定义的HttpRequest对象 #准确地说,在rlist中表示状态发生变化,参见 Python.网络编程.IO模型.四.3 部分 print(r.host,"收到响应") recv_data=bytes() while True: try: chunk=r.socket.recv(8096) recv_data+=chunk except Exception as e: break print(r.host,"返回了:",recv_data) response=HttpResponse(recv_data) r.callback(response)#对返回的数据进行相应的处理 r.socket.close() self.conn.remove(r)#1次循环处理1个响应 if len(self.conn)==0:#所有请求都已收到响应并已断开 break def f1(response): print(response.headers_dict) def f2(response): print(response.headers_dict) def f3(response): print(response.headers_dict) url_list=[ {"host":"www.baidu.com","callback":f1},#220.181.38.150;使用IP也可以 {"host":"cn.bing.com","callback":f2},#204.79.197.200 {"host":"www.cnblogs.com","callback":f3}#118.31.180.41 ] req=AsyncRequest() for item in url_list: print(item["host"]) req.add_request(item["host"],item["callback"]) req.run() #结果: www.baidu.com [WinError 10035] 无法立即完成一个非阻止性套接字操作。 cn.bing.com [WinError 10035] 无法立即完成一个非阻止性套接字操作。 www.cnblogs.com [WinError 10035] 无法立即完成一个非阻止性套接字操作。 www.baidu.com 连接成功 cn.bing.com 连接成功 www.baidu.com 收到响应 www.baidu.com 返回了: b'HTTP/1.0 200 OK\r\nAccept-Ranges:...'(省略) www.cnblogs.com 连接成功 www.cnblogs.com 收到响应 www.cnblogs.com 返回了: b'HTTP/1.1 301 Moved Permanently\r\nDate:...'(省略) cn.bing.com 收到响应 cn.bing.com 返回了: b'HTTP/1.1 200 OK\r\nCache-Control:...'(省略)
最新回复(0)