1前情回顾
域名后面没有加端口,就是采用默认的端口。HTTP是80HTTPS是443
请求报文格式:请求行,请求头,空行,请求体。前三个每个后面都有\r\n
响应报文格式:响应行、响应头、空行、响应体。
状态码:2xx成功3xx重定向4xx客户端错误5xx服务器错误
HTTP协议用在浏览器和服务器之间
应用层协议
基于TCP
工作模式:一次请求,一次响应。多次请求,多次响应
提前将每个知识点过一遍
2 web服务器
2.1目的
理解一下web服务器的出路流程
将前面的知识融合起来
2.2介绍
简单扩充一下:
互联网:泛指一切可以互联互通的网络
因特网:偏向于网页、邮件之类的(不包括局域网)外网
万维网:特指浏览器和web服务器之间的
2.3 案例
2.3.1返回固定数据
注意:三引号是一个多行字符串,有注释的功能
1"""
2三引号是一个多行字符串,有注释的功能
3"""
4import socket
5def main():
6 # 1创建套接字 绑定 监听套接字
7 server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
8 server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
9 server_socket.bind(('',10086))
10 server_socket.listen(128)
11 while True:
12 # 2 不断接受连接
13 client_socket,client_addr = server_socket.accept()
14 print("接受到了来自%s的连接请求" % str(client_addr))
15 # 3 接收请求报文
16 request_data = client_socket.recv(4096).decode()
17 # 4 解析请求报文 得到用户的资源路径
18 # 5 读取对应资源 封装在http响应报文中发送给浏览器
19 response_line = 'HTTP/1.1 200 OK\r\n' # 响应行 状态行
20 # 如果想要显示中文 网页默认是gbk
21 # 加入'Content-Type: text/html;charset=UTF-8\r\n'
22 response_header = 'Server: BMW1.0\r\n' # 响应头
23 request_body = 'Hello Ethan yan' # 响应体
24 response_data = response_line + response_header +'\r\n'+request_body
25 client_socket.send(response_data.encode())
26 # 6 在合适的位置关闭套接字
27 client_socket.close()
28
29if __name__ == '__main__':
30 main()
2.3.2返回固定网页
1import socket
2def main():
3 server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
4 server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
5 server_socket.bind(('',10010))
6 server_socket.listen(128)
7 while True:
8 client_socket,client_addr = server_socket.accept()
9 print("来自%s的最强王者上线了...." % str(client_addr))
10 request_data = client_socket.recv(4096).decode()
11 print("请求报文".center(30,'='))
12 print(request_data)
13 response_line = 'HTTP/1.1 200 OK\r\n'
14 response_header = 'Server: BMW1.0\r\n'+ 'Content-Type: text/html;charset=UTF-8\r\n'
15 # response_body = "来啦?老弟"
16 with open('index.html','rb') as file:
17 response_body = file.read()
18 response_data = (response_line + response_header + '\r\n').encode() + response_body
19 client_socket.send(response_data)
20 client_socket.close()
21
22if __name__ == '__main__':
23 main()
2.3.3返回指定网页
1import socket
2import re
3def main():
4 server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
5 server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
6 server_socket.bind(('',10010))
7 server_socket.listen(128)
8 while True:
9 client_socket,client_addr = server_socket.accept()
10 print("来自%s的最强王者上线了...." % str(client_addr))
11 request_data = client_socket.recv(4096).decode()
12 if not request_data:
13 print("最强王者已经下限了")
14 client_socket.close()
15 continue
16
17 # GET /index2.html HTTP/1.1\r\n
18 # 切割请求报文 第0个元素就是请求行
19 # 连接的时候,浏览器会提前创建好几个链接
20 request_line = request_data.split('\r\n')[0]
21 # 从请求行中提取出用户的资源请求路径
22 result = re.match(r'\w+\s+(\S+)',request_line)
23 if not result:
24 print("用户请求错误")
25 client_socket.close()
26 continue
27 print("用户的请求路径是%s" % result.group(1))
28 path_info = result.group(1)
29 # 当用户请求/ 表示首页 返回一个首页资源
30 if path_info == '/':
31 path_info = '/index.html'
32
33 response_line = 'HTTP/1.1 200 OK\r\n'
34 response_header = 'Server: BMW1.0\r\n'
35 # response_body = "来啦?老弟"
36 # 加'static'表示用户请求的路径都在这个路径下,从而保证系统的安全
37
38 with open('static'+path_info,'rb') as file:
39 response_body = file.read()
40 response_data = (response_line + response_header + '\r\n').encode() + response_body
41 client_socket.send(response_data)
42 client_socket.close()
43
44if __name__ == '__main__':
45 main()
2.3.4增加404页面
1import socket
2import re
3import os
4"""
5web服务作用: 接收请求报文 返回网页资源给web浏览器
6
7web服务器流程:
8 1 创建 绑定 监听套接字
9 2 接受连接
10 3 接收请求报文
11 4 解析请求报文 得到用户的资源请求路径
12 5 读取对应资源 封装在HTTP响应报文中发送给浏览器
13 6 在合适的位置关闭套接字即可
14"""
15def main():
16 # 1 创建 绑定 监听套接字
17 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
18 server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
19 server_socket.bind(('', 9999))
20 server_socket.listen(128)
21
22 # 2 不断地接受连接
23 while True:
24 client_socket, client_addr = server_socket.accept()
25 print("接受到了来自%s的连接请求" % str(client_addr))
26
27 # 3 接收请求报文
28 request_data = client_socket.recv(4096).decode()
29 if not request_data:
30 print("用户已经断开连接了")
31 client_socket.close()
32 continue
33
34 # 4 解析请求报文 得到用户的资源请求路径 "GET /index2.html HTTP/1.1\r\n..."
35 # 4.1 切割请求报文 第0个元素就是请求行
36 request_line = request_data.split("\r\n")[0]
37 # 4.2 从请求行中提取出 用户资源请求路径
38 result = re.match(r"\w+\s+(\S+)", request_line)
39 if not result:
40 print("用户请求格式错误")
41 client_socket.close()
42 continue
43
44 print("用户请求的路径是%s" % result.group(1)) # /home/python/Desktop/index.html
45 path_info = result.group(1)
46 # 当用户请求/ 表示首页 返回一个首页资源
47 if path_info == '/':
48 path_info = '/index.html'
49 response_header = "Server: PWS1.0\r\n" # 响应头
50 # 5 读取对应资源 封装在HTTP响应报文中发送给浏览器
51 if not os.path.exists("static" + path_info):
52 response_line = "HTTP/1.1 404 Not Found\r\n"
53 with open("static/404.html","rb") as file:
54 response_body = file.read()
55 else:
56 response_line = "HTTP/1.1 200 OK\r\n" # 响应行 状态行
57
58 with open("static" + path_info, "rb") as file:
59 response_body = file.read()
60
61 response_data = (response_line + response_header + "\r\n").encode() + response_body
62 client_socket.send(response_data)
63
64 # 6 在合适的位置关闭套接字即可
65 client_socket.close()
文本文件二进制编码解码没有问题,但是如果是图片,就会出现问题
协程用在web
如果在发送文件的时候,有可能一次发送不完整,显示有误,可能是文件太大。我们可以将send方法改成sendall方法,即可解决
2.3.5 多任务
1from gevent import monkey
2monkey.patch_all() # 自动切换 recv accept time.sleep
3import socket
4import re
5import os
6import gevent
7
8"""
9web服务作用: 接收请求报文 返回网页资源给web浏览器
10
11web服务器流程:
12 1 创建 绑定 监听套接字
13 2 接受连接
14 3 接收请求报文
15 4 解析请求报文 得到用户的资源请求路径
16 5 读取对应资源 封装在HTTP响应报文中发送给浏览器
17 6 在合适的位置关闭套接字即可
18"""
19def request_handler(client_socket):
20 """这个函数用来处理客户端请求的"""
21 # 3 接收请求报文
22 request_data = client_socket.recv(4096).decode()
23 if not request_data:
24 print("最强王者已经下线了....")
25 client_socket.close()
26 return
27
28 # 4 解析请求报文 得到用户的资源请求路径 "GET /index2.html HTTP/1.1\r\n..."
29 # 4.1 切割请求报文 第0个元素就是请求行
30 request_line = request_data.split("\r\n")[0]
31 # 4.2 从请求行中提取出 用户资源请求路径
32 result = re.match(r"\w+\s+(\S+)", request_line)
33 if not result:
34 print("用户请求格式错误")
35 client_socket.close()
36 return
37
38 print("用户请求的路径是%s" % result.group(1)) # /home/python/Desktop/index.html
39 path_info = result.group(1)
40 # 当用户请求/ 表示首页 返回一个首页资源
41 if path_info == '/':
42 path_info = '/index.html'
43 response_header = "Server: PWS1.0\r\n" # 响应头
44 # 5 读取对应资源 封装在HTTP响应报文中发送给浏览器
45 if not os.path.exists("static" + path_info):
46 response_line = "HTTP/1.1 404 Not Found\r\n"
47 with open("static/404.html", "rb") as file:
48 response_body = file.read()
49 else:
50 response_line = "HTTP/1.1 200 OK\r\n" # 响应行 状态行
51
52 with open("static" + path_info, "rb") as file:
53 response_body = file.read()
54
55 response_data = (response_line + response_header + "\r\n").encode() + response_body
56 client_socket.sendall(response_data)
57
58 # 6 在合适的位置关闭套接字即可
59 client_socket.close()
60
61def main():
62 # 1 创建 绑定 监听套接字
63 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
64 server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
65 server_socket.bind(('', 9999))
66 server_socket.listen(128)
67
68 # 2 不断地接受连接
69 while True:
70 client_socket, client_addr = server_socket.accept()
71 print("来自%s的最强王者上线了...." % str(client_addr))
72 # 当收到用户请求时, 启动一个协程来运行下面的函数
73 gevent.spawn(request_handler,client_socket)
74 # spawn创建协程并启动
75 # request_handler(client_socket)
76
77
78
79if __name__ == '__main__':
80 main()
2.3.6 面向对象
面向对象 每人的理解都不一样 面向对象 vs 面向过程 狗吃翔 吃狗翔
1from gevent import monkey
2monkey.patch_all() # 自动切换 recv accept time.sleep
3import socket
4import re
5import os
6import gevent
7
8"""
9web服务作用: 接收请求报文 返回网页资源给web浏览器
10
11web服务器流程:
12 1 创建 绑定 监听套接字
13 2 接受连接
14 3 接收请求报文
15 4 解析请求报文 得到用户的资源请求路径
16 5 读取对应资源 封装在HTTP响应报文中发送给浏览器
17 6 在合适的位置关闭套接字即可
18"""
19
20class HTTPServer(object):
21 """web服务器类"""
22 def __init__(self):
23 """初始化 实例对象"""
24 # 1 创建 绑定 监听套接字
25 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
26 server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
27 server_socket.bind(('', 9999))
28 server_socket.listen(128)
29 # 将套接字对象保存为当前对象
30 self.server_socket = server_socket
31 def start(self):
32 # 2 不断地接受连接
33 while True:
34 client_socket, client_addr = self.server_socket.accept()
35 print("来自%s的最强王者上线了...." % str(client_addr))
36 # 当收到用户请求时, 启动一个协程来运行下面的函数
37 gevent.spawn(self.request_handler, client_socket)
38 # spawn创建协程并启动
39 # request_handler(client_socket)
40 @staticmethod # 装饰器
41 def request_handler(client_socket):
42 """这个函数用来处理客户端请求的"""
43 # 3 接收请求报文
44 request_data = client_socket.recv(4096).decode()
45 if not request_data:
46 print("最强王者已经下线了....")
47 client_socket.close()
48 return
49
50 # 4 解析请求报文 得到用户的资源请求路径 "GET /index2.html HTTP/1.1\r\n..."
51 # 4.1 切割请求报文 第0个元素就是请求行
52 request_line = request_data.split("\r\n")[0]
53 # 4.2 从请求行中提取出 用户资源请求路径
54 result = re.match(r"\w+\s+(\S+)", request_line)
55 if not result:
56 print("用户请求格式错误")
57 client_socket.close()
58 return
59
60 print("用户请求的路径是%s" % result.group(1)) # /home/python/Desktop/index.html
61 path_info = result.group(1)
62 # 当用户请求/ 表示首页 返回一个首页资源
63 if path_info == '/':
64 path_info = '/index.html'
65 response_header = "Server: PWS1.0\r\n" # 响应头
66 # 5 读取对应资源 封装在HTTP响应报文中发送给浏览器
67 if not os.path.exists("static" + path_info):
68 response_line = "HTTP/1.1 404 Not Found\r\n"
69 with open("static/404.html", "rb") as file:
70 response_body = file.read()
71 else:
72 response_line = "HTTP/1.1 200 OK\r\n" # 响应行 状态行
73
74 with open("static" + path_info, "rb") as file:
75 response_body = file.read()
76
77 response_data = (response_line + response_header + "\r\n").encode() + response_body
78 client_socket.sendall(response_data)
79
80 # 6 在合适的位置关闭套接字即可
81 client_socket.close()
82def main():
83 # 创建出一个web服务器类的实例对象
84 http_server = HTTPServer()
85 # 启动服务器运行
86 http_server.start()
87if __name__ == '__main__':
88 main()
89# 面向对象 每人的理解都不一样
90# 面向对象 vs 面向过程
91# 狗吃翔 吃狗翔
2.3.7 给web服务器添加命令行参数
耦合 功能与功能之间的关联程度
开发:解耦合
高内聚,低耦合
独立性 依赖性
sys.argv里面存放的是当前进程启动时的命令行参数
sys.argv是列表,每个元素是字符串
系统将命令行参数放进去的
1from gevent import monkey
2monkey.patch_all() # 自动切换 recv accept time.sleep
3import socket
4import re
5import os
6import gevent
7import sys
8"""
9web服务作用: 接收请求报文 返回网页资源给web浏览器
10
11web服务器流程:
12 1 创建 绑定 监听套接字
13 2 接受连接
14 3 接收请求报文
15 4 解析请求报文 得到用户的资源请求路径
16 5 读取对应资源 封装在HTTP响应报文中发送给浏览器
17 6 在合适的位置关闭套接字即可
18"""
19
20class HTTPServer(object):
21 """web服务器类"""
22 def __init__(self,port):
23 """初始化 实例对象"""
24 # 1 创建 绑定 监听套接字
25 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
26 server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
27
28 server_socket.bind(('',port))
29 server_socket.listen(128)
30 # 将套接字对象保存为当前对象
31 self.server_socket = server_socket
32 def start(self):
33 # 2 不断地接受连接
34 while True:
35 client_socket, client_addr = self.server_socket.accept()
36 print("来自%s的最强王者上线了...." % str(client_addr))
37 # 当收到用户请求时, 启动一个协程来运行下面的函数
38 gevent.spawn(self.request_handler, client_socket)
39 # spawn创建协程并启动
40 # request_handler(client_socket)
41 @staticmethod # 装饰器
42 def request_handler(client_socket):
43 """这个函数用来处理客户端请求的"""
44 # 3 接收请求报文
45 request_data = client_socket.recv(4096).decode()
46 if not request_data:
47 print("最强王者已经下线了....")
48 client_socket.close()
49 return
50
51 # 4 解析请求报文 得到用户的资源请求路径 "GET /index2.html HTTP/1.1\r\n..."
52 # 4.1 切割请求报文 第0个元素就是请求行
53 request_line = request_data.split("\r\n")[0]
54 # 4.2 从请求行中提取出 用户资源请求路径
55 result = re.match(r"\w+\s+(\S+)", request_line)
56 if not result:
57 print("用户请求格式错误")
58 client_socket.close()
59 return
60
61 print("用户请求的路径是%s" % result.group(1)) # /home/python/Desktop/index.html
62 path_info = result.group(1)
63 # 当用户请求/ 表示首页 返回一个首页资源
64 if path_info == '/':
65 path_info = '/index.html'
66 response_header = "Server: PWS1.0\r\n" # 响应头
67 # 5 读取对应资源 封装在HTTP响应报文中发送给浏览器
68 if not os.path.exists("static" + path_info):
69 response_line = "HTTP/1.1 404 Not Found\r\n"
70 with open("static/404.html", "rb") as file:
71 response_body = file.read()
72 else:
73 response_line = "HTTP/1.1 200 OK\r\n" # 响应行 状态行
74
75 with open("static" + path_info, "rb") as file:
76 response_body = file.read()
77
78 response_data = (response_line + response_header + "\r\n").encode() + response_body
79 client_socket.sendall(response_data)
80
81 # 6 在合适的位置关闭套接字即可
82 client_socket.close()
83def main():
84 # sys.argv对象 存储的是 当前进程运行的命令参数 的列表 每个元素都是字符串
85 # 如果说数量属于2个(缺端口) 或者 端口数据不是数字字符构成的
86
87 if len(sys.argv) < 2 or not sys.argv[1].isdigit():
88 print("参数使用错误 usage:python3 添加命令行参数指定端口.py 8888")
89 return
90 port = int(sys.argv[1])
91 # 创建出一个web服务器类的实例对象
92 http_server = HTTPServer(port)
93 # 启动服务器运行
94 http_server.start()
95if __name__ == '__main__':
96 main()