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()