文章目录
1. WSGI
- WSGI:是一种规定了web服务器和web应用程序之间协作关系的协议
主流的python web框架和web服务器都支持WSGI协议
在支持WSGI协议的情况下 web服务器和web应用框架的开发可以各司其职专注于自己的领域
WSGI允许开发者将选择web框架和web服务器分开。可以混合匹配web服务器 和web框架,选择一个适合的配对 - 正常情况下,Web服务器会接受到两种请求资源:动态和静态
静态资源:一般不需要经常变化的资源 – 文件系统
动态资源需要经常变化的数据 – 数据库 - 当接受静态资源请求时候,它可以自己处理,当收到动态资源请求时候,为了使其压力不必过大,Web服务器会将其交给web框架来处理。他们之间的通信就是用 的 WSGI协议
- 当动态数据要更改的时候,要用到模板的替换
用户需要的不是模板文件 需要价格模板文件中的模板变量替换为用户关心的数据 - web服务器必须具备WSGI接口,所有的现代Python Web框架都已具备WSGI接口, 它让你不对代码作修改就能使服务器和特点的web框架协同工作。
2. 最简单的Web版本的“Hello World!”:
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return 'Hello World!'
- environ:一个包含所有HTTP请求信息的dict对象;
- start_response:一个发送HTTP响应的函数
3. WSGI 通信中,最终要的两个函数
- 一个是从框架定义的application(environ,start_response)
- 一个是需要在服务器 中定义的 start_response(status, header_list)
application的实现
import time
# 指定模板文件资源 所在的根目录
TEMPLATE_ROOT = "./template"
def gettime(path_info):
"""当用户请求/gettime.py的时候执行该函数代码"""
return time.ctime() # 显示当前的时间
def app(environ, start_response):
# 这个函数是应用框架提供给web服务器调用
# web服务器在接收到动态资源请求的时候调用这个函数
# 参数1(environ) 是一个含有HTTP请求相关信息的字典
# 字典中的'PATH_INFO': '/index.html'就是用户请求的路径
# 参数2(start_response) 是一个web提供的函数的引用
# start_response函数功能主要是用以设置 str响应状态和响应头部
# start_response参数1 是需要设置的响应状态
# start_response参数2 是一个响应头信息的列表 列表中每个元素是一个元组
# 元组前者是响应头名称 后者是响应头对应的值
# 获取到用户请求路径
# 设置前面的路径
path_info = environ['PATH_INFO']
# 当用户访问已经实现了功能的路径 就返回200 OK 其余的返回404 Not Found
if path_info == '/gettime.py':
start_response('200 OK', [('Content-Type', 'text/html'),
("Server", "PythonWebServer")])
return gettime(path_info)
else:
# app函数的返回值就是响应体
start_response('404 Not Found', [('Content-Type', 'text/html'),
("Server", "PythonWebServer")])
return 'Hello World!!!!!' + str(environ)
def start_response(self,status, header_list):
"""设置响应状态和响应头部 web服务器提供给web应用程序调用的"""
# 参数1 是一个状态-str类型
# 参数2 是多个响应头构成的列表<顺序> [(名,值),(),()]
response_line = "HTTP/1.1 %s\r\n" % status
self.status_headers = response_line
for header_name, header_value in header_list:
self.status_headers += "%s: %s\r\n"%(header_name, header_value)
以下例中,用到的函数解析:
Argv函数,是指在命令行输入的时候可以取出他们的参数
import sys
Data = sys.argv[1], 表示将命令行索引坐标为1的数赋值给Data
# __import__是import函数的底层实现
# 参数是 模块的名称 返回值就是模块对象
# 重点理解 -- import threading 等价于 threading = __import__("threading")
# getattr(对象,属性名) 返回值就是属性的引用
# 重点理解 -- Application.app 等价于 app = getattr(Application, "app")
4. 实现:web服务器
from gevent import monkey
monkey.patch_all() # recv recvfrom accept time.sleep()在本应该阻塞的地方能够被gevent自动切换
import gevent
import socket
import re
import sys
"""
a 创建TCP套接字 绑定和监听
b 接受客户端的连接请求
c 接收客户端的HTTP请求报文数据
d 解析 ------> 发送HTTP响应报文数据
"""
class HTTPServer(object):
def __init__(self,port,app):
"""初始化"""
# a 创建TCP套接字 绑定和监听
self.tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置套接字选项 忽略等待2MSL时间-----> 立即重新绑定该端口
self.tcp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定端口 -- 固定端口
self.tcp_socket.bind(('', port))
# 设置为被动监听模式
self.tcp_socket.listen(128)
# 在web服务器中保存一个应用程序提供的函数的引用
self.app = app
def start(self):
while True:
# b 接受客户端的连接请求
client_socket, client_addr = self.tcp_socket.accept()
print("接受来自%s的连接请求" % str(client_addr))
# 调用函数 完成对客户端的响应
# client_handler(client_socket)
# 在接收一个用户请求的时候 创建一个协程并且启动 为一个客户服务
gevent.spawn(self.client_handler, client_socket)
# 在start方法中 while True不会退出
def client_handler(self, client_socket):
"""处理客户端请求的函数"""
# c 接收客户端的HTTP请求报文数据
# 接收请求数据
request_data = client_socket.recv(4096)
if not request_data:
print("客户端已经断开连接")
client_socket.close()
# print(request_data)
# 解码
request_str_data = request_data.decode()
data_list = request_str_data.split("\r\n")
# for line in data_list:
# print(line)
# 获取到请求行 GET /a/b/c/index.html HTTP/1.1
request_line = data_list[0]
result = re.match(r"\w+\s+(\S+)", request_line)
if not result:
print("格式错误")
client_socket.close()
return
# 格式正确
# 用户的请求路径信息
path_info = result.group(1)
print("收到用户的请求:" + path_info)
# web服务器的默认规则
# 当用户访问/目录的时候 默认使其能够访问一个网页
if path_info == '/':
path_info = "/index.html"
# 用户请求的路径信息 相关的文件数据当做响应体发送给客户
# d 解析 ------> 发送HTTP响应报文数据
"""
HTTP/1.1 200 OK\r\nServer: PythonWebServer6.0\r\n\r\n文件数据
HTTP/1.1 404 Not Found\r\nServer: PythonWebServer6.0\r\n\r\nerror
"""
# .jsp .php .aspx
# 首先判断用户资源请求类型是静态.css .png .jpg 的还是动态.py
env = {
"PATH_INFO": path_info
}
# 字典是一种散列结构 -- 无序
if path_info.endswith(".py"):
# 动态资源请求
# 调用应用程序提供的一个函数 函数的返回值就是响应体
response_body = self.app(env, self.start_response)
# 将web服务器中已经保存的响应状态和头 + 响应体
response_data = self.status_headers + "\r\n" + response_body
client_socket.send(response_data.encode())
client_socket.close()
else:
# 服务器指定路径 用户请求路径
# static /index.html ---》 /home/it/123.txt
try:
# file = open("static"+ path_info, 'rb')
#
# # 如果文件很大 会有隐患 rb读取的文件 就是Bytes类型
# file_data = file.read()
# file.close()
with open("static" + path_info, 'rb') as file:
file_data = file.read()
except Exception as e:
print(e)
# 出现异常 路径没有相关的文件数据
# 响应行
response_line = "HTTP/1.1 404 Not Found\r\n"
# 响应头
response_header = "Server: PythonWebServer6.0\r\n"
# 下面这个头 会让浏览器将响应体数据 当做utf-8编码处理
response_header += "Content-Type: text/html;charset=utf-8\r\n"
# 响应体
response_body = "ERROR!!!!!你所请求的资源没有找到"
response_data = response_line + response_header + "\r\n" + response_body
client_socket.send(response_data.encode())
else:
# 响应行
response_line = "HTTP/1.1 200 OK\r\n"
# 响应头
response_header = "Server: PythonWebServer6.0\r\n"
# 响应体 Bytes类型
response_body = file_data
# 将相应数据拼接
response_data = (response_line + response_header + "\r\n").encode() + response_body
# send函数的返回值表示 发送数据的字节数
# send_len = client_socket.send(response_data)
# print("数据总大小%d 已经发送%d" % (len(response_data), send_len))
client_socket.sendall(response_data)
finally:
# 关闭套接字
client_socket.close()
def start_response(self,status, header_list):
"""设置响应状态和响应头部 web服务器提供给web应用程序调用的"""
# 参数1 是一个状态-str类型
# 参数2 是多个响应头构成的列表<顺序> [(名,值),(),()]
response_line = "HTTP/1.1 %s\r\n" % status
self.status_headers = response_line
for header_name, header_value in header_list:
self.status_headers += "%s: %s\r\n"%(header_name, header_value)
def main():
# python3 webxxxx.py 9999
# 创建一个实例对象
# 获取到操作系统给我传递的 命令行参数列表
# print(sys.argv)
# 第三个版本 通过命令行参数指定 应用程序所在模块:函数
if len(sys.argv) != 3:
print("使用方式 python3 webxxxx.py 9999 应用模块名:函数名")
return
data = sys.argv[1]
# print(sys.argv[2])
mod_name_app_name = sys.argv[2]
data_list = mod_name_app_name.split(":")
if len(data_list) != 2:
print("使用方式 python3 webxxxx.py 9999 应用模块名:函数名")
return
# 模块名 “Application”
module_name = data_list[0]
# 函数名 "app"
app_name = data_list[1]
# __import__是import函数的底层实现
# 参数是 模块的名称 返回值就是模块对象
# 重点理解 -- import threading 等价于 threading = __import__("threading")
Application = __import__(module_name)
# getattr(对象,属性名) 返回值就是属性的引用
# 重点理解 -- Application.app 等价于 app = getattr(Application, "app")
app = getattr(Application, "app")
if data.isdigit():
port = int(data)
http_server = HTTPServer(port, app)
# 启动HTTP服务器的运行
http_server.start()
if __name__ == '__main__':
main()
5. 实现:web框架 Application.py
import time
import re
# 指定模板文件资源 所在的根目录
TEMPLATE_ROOT = "./template"
def gettime(path_info):
"""当用户请求/gettime.py的时候执行该函数代码"""
return time.ctime()
def center(path_info):
"""当用户请求/index.py的时候执行该函数代码"""
# 读取模板文件
# /index.py ----》 /index.html
path_info = path_info.replace(".py", ".html")
with open(TEMPLATE_ROOT + path_info) as file:
html = file.read()
# 将模板文件的数据进行 替换 --正则替换 {%content%}
data_from_mysql = "这是从数据库中查询出来的相关信息"
html = re.sub(r"\{%content%\}",data_from_mysql,html)
return html
def app(environ, start_response):
# 这个函数是应用框架提供给web服务器调用
# web服务器在接收到动态资源请求的时候调用这个函数
# 参数1 是一个含有HTTP请求相关信息的字典
# 字典中的'PATH_INFO': '/index.html'就是用户请求的路径
# 参数2 是一个web提供的函数的引用
# start_response函数功能主要是用以设置 str响应状态和响应头部
# start_response参数1 是需要设置的响应状态
# start_response参数2 是一个响应头信息的列表 列表中每个元素是一个元组
# 元组前者是响应头名称 后者是响应头对应的值
# 获取到用户请求路径
path_info = environ['PATH_INFO']
# 当用户访问已经实现了功能的路径 就返回200 OK 其余的返回404 Not Found
if path_info == '/gettime.py':
start_response('200 OK', [('Content-Type', 'text/html'),
("Server", "PythonWebServer")])
return gettime(path_info)
elif path_info == '/index.py':
# 读取模板文件 并且进行替换 然后通过返回值返回给web服务器
start_response('200 OK', [('Content-Type', 'text/html'),
("Server", "PythonWebServer")])
return index(path_info)
else:
# app函数的返回值就是响应体
start_response('404 Not Found', [('Content-Type', 'text/html'),
("Server", "PythonWebServer")])
return 'Hello World!!!!!' + str(environ)
6. 图片展示访问交互过程