1.自定义异步IO模块
HTTP请求本质,阻塞

1 sk = socket.socket() 2 # 1.连接 3 sk.connect(('www.baidu.com',80,)) # IO阻塞 4 print('连接成功了...') 5 6 # 2. 连接成功发送消息 7 sk.send(b'GET / HTTP/1.0\r\nHost:www.baidu.com\r\n\r\n') 8 # sk.send(b'POST / HTTP/1.0\r\nHost:www.baidu.com\r\n\r\nk1=v1&k2=v2') 9 10 # 3. 等待着服务端响应 11 data = sk.recv(8096) # IO阻塞 12 print(data) 13 14 # 关闭连接 15 sk.close()
1、自定义异步IO模块(客户端)
IO多路复用:r,w,e = while 监听多个socket对象
r:表示有人给我发送数据
w:表示已经与别人创建连接成功
e:错误相关
异步IO模块(客户端):非阻塞的socket(sk.setblocking(0)),与IO多路复用结合,实现:
作为客户端向服务端发起“并发”请求,select监听socket是否已经有变化

1 import socket 2 import select 3 4 5 class HttpRequest: 6 def __init__(self,sk,host,callback): 7 self.socket = sk 8 self.host = host 9 self.callback = callback 10 def fileno(self): 11 return self.socket.fileno() 12 13 class HttpResponse: 14 def __init__(self,recv_data): 15 self.recv_data = recv_data 16 self.header_dict = {} 17 self.body = None 18 19 self.initialize() 20 def initialize(self): 21 headers, body = self.recv_data.split(b'\r\n\r\n', 1) 22 self.body = body 23 header_list = headers.split(b'\r\n') 24 for h in header_list: 25 h_str = str(h,encoding='utf-8') 26 v = h_str.split(':',1) 27 # print ("v:",v) 28 if len(v) == 2: 29 self.header_dict[v[0]] = v[1] 30 31 32 class AsyncRequest: 33 def __init__(self): 34 self.conn = [] 35 self.connection = [] # 用于检测是否已经连接成功 36 37 def add_request(self,host,callback): 38 try: 39 sk = socket.socket() 40 sk.setblocking(0) 41 sk.connect((host,80,)) 42 except BlockingIOError as e: 43 pass 44 request = HttpRequest(sk,host,callback) 45 self.conn.append(request) 46 self.connection.append(request) 47 48 def run(self): 49 50 while True: 51 rlist,wlist,elist = select.select(self.conn,self.connection,self.conn,0.05) 52 for w in wlist: 53 print(w.host,'连接成功...') 54 # 只要能循环到,表示socket和服务器端已经连接成功 55 tpl = "GET / HTTP/1.0\r\nHost:%s\r\n\r\n" %(w.host,) 56 w.socket.send(bytes(tpl,encoding='utf-8')) 57 self.connection.remove(w) 58 for r in rlist: 59 # r,是HttpRequest 60 recv_data = bytes()#字节类型 61 while True: 62 try: 63 chunck = r.socket.recv(8096) 64 recv_data += chunck 65 except Exception as e: 66 break 67 response = HttpResponse(recv_data) 68 r.callback(response) 69 r.socket.close() 70 self.conn.remove(r) 71 if len(self.conn) == 0: 72 break 73 74 def f1(response): 75 print('保存到文件',response.header_dict) 76 77 def f2(response): 78 print('保存到数据库', response.header_dict) 79 80 url_list = [ 81 {'host':'www.baidu.com','callback': f1}, 82 {'host':'cn.bing.com','callback': f2}, 83 {'host':'www.cnblogs.com','callback': f2}, 84 ] 85 86 req = AsyncRequest() 87 for item in url_list: 88 req.add_request(item['host'],item['callback']) 89 90 req.run()
2.1、自定义同步非阻塞web服务端

1 import socket 2 import select 3 import time 4 from login_test import settings 5 import os 6 7 class HttpRequest(object): 8 """ 9 用户封装用户请求信息 10 """ 11 def __init__(self, content): 12 """ 13 14 :param content:用户发送的请求数据:请求头和请求体 15 """ 16 print("content:",content) 17 self.content = content 18 19 self.header_bytes = bytes() 20 self.body_bytes = bytes() 21 22 self.header_dict = {} 23 24 self.method = "" 25 self.url = "" 26 self.protocol = "" 27 28 self.initialize() 29 self.initialize_headers() 30 31 def initialize(self): 32 33 temp = self.content.split(b'\r\n\r\n', 1) 34 print("temp:",temp) 35 if len(temp) == 1:# 只有请求头 36 self.header_bytes += temp 37 else: 38 h, b = temp # h:请求头,b:请求体 39 self.header_bytes += h 40 self.body_bytes += b 41 42 @property 43 def header_str(self):# 将字节转化成字符串,方便分割 44 45 return str(self.header_bytes, encoding='utf-8') 46 47 def initialize_headers(self): 48 49 headers = self.header_str.split('\r\n') 50 first_line = headers[0].split(' ') # first_line: ['GET', '/', 'HTTP/1.1'] or ['GET', '/favicon.ico', 'HTTP/1.1'] 51 # print("first_line:",first_line) 52 if len(first_line) == 3: 53 self.method, self.url, self.protocol = headers[0].split(' ') 54 for line in headers: 55 kv = line.split(':') 56 if len(kv) == 2: 57 k, v = kv 58 self.header_dict[k] = v 59 60 # 响应头 响应头 ,回复时用 61 index_content = ''' 62 HTTP/1.1 200 OK 63 Content-Type: text/html 64 \r\n\r\n 65 ''' 66 67 68 69 test_login_dir = os.path.join(settings.TEMPLATES[0]["DIRS"][0],"test_login.html") 70 # print(test_login_dir) # G:\Python\login_test\templates\test_login.html 71 file = open(test_login_dir, 'r',encoding='utf-8') 72 index_content += file.read() 73 file.close() 74 75 76 def run(): 77 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 78 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 79 sock.bind(("127.0.0.1", 9999,)) 80 sock.setblocking(False) 81 sock.listen(128) 82 83 inputs = [] 84 inputs.append(sock) 85 86 async_request_dict = { 87 # 'socket': futrue 88 } 89 90 while True: 91 rlist,wlist,elist = select.select(inputs,[],[],0.05) 92 for r in rlist: 93 if r == sock: 94 print("123") 95 """新请求到来""" 96 conn,addr = sock.accept() 97 conn.setblocking(False) 98 inputs.append(conn) 99 else: 100 """客户端发来数据""" 101 print("456") 102 103 data = b"" 104 while True: 105 try: 106 chunk = r.recv(1024) 107 data = data + chunk 108 except Exception as e: 109 chunk = None 110 if not chunk: 111 break 112 113 result = HttpRequest(data) 114 115 r.sendall(bytes(index_content ,encoding='utf-8')) 116 # r.sendall(b'HTTP/1.1 200 OK\r\n\r\n<html><body>hello</body></html>') 117 118 inputs.remove(r) 119 r.close() 120 121 122 123 if __name__ == '__main__': 124 run()
2.2、自定义异步非阻塞web框架(服务端)
注:当在非Future对象中使用time.sleep等,无法实现并发操作,线程会被hang住

1 import socket 2 import select 3 import time 4 5 """ 6 异步非阻塞框架(服务端): 7 设置 future = Future(),通过Future实现异步非阻塞 8 1. 当设置 future = Future()时,挂起当前请求,线程可以处理其他请求 9 2. 当给future设置值时,当前挂起的请求返回 10 """ 11 12 class HttpRequest(object): 13 """ 14 用户封装用户请求信息 15 """ 16 def __init__(self, content): 17 """ 18 19 :param content:用户发送的请求数据:请求头和请求体 20 """ 21 self.content = content 22 23 self.header_bytes = bytes() 24 self.body_bytes = bytes() 25 26 self.header_dict = {} 27 28 self.method = "" 29 self.url = "" 30 self.protocol = "" 31 32 self.initialize() 33 self.initialize_headers() 34 35 def initialize(self): 36 37 temp = self.content.split(b'\r\n\r\n', 1) 38 if len(temp) == 1: 39 self.header_bytes += temp[0] 40 else: 41 h, b = temp 42 self.header_bytes += h 43 self.body_bytes += b 44 45 @property 46 def header_str(self): 47 return str(self.header_bytes, encoding='utf-8') 48 49 def initialize_headers(self): 50 headers = self.header_str.split('\r\n') 51 first_line = headers[0].split(' ') 52 if len(first_line) == 3: 53 self.method, self.url, self.protocol = headers[0].split(' ') 54 for line in headers: 55 kv = line.split(':') 56 if len(kv) == 2: 57 k, v = kv 58 self.header_dict[k] = v 59 60 class Future(object): 61 def __init__(self): 62 self.result = None 63 F = Future() 64 def main(request): 65 global F 66 return F # Future对象 67 68 def stop(request): 69 global F 70 F.result = b"xxxxxxxxxxxxx" 71 return "stop" 72 73 74 def index(request): 75 76 return "indexasdfasdfasdf" 77 78 79 routers = [ 80 ('/main/',main), 81 ('/index/',index), 82 ('/stop/',stop), 83 ] 84 85 def run(): 86 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 87 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 88 sock.bind(("127.0.0.1", 9999,)) 89 sock.setblocking(False) 90 sock.listen(128) 91 92 inputs = [] 93 inputs.append(sock) 94 95 async_request_dict = { 96 # 'socket': futrue 97 } 98 99 while True: 100 rlist,wlist,elist = select.select(inputs,[],[],0.05) 101 for r in rlist: 102 if r == sock: 103 """新请求到来""" 104 conn,addr = sock.accept() 105 conn.setblocking(False) 106 inputs.append(conn) 107 else: 108 """客户端发来数据""" 109 data = b"" 110 while True: 111 try: 112 chunk = r.recv(1024) 113 data = data + chunk 114 except Exception as e: 115 chunk = None 116 if not chunk: 117 break 118 # data进行处理:请求头和请求体 119 request = HttpRequest(data) 120 # 1. 请求头中获取url 121 # 2. 去路由中匹配,获取指定的函数 122 # 3. 执行函数,获取返回值 123 # 4. 将返回值 r.sendall(b'alskdjalksdjf;asfd') 124 import re 125 flag = False 126 func = None 127 for route in routers: 128 if re.match(route[0],request.url): 129 print(route[0],request.url) 130 flag = True 131 func = route[1] 132 break 133 if flag: # url路径匹配成功 134 result = func(request) # 执行函数 135 if isinstance(result,Future):# 判断是否Future对象,如是则挂起不执行,否则执行服务端响应操作 136 async_request_dict[r] = result 137 else: 138 r.sendall(b'HTTP/1.1 200 OK\r\n\r\n<html><body>hello</body></html>') 139 inputs.remove(r) 140 r.close() 141 else:# 匹配不成功,返回404错误 142 r.sendall(b"HTTP/1.1 200 OK\r\n\r\n<html><body>404</body></html>") 143 inputs.remove(r) 144 r.close() 145 146 147 for conn in list(async_request_dict.keys()):# 当Future函数中result有值,才执行。实现可控制的异步非阻塞操作 148 future = async_request_dict[conn] 149 if future.result: 150 conn.sendall(b'HTTP/1.1 200 OK\r\n\r\n<html><body>hello</body></html>') 151 conn.close() 152 del async_request_dict[conn] 153 inputs.remove(conn) 154 155 if __name__ == '__main__': 156 run()
主要:
2.3、自定义异步非阻塞web框架(服务端)
通过Future设置,实现并发操作

1 import socket 2 import select 3 import time 4 5 class HttpRequest(object): 6 """ 7 用户封装用户请求信息 8 """ 9 def __init__(self, content): 10 """ 11 12 :param content:用户发送的请求数据:请求头和请求体 13 """ 14 self.content = content 15 16 self.header_bytes = bytes() 17 self.body_bytes = bytes() 18 19 self.header_dict = {} 20 21 self.method = "" 22 self.url = "" 23 self.protocol = "" 24 25 self.initialize() 26 self.initialize_headers() 27 28 def initialize(self): 29 30 temp = self.content.split(b'\r\n\r\n', 1) 31 if len(temp) == 1: 32 self.header_bytes += temp[0] 33 else: 34 h, b = temp 35 self.header_bytes += h 36 self.body_bytes += b 37 38 @property 39 def header_str(self): 40 return str(self.header_bytes, encoding='utf-8') 41 42 def initialize_headers(self): 43 headers = self.header_str.split('\r\n') 44 first_line = headers[0].split(' ') 45 if len(first_line) == 3: 46 self.method, self.url, self.protocol = headers[0].split(' ') 47 for line in headers: 48 kv = line.split(':') 49 if len(kv) == 2: 50 k, v = kv 51 self.header_dict[k] = v 52 53 class Future(object): 54 def __init__(self,timeout=0): 55 self.result = None 56 self.timeout = timeout 57 self.start = time.time() 58 def main(request): 59 #挂起当前请求,5秒后返回当前挂起请求 60 f = Future(5) 61 return f 62 63 def index(request): 64 return "indexasdfasdfasdf" 65 66 67 routers = [ 68 ('/main/',main), 69 ('/index/',index), 70 ] 71 72 def run(): 73 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 74 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 75 sock.bind(("127.0.0.1", 9999,)) 76 sock.setblocking(False) 77 sock.listen(128) 78 79 inputs = [] 80 inputs.append(sock) 81 82 async_request_dict = { 83 # 'socket': futrue 84 } 85 86 while True: 87 rlist,wlist,elist = select.select(inputs,[],[],0.05) 88 for r in rlist: 89 if r == sock: 90 """新请求到来""" 91 conn,addr = sock.accept() 92 conn.setblocking(False) 93 inputs.append(conn) 94 else: 95 """客户端发来数据""" 96 data = b"" 97 while True: 98 try: 99 chunk = r.recv(1024) 100 data = data + chunk 101 except Exception as e: 102 chunk = None 103 if not chunk: 104 break 105 # data进行处理:请求头和请求体 106 request = HttpRequest(data) 107 # 1. 请求头中获取url 108 # 2. 去路由中匹配,获取指定的函数 109 # 3. 执行函数,获取返回值 110 # 4. 将返回值 r.sendall(b'alskdjalksdjf;asfd') 111 import re 112 flag = False 113 func = None 114 for route in routers: 115 if re.match(route[0],request.url): 116 flag = True 117 func = route[1] 118 break 119 if flag: 120 result = func(request) 121 if isinstance(result,Future): 122 async_request_dict[r] = result 123 else: 124 r.sendall(b'HTTP/1.1 200 OK\r\n\r\n<html><body>hello,baby!</body></html>') 125 inputs.remove(r) 126 r.close() 127 else: 128 r.sendall(b"HTTP/1.1 200 OK\r\n\r\n<html><body>404</body></html>") 129 inputs.remove(r) 130 r.close() 131 132 for conn in list(async_request_dict.keys()): 133 future = async_request_dict[conn] 134 start = future.start 135 timeout = future.timeout 136 ctime = time.time() 137 if (start + timeout) <= ctime : 138 future.result = b"timeout" 139 if future.result: 140 conn.sendall(b'HTTP/1.1 200 OK\r\n\r\n<html><body>hello web</body></html>') 141 conn.close() 142 del async_request_dict[conn] 143 inputs.remove(conn) 144 145 if __name__ == '__main__': 146 run()
转载:
自定义异步非阻塞Web框架(服务器)

1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 import re 4 import socket 5 import select 6 import time 7 8 9 class HttpResponse(object): 10 """ 11 封装响应信息 12 """ 13 def __init__(self, content=''): 14 self.content = content 15 16 self.headers = {} 17 self.cookies = {} 18 19 def response(self): 20 return bytes(self.content, encoding='utf-8') 21 22 23 class HttpNotFound(HttpResponse): 24 """ 25 404时的错误提示 26 """ 27 def __init__(self): 28 super(HttpNotFound, self).__init__('404 Not Found') 29 30 31 class HttpRequest(object): 32 """ 33 用户封装用户请求信息 34 """ 35 def __init__(self, conn): 36 self.conn = conn 37 38 self.header_bytes = bytes() 39 self.header_dict = {} 40 self.body_bytes = bytes() 41 42 self.method = "" 43 self.url = "" 44 self.protocol = "" 45 46 self.initialize() 47 self.initialize_headers() 48 49 def initialize(self): 50 51 header_flag = False 52 while True: 53 try: 54 received = self.conn.recv(8096) 55 except Exception as e: 56 received = None 57 if not received: 58 break 59 if header_flag: 60 self.body_bytes += received 61 continue 62 temp = received.split(b'\r\n\r\n', 1) 63 if len(temp) == 1: 64 self.header_bytes += temp 65 else: 66 h, b = temp 67 self.header_bytes += h 68 self.body_bytes += b 69 header_flag = True 70 71 @property 72 def header_str(self): 73 return str(self.header_bytes, encoding='utf-8') 74 75 def initialize_headers(self): 76 headers = self.header_str.split('\r\n') 77 first_line = headers[0].split(' ') 78 if len(first_line) == 3: 79 self.method, self.url, self.protocol = headers[0].split(' ') 80 for line in headers: 81 kv = line.split(':') 82 if len(kv) == 2: 83 k, v = kv 84 self.header_dict[k] = v 85 86 87 class Future(object): 88 """ 89 异步非阻塞模式时封装回调函数以及是否准备就绪 90 """ 91 def __init__(self, callback): 92 self.callback = callback 93 self._ready = False 94 self.value = None 95 96 def set_result(self, value=None): 97 self.value = value 98 self._ready = True 99 100 @property 101 def ready(self): 102 return self._ready 103 104 105 class TimeoutFuture(Future): 106 """ 107 异步非阻塞超时 108 """ 109 def __init__(self, timeout): 110 super(TimeoutFuture, self).__init__(callback=None) 111 self.timeout = timeout 112 self.start_time = time.time() 113 114 @property 115 def ready(self): 116 current_time = time.time() 117 if current_time > self.start_time + self.timeout: 118 self._ready = True 119 return self._ready 120 121 122 class Snow(object): 123 """ 124 微型Web框架类 125 """ 126 def __init__(self, routes): 127 self.routes = routes 128 self.inputs = set() 129 self.request = None 130 self.async_request_handler = {} 131 132 def run(self, host='localhost', port=9999): 133 """ 134 事件循环 135 :param host: 136 :param port: 137 :return: 138 """ 139 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 140 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 141 sock.bind((host, port,)) 142 sock.setblocking(False) 143 sock.listen(128) 144 sock.setblocking(0) 145 self.inputs.add(sock) 146 try: 147 while True: 148 readable_list, writeable_list, error_list = select.select(self.inputs, [], self.inputs,0.005) 149 for conn in readable_list: 150 if sock == conn: 151 client, address = conn.accept() 152 client.setblocking(False) 153 self.inputs.add(client) 154 else: 155 gen = self.process(conn) 156 if isinstance(gen, HttpResponse): 157 conn.sendall(gen.response()) 158 self.inputs.remove(conn) 159 conn.close() 160 else: 161 yielded = next(gen) 162 self.async_request_handler[conn] = yielded 163 self.polling_callback() 164 165 except Exception as e: 166 pass 167 finally: 168 sock.close() 169 170 def polling_callback(self): 171 """ 172 遍历触发异步非阻塞的回调函数 173 :return: 174 """ 175 for conn in list(self.async_request_handler.keys()): 176 yielded = self.async_request_handler[conn] 177 if not yielded.ready: 178 continue 179 if yielded.callback: 180 ret = yielded.callback(self.request, yielded) 181 conn.sendall(ret.response()) 182 self.inputs.remove(conn) 183 del self.async_request_handler[conn] 184 conn.close() 185 186 def process(self, conn): 187 """ 188 处理路由系统以及执行函数 189 :param conn: 190 :return: 191 """ 192 self.request = HttpRequest(conn) 193 func = None 194 for route in self.routes: 195 if re.match(route[0], self.request.url): 196 func = route[1] 197 break 198 if not func: 199 return HttpNotFound() 200 else: 201 return func(self.request)
测试demo:

1 import socket 2 import select 3 import time 4 5 class HttpRequest(object): 6 """ 7 用户封装用户请求信息 8 """ 9 def __init__(self, content): 10 """ 11 12 :param content:用户发送的请求数据:请求头和请求体 13 """ 14 self.content = content 15 16 self.header_bytes = bytes() 17 self.body_bytes = bytes() 18 19 self.header_dict = {} 20 21 self.method = "" 22 self.url = "" 23 self.protocol = "" 24 25 self.initialize() 26 self.initialize_headers() 27 28 def initialize(self): 29 30 temp = self.content.split(b'\r\n\r\n', 1) 31 if len(temp) == 1: 32 self.header_bytes += temp 33 else: 34 h, b = temp 35 self.header_bytes += h 36 self.body_bytes += b 37 38 @property 39 def header_str(self): 40 return str(self.header_bytes, encoding='utf-8') 41 42 def initialize_headers(self): 43 headers = self.header_str.split('\r\n') 44 first_line = headers[0].split(' ') 45 if len(first_line) == 3: 46 self.method, self.url, self.protocol = headers[0].split(' ') 47 for line in headers: 48 kv = line.split(':') 49 if len(kv) == 2: 50 k, v = kv 51 self.header_dict[k] = v 52 53 class Future(object): 54 def __init__(self,timeout=0): 55 self.result = None 56 self.timeout = timeout 57 self.start = time.time() 58 def main(request): 59 f = Future(5) 60 return f 61 62 def index(request): 63 return "indexasdfasdfasdf" 64 65 66 routers = [ 67 ('/main/',main), 68 ('/index/',index), 69 ] 70 71 def run(): 72 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 73 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 74 sock.bind(("127.0.0.1", 9999,)) 75 sock.setblocking(False) 76 sock.listen(128) 77 78 inputs = [] 79 inputs.append(sock) 80 81 async_request_dict = { 82 # 'socket': futrue 83 } 84 85 while True: 86 rlist,wlist,elist = select.select(inputs,[],[],0.05) 87 for r in rlist: 88 if r == sock: 89 """新请求到来""" 90 conn,addr = sock.accept() 91 conn.setblocking(False) 92 inputs.append(conn) 93 else: 94 """客户端发来数据""" 95 data = b"" 96 while True: 97 try: 98 chunk = r.recv(1024) 99 data = data + chunk 100 except Exception as e: 101 chunk = None 102 if not chunk: 103 break 104 # data进行处理:请求头和请求体 105 request = HttpRequest(data) 106 # 1. 请求头中获取url 107 # 2. 去路由中匹配,获取指定的函数 108 # 3. 执行函数,获取返回值 109 # 4. 将返回值 r.sendall(b'alskdjalksdjf;asfd') 110 import re 111 flag = False 112 func = None 113 for route in routers: 114 if re.match(route[0],request.url): 115 flag = True 116 func = route[1] 117 break 118 if flag: 119 result = func(request) 120 if isinstance(result,Future): 121 async_request_dict[r] = result 122 else: 123 r.sendall(bytes(result,encoding='utf-8')) 124 inputs.remove(r) 125 r.close() 126 else: 127 r.sendall(b"404") 128 inputs.remove(r) 129 r.close() 130 131 for conn in async_request_dict.keys(): 132 future = async_request_dict[conn] 133 start = future.start 134 timeout = future.timeout 135 ctime = time.time() 136 if (start + timeout) <= ctime : 137 future.result = b"timeout" 138 if future.result: 139 conn.sendall(future.result) 140 conn.close() 141 del async_request_dict[conn] 142 inputs.remove(conn) 143 144 if __name__ == '__main__': 145 run()
未来的你,会感谢现在努力的你!