web框架的本质——通俗的说,web框架封装了socket、数据库操作、路由分发、模板配置等,留给我们现成的接口,让我们更专注业务逻辑本身。
一个简单的web示例
import socket # 1. 创建socket对象 sk = socket.socket() # 2. 绑定IP和端口 sk.bind(('127.0.0.1', 8888)) # 3. 监听 sk.listen(5) # 4. 循环监听 while 1: conn, addr = sk.accept() # 等待连接 received_data = conn.recv(8192) # 接收数据 print(received_data) # b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8888\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\nCookie: csrftoken=FQW7H4SEVPRqdqIr4vSeiP3EdjX2C0JY7VwCdmXevoXcPjiAMBtNptkiZLHqXo4d\r\n\r\n' conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 接收数据 conn.send(b'<h1>Successful</h1>') conn.close() # 关闭连接
现在,使用浏览器地址栏访问http://127.0.0.1:8888/
,就会得到Successful
。
根据不同路径返回不同内容
第一版·普通版
import socket # 创建socket对象 sk = socket.socket() # 绑定IP和端口 sk.bind(('127.0.0.1', 8888)) # 监听 sk.listen() while True: # 等待连接 conn, addr = sk.accept() # 接收数据 data = conn.recv(8192) data = data.decode('utf-8') url = data.split()[1] print(url) # 返回状态行 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') if url == '/oumei': conn.send(b'<h1>oumei</h1>') elif url == '/rihan': conn.send(b'<h1>rihan</h1>') else: conn.send(b'<h1>404</h1>') # 关闭连接 conn.close()
第二版·函数版
import socket # 创建socket对象 sk = socket.socket() # 绑定IP和端口 sk.bind(('127.0.0.1', 8888)) # 监听 sk.listen() def oumei(url): ret = 'oumei - {}'.format(url) return ret.encode('utf-8') def rihan(url): ret = 'rihan//// - {}'.format(url) return ret.encode('utf-8') while True: # 等待连接 conn, addr = sk.accept() # 接收数据 data = conn.recv(8192) data = data.decode('utf-8') url = data.split()[1] print(url) # 返回状态行 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') if url == '/oumei': ret = oumei(url) elif url == '/rihan': ret = rihan(url) else: ret = b'404 not found' conn.send(ret) # 关闭连接 conn.close()
第三版·函数进阶版
import socket # 创建socket对象 sk = socket.socket() # 绑定IP和端口 sk.bind(('127.0.0.1', 8888)) # 监听 sk.listen() def oumei(url): ret = 'oumei - {}'.format(url) return ret.encode('utf-8') def rihan(url): ret = 'rihan//// - {}'.format(url) return ret.encode('utf-8') def guochan(url): ret = 'guochan - {}'.format(url) return ret.encode('utf-8') list = [ ('/oumei', oumei), ('/rihan', rihan), ('/guochan', guochan), ] while True: # 等待连接 conn, addr = sk.accept() # 接收数据 data = conn.recv(8192) data = data.decode('utf-8') url = data.split()[1] print(url) # 返回状态行 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') func = None for i in list: if i[0] == url: func = i[1] break if func: ret = func(url) else: ret = b'404 not found' conn.send(ret) # 关闭连接 conn.close()
第四版·反射版
ps:仅针对当前脚本使用的反射。
import socket sk = socket.socket() sk.bind(("127.0.0.1", 8888)) sk.listen() class Server(object): """ 模拟多个页面 """ def europe(self): return "<h1>europe</h1>".encode('utf8') def kkc(self): return "<h1>kkc</h1>".encode('utf8') def notfond(self): return "<h1>404 not fond</h1>".encode('utf8') while 1: # 等待请求 conn, address = sk.accept() # 获取数据 data = conn.recv(8192).decode('utf8') # 处理请求拿到请求路径 # print(data) url = data.split(' ', 2)[1][1:] # print(url, data) # 返回数据 server_obj = Server() if hasattr(server_obj, url): res = getattr(server_obj, url)() else: res = server_obj.notfond() conn.send(b'HTTP/1.1 200 OK\r\n\r\n') conn.send(res) conn.close()
返回HTML页面
首先在同级目录有个index.html
页面:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> </head> <body> <h1>index page</h1> </body> </html>
然后写server
端:
import socket # 创建socket对象 sk = socket.socket() # 绑定IP和端口 sk.bind(('127.0.0.1', 8888)) # 监听 sk.listen() def oumei(url): ret = 'oumei - {}'.format(url) return ret.encode('utf-8') def rihan(url): ret = 'rihan//// - {}'.format(url) return ret.encode('utf-8') def guochan(url): ret = 'guochan - {}'.format(url) return ret.encode('utf-8') def index(url): with open('index.html', 'rb') as f: ret = f.read() return ret list = [ ('/oumei', oumei), ('/rihan', rihan), ('/guochan', guochan), ('/index', index), ] while True: # 等待连接 conn, addr = sk.accept() # 接收数据 data = conn.recv(8192) data = data.decode('utf-8') url = data.split()[1] print(url) # 返回状态行 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') func = None for i in list: if i[0] == url: func = i[1] break if func: ret = func(url) else: ret = b'404 not found' conn.send(ret) # 关闭连接 conn.close()
返回动态页面
在同级目录有个tiem.html
页面:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>当前时间是: @@time@@ </h1> </body> </html>
在来实现代码:
import socket import time # 创建socket对象 sk = socket.socket() # 绑定IP和端口 sk.bind(('0.0.0.0', 8000)) # 监听 sk.listen() def oumei(url): ret = 'oumei - {}'.format(url) return ret.encode('utf-8') def rihan(url): ret = 'rihan//// - {}'.format(url) return ret.encode('utf-8') def guochan(url): ret = 'guochan - {}'.format(url) return ret.encode('utf-8') def index(url): with open('index.html', 'rb') as f: ret = f.read() return ret def timer(url): now = time.strftime('%Y-%m-%d %H:%M:%S') with open('time.html', 'r', encoding='utf-8') as f: data = f.read() data = data.replace('@@time@@', now) return data.encode('utf-8') list = [ ('/oumei', oumei), ('/rihan', rihan), ('/guochan', guochan), ('/index', index), ('/time', timer), ] while True: # 等待连接 conn, addr = sk.accept() # 接收数据 data = conn.recv(8096) data = data.decode('utf-8') url = data.split()[1] print(url) # 返回状态行 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') func = None for i in list: if i[0] == url: func = i[1] break if func: ret = func(url) else: ret = b'404 not found' conn.send(ret) # 关闭连接 conn.close()
总结
正如文章开始所示,web框架本质就是一个socket服务端。
web框架功能:
- socket收发消息
- 根据不同的路径返回不同的内容
- 可以返回动态页面(使用字符串替换等方式进行模板渲染)