Python基础Web服务器案例
一、WSGI
1、PythonWeb服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI) 是Python应用程序或框架和Web服务器之间的一种接口,已经被广泛接受, 它已基本达成它的可移植性方面的目标。
2、WSGI 没有官方的实现, 因为WSGI更像一个协议。 只要遵照这些协议, WSGI应用(Application)都可以在任何服务器(Server)上运行, 反之亦然。
3、WSGI允许开发者将选择web框架和web服务器分开,web服务器必须具备WSGI接口。
4、示例:浏览器请求动态页面过程
二、Web动态服务器代码示例
1、目录结构
2、服务器 MyWebServer.py
1 # coding:utf-8 2 3 import socket 4 import re 5 import sys 6 7 from multiprocessing import Process 8 9 # 设置框架文件根目录 10 FRAMEWORK_DIR = "./Frameworks" 11 12 13 class HTTPServer(object): 14 """web服务器""" 15 def __init__(self, application): 16 """构造函数, application指的是框架的app""" 17 self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 18 # 服务端close时释放端口 19 self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 20 # web框架 21 self.app = application 22 # 响应头 23 self.response_headers = "" 24 25 def start(self): 26 """服务器启动""" 27 self.server_socket.listen(128) 28 while True: 29 client_socket, client_address = self.server_socket.accept() 30 # print("[%s : %s]用户连接上了" % (client_address[0],client_address[1])) 31 print("[%s, %s]用户连接上了\r\n" % client_address) 32 # 开启多进程,处理客户端请求 33 handle_client_process = Process(target=self.handle_client, args=(client_socket,)) 34 handle_client_process.start() 35 client_socket.close() 36 37 def start_response(self, status, headers): 38 """传递给web框架的方法(回调),用来获得框架返回的状态、headers""" 39 response_headers = "HTTP/1.1 " + status + "\r\n" 40 for header in headers: 41 response_headers += "%s: %s\r\n" % header 42 # 在服务器中记录框架返回的响应头 43 self.response_headers = response_headers 44 45 def handle_client(self, client_socket): 46 """处理客户端请求""" 47 48 # 获取客户端请求数据 49 request_data = client_socket.recv(1024) 50 print("客户端的请求数据:\r\n", request_data.decode("utf-8")) 51 request_lines = request_data.splitlines() 52 print("将请求数据按照每一行切片:\r\n") 53 for index in range(len(request_lines)-1): 54 print("第%d行: %s"%(index+1,request_lines[index].decode("utf-8"))) 55 56 # 解析请求报文:提取用户请求的文件名、方法名 57 request_start_line = request_lines[0] 58 print(request_start_line.decode("utf-8")) 59 file_name = re.match(r"\w+ +(/[^ ]*) ", request_start_line.decode("utf-8")).group(1) 60 method = re.match(r"(\w+) +/[^ ]* ", request_start_line.decode("utf-8")).group(1) 61 env = { 62 "PATH_INFO": file_name, 63 "METHOD": method 64 } 65 66 # 传入请求参数和响应回调方法,获得响应体内容:此处把对象当做函数直接来用,会掉对象的__call__方法 67 response_body = self.app(env, self.start_response) 68 69 # 拼接出完整的回复内容:响应头 + 响应体 70 response = self.response_headers + "\r\n" + response_body 71 72 # 向客户端返回响应数据:python3中必须是 bytes()返回的bytes对象 73 client_socket.send(bytes(response, "utf-8")) 74 75 # 关闭客户端连接 76 client_socket.close() 77 78 def bind(self, port): 79 self.server_socket.bind(("", port)) 80 81 82 def main(): 83 84 # 增加检索包的路径 85 sys.path.insert(1, FRAMEWORK_DIR) 86 87 # 获取程序启动参数 88 if len(sys.argv) < 2: 89 sys.exit("ERROR, you should input :python3 MyWebServer.py [Module]:app") 90 module_name, app = sys.argv[1].split(":") 91 92 # 动态载入模块 93 m = __import__(module_name) 94 95 # 获取模块中的属性:此处是web框架的实例对象 96 app = getattr(m, app) 97 98 # 创建并开启服务器 99 http_server = HTTPServer(app) 100 http_server.bind(8888) 101 http_server.start() 102 103 104 if __name__ == "__main__": 105 main()
3、web框架 MyWebFramework.py
1 # coding:utf-8 2 3 import time 4 5 # from MyWebServer import HTTPServer 6 7 # 设置静态文件根目录 8 HTML_ROOT_DIR = "./html" 9 10 11 class Application(object): 12 """框架的核心部分,也就是框架的主题程序,框架是通用的""" 13 def __init__(self, urls): 14 # 设置路由信息 15 self.urls = urls 16 17 def __call__(self, env, start_response): 18 """将类名当做函数来直接调用,重写此方法。此处方法的作用是返回给服务器响应体""" 19 20 # 获取文件名(路径),默认为"/" 21 path = env.get("PATH_INFO", "/") 22 23 # 判断是静态还是动态文件 24 if path.startswith("/static"): 25 # 要访问静态文件: /static/index.html 26 file_name = path[7:] 27 # 打开文件,读取内容 28 try: 29 file = open(HTML_ROOT_DIR + file_name, "rb") 30 except IOError: 31 # 代表未找到路由信息,404错误 32 status = "404 Not Found" 33 headers = [] 34 # 调用传入的回调方法,将状态和头部信息返回 35 start_response(status, headers) 36 # 直接返回响应体 37 return "Page not found" 38 else: 39 # 读取文件内容 40 file_data = file.read() 41 file.close() 42 43 # 设置响应内容 44 status = "200 OK" 45 headers = [] 46 start_response(status, headers) 47 return file_data.decode("utf-8") 48 else: 49 # 访问动态文件,查询路由表中是否包含要执行的文件(是否有路由信息) 50 for url, handler in self.urls: 51 if path == url: 52 # handler 即为路由表中映射的方法(非字符串) 53 return handler(env, start_response) 54 55 # 代表未找到路由信息,404错误 56 status = "404 Not Found" 57 headers = [] 58 start_response(status, headers) 59 return "Program not found" 60 61 62 def show_time(env, start_response): 63 status = "200 OK" 64 headers = [ 65 ("Content-Type", "text/plain") 66 ] 67 # 调用传入的回调方法,将状态和头部信息返回 68 start_response(status, headers) 69 return time.ctime() 70 71 72 def print_hello(env, start_response): 73 status = "200 OK" 74 headers = [ 75 ("Content-Type", "text/plain") 76 ] 77 start_response(status, headers) 78 return "hello everyone" 79 80 81 def print_json(env, start_response): 82 status = "200 OK" 83 headers = [ 84 ("Content-Type", "text/plain") 85 ] 86 start_response(status, headers) 87 jsonStr = "{\"code\":200,\"data\": {\"list\":[{\"firstName\":\"Qi\",\"lastName\":\"Zhang\"}]},\"msg\":\"OK\"}" 88 return jsonStr 89 90 91 def show_default(env, start_response): 92 try: 93 file = open(HTML_ROOT_DIR + "/index.html", "rb") 94 except IOError: 95 status = "404 Not Found" 96 headers = [] 97 start_response(status, headers) 98 return "DefaultPage not found" 99 else: 100 file_data = file.read() 101 file.close() 102 status = "200 OK" 103 headers = [] 104 start_response(status, headers) 105 return file_data.decode("utf-8") 106 107 108 # 路由信息,根据映射表,动态调用web框架中的方法 109 urls = [("/", show_default), 110 ("/time", show_time), 111 ("/hello", print_hello), 112 ("/json", print_json)] 113 114 # 通过路由表实例化对象(当前模块载入时就会创建),直接提供给服务器 115 app = Application(urls)
4、执行
(1)从业务和使用逻辑上,应该是“启动”服务器,所以运行服务器程序
(2)配置程序参数
(3)浏览器验证: 127.0.0.1:8888 、 json 127.0.0.1:8888/json 、127.0.0.1:8888/static/index.html