learning from 《python web开发从入门到精通》
HTTP
协议(HyperText Transfer Protocol
超文本传输协议)HTTP
协议实现TCP
在两台计算机(如Web服务器,客户端)之间传输信息,客户端使用 web浏览器发送 HTTP
请求给 web 服务器,服务器发送响应给客户端当在浏览器中输入 url 后:
HTTP Request
请求 给拥有该 IP 的主机HTTP Response
响应,浏览器渲染效果后呈现给用户Web 服务器工作原理:
TCP/IP
协议建立到服务器的 TCP
连接HTTP
协议请求包,请求资源HTTP
协议应答包,如果资源包含动态语言内容,会先进行处理,得到的数据返回客户端,客户端解释 HTML
渲染在屏幕上常用请求方法:
返回状态码:
1**
:请求收到,继续处理2**
:成功返回响应3**
:重定向,为了完成请求,必须进一步执行的动作4**
:客户端错误,如语法错误,或者请求无法实现5**
:服务器错误,服务器不能实现一种明显无效的请求浏览器 按 F12 可以查看相关信息
HTML
页面被称为 静态页面
例子:创建一个静态服务器,通过该服务器可以访问包含两个静态页面的网站
simple_navbar.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用bootstrap框架制作导航栏</title>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.0.2/css/bootstrap.css" rel="stylesheet">
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/popper.js/2.9.2/cjs/popper-base.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.0.2/js/bootstrap.js"></script>
</head>
<body>
<nav class="navbar navbar-expand-sm navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="https://michael.blog.csdn.net/">我的导航</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="https://michael.blog.csdn.net/">主页</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">链接</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button"
data-bs-toggle="dropdown" aria-expanded="false">
关于作者
</a>
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
<li><a class="dropdown-item" href="#">简历</a></li>
<li><a class="dropdown-item" href="#">项目</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li><a class="dropdown-item" href="contact.html">联系方式</a></li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link disabled">商城</a>
</li>
</ul>
<form class="d-flex">
<input class="form-control me-2" type="search" placeholder="输入你的关键词" aria-label="Search">
<button class="btn btn-outline-success" type="submit">站内搜索</button>
</form>
</div>
</div>
</nav>
</body>
</html>
contact.html
,在上面的基础上,添加一些额外的联系信息<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>联系michael</title>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.0.2/css/bootstrap.css" rel="stylesheet">
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/popper.js/2.9.2/cjs/popper-base.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.0.2/js/bootstrap.js"></script>
</head>
<body>
<nav class="navbar navbar-expand-sm navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="https://michael.blog.csdn.net/">我的导航</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="https://michael.blog.csdn.net/">主页</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">链接</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button"
data-bs-toggle="dropdown" aria-expanded="false">
关于作者
</a>
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
<li><a class="dropdown-item" href="#">简历</a></li>
<li><a class="dropdown-item" href="#">项目</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li><a class="dropdown-item" href="contact.html">联系方式</a></li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link disabled">商城</a>
</li>
</ul>
<form class="d-flex">
<input class="form-control me-2" type="search" placeholder="输入你的关键词" aria-label="Search">
<button class="btn btn-outline-success" type="submit">站内搜索</button>
</form>
</div>
</div>
</nav>
<div class="bs-docs-header" id="content" tabindex="-1">
<div class="container">
<h1> 联系michael </h1>
<div class="lead">
<address>
电子邮件:<strong>michael@xxx.com</strong>
<br>地址:地球村86号
<br>邮政编码:<strong>xxxxxx</strong>
<br><abbr title="Phone">联系电话:</abbr> 1234567890
</address>
</div>
</div>
</div>
</body>
</html>
实现客户端和服务器的 HTTP
通信
注:由于 HTML 里面写了很多下载的 css 文件地址,路径总是报错,最后还是 建议引用 CDN 写法,相关库地址查询https://www.bootcdn.cn/
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.0.2/css/bootstrap.css" rel="stylesheet">
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/popper.js/2.9.2/cjs/popper-base.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.0.2/js/bootstrap.js"></script>
import socket
import re
from multiprocessing import Process # 多线程
HTML_ROOT_DIR = './' # 设置静态页面的根目录
class HTTPServer:
def __init__(self):
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def start(self):
self.server_socket.listen(128) # 最大连接数128
print("服务器等待客户端连接...")
while True:
client_socket, client_addr = self.server_socket.accept() # 建立客户端连接
print("[%s, %s]用户连接上了" % client_addr)
handle_client_process = Process(target=self.handle_client, args=(client_socket,))
# 实例化线程,第一个参数调用函数 ,第二个参数 传递给前者的参数,元组形式
handle_client_process.start() # 开启线程
client_socket.close() # 关闭客户端socket
def handle_client(self, client_socket):
# 处理客户端请求
request_data = client_socket.recv(1024) # 接收客户端请求
print("request data:", request_data)
request_lines = request_data.splitlines() # 按行分割
for line in request_lines:
print(line) # 输出信息
request_start_line = request_lines[0] # 获取请求报文
print("*" * 10)
print(request_start_line.decode("utf-8"))
file_name = re.match(r"\w+ +(/[^ ]*) ", request_start_line.decode("utf-8")).group(1)
# 使用正则表达式,提取请求的文件名,group(1) 列出第一个括号匹配部分
if file_name == "/":
file_name = "/simple_navbar.html"
try:
# 尝试打开文件
file = open(HTML_ROOT_DIR + file_name, "rb")
except IOError:
# 读取文件失败,返回404
response_start_line = "HTTP/1.1 404 Not Found\r\n"
response_headers = "Server: Michael server\r\n"
response_body = "The file %s is not found! please check again!" % (HTML_ROOT_DIR + file_name)
else:
file_data = file.read()
file.close()
# 构造响应数据
response_start_line = "HTTP/1.1 200 OK\r\n"
response_headers = "Server: Michael server\r\n"
response_body = file_data.decode("utf-8")
# 拼接返回数据
response = response_start_line + response_headers + "\r\n" + response_body
print("response data:", response)
client_socket.send(bytes(response, "utf-8")) # 向客户端发送响应数据
client_socket.close() # 关闭客户端连接
def bind(self, port):
self.server_socket.bind(("127.0.0.1", port))
def main():
http_server = HTTPServer()
http_server.bind(8000)
http_server.start()
if __name__ == "__main__":
main()
上面实现了一个静态服务器,但是现在很少使用,更多的是使用 动态页面,实现交互性
Common Gateway Interface
是一段程序,运行在服务器上CGI
局限性:创建完解释器进程,用完就抛弃,大量的请求导致服务器停机
CGI
加强版 FastCGI
出现,其使用 进程/线程池
来处理一连串的请求
减少了 网页服务器
与 CGI 程序
之间 交互的开销FastCGI
标准下写异步的 Web 服务不太方便,WSGI (Web Server Gateway Interface 服务器网关接口)
出现下图 from https://www.cnblogs.com/wilber2013/p/4763067.html
最简单的web版本 hello world
def applications(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return [b'<h1>Hello, Michael!</h1>']
上面函数就是符合 WSGI 标准的一个 HTTP 处理函数
environ
:一个所有 HTTP 请求信息的字典对象start_response
:一个发送 HTTP 响应的函数好处:web 解析 和 应用程序逻辑 分离,可以各自做自己擅长的事
python 内置了 WSGI:wsgiref
模块(它没有考虑运行效率,仅供开发测试)
wsgi_app.py
# wsgi 应用程序
def app(environ, start_response):
# 响应信息
start_response('200 OK', [('Content-Type', 'text/html')])
file_name = environ['PATH_INFO'][1:] or 'simple_navbar.html'
HTML_ROOT_DIR = './'
try:
# 打开文件
file = open(HTML_ROOT_DIR + file_name, 'rb')
except IOError:
# 响应异常
response_body = "{} not found".format(HTML_ROOT_DIR + file_name)
else:
# 读取文件
file_data = file.read()
file.close()
# 构造响应数据
response_body = file_data.decode('utf-8')
return [response_body.encode('utf-8')] # 返回数据
wsgi_server.py
# WSGI 服务器
from wsgiref.simple_server import make_server
from wsgi_app import app
# 创建一个服务器,IP地址为空,端口号为8000,处理函数是app
httpd = make_server('', 8000, app)
print('Serving HTTP on port 8000...')
httpd.serve_forever() # 开始监听HTTP请求
运行 wsgi_server.py ,在浏览器输入 http://127.0.0.1:8000/
就可以访问网页了