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

 

posted @ 2019-04-19 15:42  执着的怪味豆  阅读(1183)  评论(0编辑  收藏  举报