基于TCP的Web服务器
模拟浏览器请求web服务器的网页过程,使用TCP实现HTTP协议(请求报文格式和响应报文格式)
"""
1、导入模块
2、创建套接字
3、建立连接
4、拼接请求协议
5、发送请求协议
6、接收服务器响应内容
7、保存内容
8、关闭连接
"""
# 1、导入模块
import socket
# 2、创建套接字
tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3、建立连接
tcp_client_socket.connect(("www.baidu.com", 80))
# 4、拼接请求协议
request_line = "GET / HTTP/1.1\r\n"
# 4.2 请求头
request_header = "Host:www.baidu.com\r\n"
# 4.3 请求空行
request_blank = "\r\n"
# 整体拼接
request_data = request_line + request_header + request_blank
# 5、发送请求协议
tcp_client_socket.send(request_data.encode())
# 6、接收服务器响应内容
recv_date = tcp_client_socket.recv(4096).decode()
# print(recv_date)
# 7、保存内容
print(recv_date[recv_date.find("\r\n\r\n")+4:recv_date.find("\r\n\r\n")+104]) # 节省空间,只取100个字符
#或
recv_list = []
for i in recv_date.split("\r\n"):
recv_list.append(i)
print(recv_list[-1][:100]) # 节省空间,只取100个字符
# 8、关闭连接
tcp_client_socket.close()
<!DOCTYPE html><html>
<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<!DOCTYPE html><html>
<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
简单的Web服务器
"""
1、导入模块
2、创建套接字
3、设置地址重用
4、绑定端口
5、设置监听,让套接字由主动变为被动接收
6、接受客户端连接 定义函数 request_handler()
7、接收客户端浏览器发送的请求协议
8、判断协议是否为空
9、拼接响应的报文
10、发送响应报文
11、关闭操作
"""
# 1、导入模块
import socket
# 定义函数 request_handler()
def request_handler(new_client_socket, ip_port):
# 7、接收客户端浏览器发送的请求协议
request_data = new_client_socket.recv(1024).decode()
# print(request_data)
# 8、判断协议是否为空
if not request_data:
print(f"客户端{str(ip_port)}已经下线!")
new_client_socket.close()
return
else:
# 9、拼接响应的报文
# 9.1 响应行
response_line = "HTTP/1.1 200 OK\r\n"
# 9.2 响应头
response_header = "Server:Python20WS/2.1\r\n"
# 9.3 响应空行
response_blank = "\r\n"
# 9.4 响应的主体
response_body = "HelloWorld!"
# 9.5 响应报文拼接
response_data = response_line + response_header + response_blank + response_body
# 10、发送响应报文
new_client_socket.send(response_data.encode())
# 11、关闭当前连接
new_client_socket.close()
def main():
"""主函数段"""
# 2、创建套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3、设置地址重用
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 4、绑定端口
tcp_server_socket.bind(("", 8080))
# 5、设置监听,让套接字由主动变为被动接收
tcp_server_socket.listen(128)
# 可循环访问
while True:
# 6、接受客户端连接
new_client_socket, ip_port = tcp_server_socket.accept()
# 调用函数
request_handler(new_client_socket, ip_port)
# 12、关闭操作
tcp_server_socket.close()
if __name__ == '__main__':
main()
# HelloWorld!
简单的Web服务器-返回固定页面
"""
1、导入模块
2、创建套接字
3、设置地址重用
4、绑定端口
5、设置监听,让套接字由主动变为被动接收
6、接受客户端连接 定义函数 request_handler()
7、接收客户端浏览器发送的请求协议
8、判断协议是否为空
9、拼接响应的报文
10、发送响应报文
11、关闭操作
"""
# 1、导入模块
import socket
# 定义函数 request_handler()
def request_handler(new_client_socket, ip_port):
# 7、接收客户端浏览器发送的请求协议
request_data = new_client_socket.recv(1024).decode()
# print(request_data)
# 8、判断协议是否为空
if not request_data:
print(f"客户端{str(ip_port)}已经下线!")
new_client_socket.close()
return
else:
# 9、拼接响应的报文
# 9.1 响应行
response_line = "HTTP/1.1 200 OK\r\n"
# 9.2 响应头
response_header = "Server:Python20WS/2.1\r\n"
# 9.3 响应空行
response_blank = "\r\n"
# 9.4 响应的主体-固定页面
with open("D:/dist/index.html", "rb") as file:
response_body = file.read()
# 9.5 响应报文拼接
response_data = response_line.encode() + response_header.encode() + response_blank.encode() + response_body
# 10、发送响应报文
new_client_socket.send(response_data)
# 11、关闭当前连接
new_client_socket.close()
def main():
"""主函数段"""
# 2、创建套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3、设置地址重用
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 4、绑定端口
tcp_server_socket.bind(("", 8080))
# 5、设置监听,让套接字由主动变为被动接收
tcp_server_socket.listen(128)
# 可循环访问
while True:
# 6、接受客户端连接
new_client_socket, ip_port = tcp_server_socket.accept()
# 调用函数
request_handler(new_client_socket, ip_port)
# 12、关闭操作
tcp_server_socket.close()
if __name__ == '__main__':
main()
简单的Web服务器-返回指定页面
"""
1、导入模块
2、创建套接字
3、设置地址重用
4、绑定端口
5、设置监听,让套接字由主动变为被动接收
6、接受客户端连接 定义函数 request_handler()
7、接收客户端浏览器发送的请求协议
8、判断协议是否为空
9、拼接响应的报文
10、发送响应报文
11、关闭操作
"""
# 1、导入模块
import socket
# 定义函数 request_handler()
def request_handler(new_client_socket, ip_port):
# 7、接收客户端浏览器发送的请求协议
request_data = new_client_socket.recv(1024).decode()
# print(request_data)
# 8、判断协议是否为空
if not request_data:
print(f"客户端{str(ip_port)}已经下线!")
new_client_socket.close()
return
else:
# 根据客户端浏览器请求的资源路径,返回请求资源
request_line_path = request_data.split("\r\n")[0].split(" ")[1]
print(request_line_path)
# 配置默认访问页面
if request_line_path == "/":
request_line_path = "/index.html"
try:
# 9、拼接响应的报文
# 9.1 响应行
response_line = "HTTP/1.1 200 OK\r\n"
# 9.2 响应头
response_header = "Server:Python20WS/2.1\r\n"
# 9.3 响应空行
response_blank = "\r\n"
# 9.4 响应的主体-固定页面
with open(f"D:/dist{request_line_path}", "rb") as file:
response_body = file.read()
# 9.5 响应报文拼接
response_data = response_line.encode() + response_header.encode() + response_blank.encode() + response_body
# 10、发送响应报文
new_client_socket.send(response_data)
# 11、关闭当前连接
new_client_socket.close()
except Exception as e:
print("访问路径不存在!请重试!")
response_line = "HTTP/1.1 404 Not Found\r\n"
# 9.2 响应头
response_header = "Server:Python20WS/2.1\r\n"
# 9.3 响应空行
response_blank = "\r\n"
# 9.4 响应的主体
response_body = f"Error! 页面{request_line_path}不存在!"
# 9.5 响应报文拼接
response_data = response_line.encode() + response_header.encode() + response_blank.encode() + response_body.encode("GBK")
# 10、发送响应报文
new_client_socket.send(response_data)
# 11、关闭当前连接
new_client_socket.close()
else:
print("访问成功!")
def main():
"""主函数段"""
# 2、创建套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3、设置地址重用
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 4、绑定端口
tcp_server_socket.bind(("", 8080))
# 5、设置监听,让套接字由主动变为被动接收
tcp_server_socket.listen(128)
# 可循环访问
while True:
# 6、接受客户端连接
new_client_socket, ip_port = tcp_server_socket.accept()
# 调用函数
request_handler(new_client_socket, ip_port)
# 12、关闭操作
tcp_server_socket.close()
if __name__ == '__main__':
main()
# /
# 访问成功!
# /favicon.ico
# 访问成功!
# /index2.html
# 访问成功!
# /ddd.html
# 访问路径不存在!请重试!
/
访问成功!
/favicon.ico
访问成功!
/index2.html
访问成功!
/ddd.html
访问路径不存在!请重试!
简单的Web服务器-面向对象封装
"""
1、导入模块
2、创建套接字
3、设置地址重用
4、绑定端口
5、设置监听,让套接字由主动变为被动接收
6、接受客户端连接 定义函数 request_handler()
7、接收客户端浏览器发送的请求协议
8、判断协议是否为空
9、拼接响应的报文
10、发送响应报文
11、关闭操作
"""
# 1、导入模块
import socket
# 创建类
class WebServer(object):
# 初始化方法
def __init__(self):
# 2、创建套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3、设置地址重用
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 4、绑定端口
tcp_server_socket.bind(("", 8080))
# 5、设置监听,让套接字由主动变为被动接收
tcp_server_socket.listen(128)
self.tcp_server_socket = tcp_server_socket
def start(self):
# 启动web服务器
# 可循环访问
while True:
# 6、接受客户端连接
new_client_socket, ip_port = self.tcp_server_socket.accept()
# 调用函数
# self.new_client_socket = new_client_socket
self.request_handler(new_client_socket, ip_port)
# 定义方法 request_handler()
def request_handler(self, new_client_socket, ip_port):
# 7、接收客户端浏览器发送的请求协议
request_data = new_client_socket.recv(1024).decode()
# print(request_data)
# 8、判断协议是否为空
if not request_data:
print(f"客户端{str(ip_port)}已经下线!")
new_client_socket.close()
return
else:
# 根据客户端浏览器请求的资源路径,返回请求资源
request_line_path = request_data.split("\r\n")[0].split(" ")[1]
print(request_line_path)
# 配置默认访问页面
if request_line_path == "/":
request_line_path = "/index.html"
try:
# 9、拼接响应的报文
# 9.1 响应行
response_line = "HTTP/1.1 200 OK\r\n"
# 9.2 响应头
response_header = "Server:Python20WS/2.1\r\n"
# 9.3 响应空行
response_blank = "\r\n"
# 9.4 响应的主体-固定页面
with open(f"D:/dist{request_line_path}", "rb") as file:
response_body = file.read()
# 9.5 响应报文拼接
response_data = response_line.encode() + response_header.encode() + response_blank.encode() + response_body
# 10、发送响应报文
new_client_socket.send(response_data)
# 11、关闭当前连接
new_client_socket.close()
except Exception as e:
print("访问路径不存在!请重试!")
response_line = "HTTP/1.1 404 Not Found\r\n"
# 9.2 响应头
response_header = "Server:Python20WS/2.1\r\n"
# 9.3 响应空行
response_blank = "\r\n"
# 9.4 响应的主体
response_body = f"Error! 页面{request_line_path}不存在!"
# 9.5 响应报文拼接
response_data = response_line.encode() + response_header.encode() + response_blank.encode() + response_body.encode("GBK")
# 10、发送响应报文
new_client_socket.send(response_data)
# 11、关闭当前连接
new_client_socket.close()
else:
print("访问成功!")
def main():
"""主函数段"""
# 创建WebServer类的对象
# 对象.start() 启动Web服务器
ws = WebServer()
ws.start()
if __name__ == '__main__':
main()
# /index2.html
# 访问成功!
# /favicon.ico
# 访问成功!
# /index.html
# 访问成功!
# /
# 访问成功!
# /ddd.html
# 访问路径不存在!请重试!
/index2.html
访问成功!
/favicon.ico
访问成功!
/index.html
访问成功!
/
访问成功!
/ddd.html
访问路径不存在!请重试!
简单的Web服务器-基础框架构建
# WebServer_utils.py
def create_http_response(status, response_body):
# 9、拼接响应的报文
# 9.1 响应行
response_line = "HTTP/1.1 %s\r\n" % status
# 9.2 响应头
response_header = "Server:Python20WS/2.1\r\n"
# 9.3 响应空行
response_blank = "\r\n"
# 9.5 响应报文拼接
response_data = response_line.encode() + response_header.encode() + response_blank.encode() + response_body
return response_data
# WebServer_app.py
from py.WebServer import WebServer_utils
def application(current_dir, request_data):
# 根据客户端浏览器请求的资源路径,返回请求资源
request_line_path = request_data.split("\r\n")[0].split(" ")[1]
print(request_line_path)
# 配置默认访问页面
if request_line_path == "/":
request_line_path = "/index.html"
try:
# 通过with open读取文件
with open(f"{current_dir}{request_line_path}", "rb") as file:
response_body = file.read()
# 调用utils模块的create_http_response函数接接响应信息
status = "200 OK"
response_data = WebServer_utils.create_http_response(status, response_body)
except Exception as e:
print("访问路径不存在!请重试!")
response_body = f"Error! 页面{request_line_path}不存在!"
response_body = response_body.encode("GBK")
status = "404 Not Found"
response_data = WebServer_utils.create_http_response(status, response_body)
return response_data
# 1、导入模块
import socket
from py.WebServer import WebServer_app
# 创建类
class WebServer(object):
# 初始化方法
def __init__(self):
# 2、创建套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3、设置地址重用
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 4、绑定端口
tcp_server_socket.bind(("", 8080))
# 5、设置监听,让套接字由主动变为被动接收
tcp_server_socket.listen(128)
self.tcp_server_socket = tcp_server_socket
def start(self):
# 启动web服务器
# 可循环访问
while True:
# 6、接受客户端连接
new_client_socket, ip_port = self.tcp_server_socket.accept()
# 调用函数
# self.new_client_socket = new_client_socket
self.request_handler(new_client_socket, ip_port)
# 定义方法 request_handler()
def request_handler(self, new_client_socket, ip_port):
# 7、接收客户端浏览器发送的请求协议
request_data = new_client_socket.recv(1024).decode()
# print(request_data)
# 8、判断协议是否为空
if not request_data:
print(f"客户端{str(ip_port)}已经下线!")
new_client_socket.close()
return
# 使用py文件夹app模块的application()函数处理
request_data = WebServer_app.application("D:/dist", request_data)
# 10、发送响应报文
new_client_socket.send(request_data)
# 11、关闭当前连接
new_client_socket.close()
def main():
"""主函数段"""
# 创建WebServer类的对象
# 对象.start() 启动Web服务器
ws = WebServer()
ws.start()
if __name__ == '__main__':
main()
简单的Web服务器-加上命令行参数
#!/usr/bin/python
# WebServer_cli.py
"""
1、导入模块
2、创建套接字
3、设置地址重用
4、绑定端口
5、设置监听,让套接字由主动变为被动接收
6、接受客户端连接 定义函数 request_handler()
7、接收客户端浏览器发送的请求协议
8、判断协议是否为空
9、拼接响应的报文
10、发送响应报文
11、关闭操作
"""
# 1、导入模块
import socket
from py.WebServer import WebServer_app
import sys
# WebServer 简单web服务命令行指定参数启动
# 创建类
class WebServer(object):
# 初始化方法
def __init__(self, port):
# 2、创建套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3、设置地址重用
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 4、绑定端口
tcp_server_socket.bind(("", port))
# 5、设置监听,让套接字由主动变为被动接收
tcp_server_socket.listen(128)
self.tcp_server_socket = tcp_server_socket
def start(self):
# 启动web服务器
print("Web服务器已经启动,等待客户端连接!")
# 可循环访问
while True:
# 6、接受客户端连接
new_client_socket, ip_port = self.tcp_server_socket.accept()
# 调用函数
# self.new_client_socket = new_client_socket
self.request_handler(new_client_socket, ip_port)
# 定义方法 request_handler()
def request_handler(self, new_client_socket, ip_port):
# 7、接收客户端浏览器发送的请求协议
request_data = new_client_socket.recv(1024).decode()
# print(request_data)
# 8、判断协议是否为空
if not request_data:
print(f"客户端{str(ip_port)}已经下线!")
new_client_socket.close()
return
# 使用py文件夹app模块的application()函数处理
response_data = WebServer_app.application("D:/dist", request_data)
# 10、发送响应报文
new_client_socket.send(response_data)
# 11、关闭当前连接
new_client_socket.close()
def main():
"""主函数段"""
"""
命令行指定参数启动
1、导入sys模块
2、获取系统传递到程序的参数
3、判断参数格式是否正确
4、判断端口号是否是一个数字
5、获取端口号
6、在启动的web服务器的时候,使用指定的端口
"""
# 获取系统传递到程序的参数
params_list = sys.argv
print(params_list)
if len(params_list) == 1:
port = 8080
else:
# 判断参数格式是否正确
if len(params_list) != 2:
print(f"指定的参数格式错误!正确格式:python3 xxx.py 端口号")
return
#判断端口号是否是一个数字
if not params_list[1].isdigit():
print("启动失败,端口号必须是一个纯数字")
return
port = int(params_list[1])
# 创建WebServer类的对象
# 对象.start() 启动Web服务器
ws = WebServer(port)
ws.start()
if __name__ == '__main__':
main()
# 指定端口运行: python.exe .\进阶\py\WebServer_cli.py 8000
# 默认端口运行: python.exe .\进阶\py\WebServer_cli.py
简单的Web服务器-网游服务器
#!/usr/bin/python
# WSgame.py
"""
1、导入模块
2、创建套接字
3、设置地址重用
4、绑定端口
5、设置监听,让套接字由主动变为被动接收
6、接受客户端连接 定义函数 request_handler()
7、接收客户端浏览器发送的请求协议
8、判断协议是否为空
9、拼接响应的报文
10、发送响应报文
11、关闭操作
"""
# 1、导入模块
import socket
import WebServer_app
import sys
"""
1、在类的初始化方法中配置当前当前的项目
{"2048":"./2048", "植物大战僵尸v1":"./zwdzjs-v1", ...}
2、给类增加一个初始化项目配置的方法 init_projects()
2.1 显示所有可以发布的游戏 菜单
2.2 接收用户的选择
2.3 根据用户的选择发布指定的项目 (保存用户选择的游戏对应的本地目录)
3、更改Web服务器打开的文件目录
"""
# WebServer 简单web服务命令行指定参数启动
# 创建类
class WebServer(object):
# 初始化方法
def __init__(self, port):
# 2、创建套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3、设置地址重用
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 4、绑定端口
tcp_server_socket.bind(("", port))
# 5、设置监听,让套接字由主动变为被动接收
tcp_server_socket.listen(128)
self.tcp_server_socket = tcp_server_socket
# 定义实例属性,保存要发布的项目路径
self.current_dir = ""
# 定义类的实例属性,projects_dict初始化为空
self.projects_dict = dict()
# key value
self.projects_dict['植物大战僵尸-普通版'] = "zwdzjs-v1"
self.projects_dict['植物大战僵尸-外挂版'] = "zwdzjs-v2"
self.projects_dict['保卫萝卜'] = "tafang"
self.projects_dict['2048'] = "2048"
self.projects_dict['读心术'] = "dxs"
# 调用初始化游戏项目的方法
self.init_projects()
# 2、给类增加一个初始化项目配置的方法 init_projects()
def init_projects(self):
# 2.1 显示所有可以发布的游戏 菜单
# 取出字典的keys 并转换为列表
keys_list = list(self.projects_dict.keys())
# print(keys_list)
# print(list(enumerate(keys_list)))
# 遍历显示所有的keys 使用enumerate()方法将列表组合为一个索引序列
for index, game_name in enumerate(keys_list):
print(f"{index}.{game_name}")
# 2.2 接收用户的选择
sel_no = int(input("请选择要发布的游戏序号:\n"))
# 2.3 根据用户的选择发布指定的项目 (保存用户选择的游戏对应的本地目录)
# 根据用户的选择得到游戏的名称
key = keys_list[sel_no]
# 根据字的key得到游戏项目的路径
self.current_dir = self.projects_dict[key]
# print(key,current_dir)
def start(self):
# 启动web服务器
print("Web服务器已经启动,等待客户端连接!")
# 可循环访问
while True:
# 6、接受客户端连接
new_client_socket, ip_port = self.tcp_server_socket.accept()
# 调用函数
# self.new_client_socket = new_client_socket
self.request_handler(new_client_socket, ip_port)
# 定义方法 request_handler()
def request_handler(self, new_client_socket, ip_port):
# 7、接收客户端浏览器发送的请求协议
request_data = new_client_socket.recv(1024).decode()
# print(request_data)
# 8、判断协议是否为空
if not request_data:
print(f"客户端{str(ip_port)}已经下线!")
new_client_socket.close()
return
# 使用py文件夹app模块的application()函数处理
response_data = WebServer_app.application_game(f"D:/python-study-master/day05/{self.current_dir}", request_data)
# 10、发送响应报文
new_client_socket.send(response_data)
# 11、关闭当前连接
new_client_socket.close()
def main():
"""主函数段"""
"""
命令行指定参数启动
1、导入sys模块
2、获取系统传递到程序的参数
3、判断参数格式是否正确
4、判断端口号是否是一个数字
5、获取端口号
6、在启动的web服务器的时候,使用指定的端口
"""
# 获取系统传递到程序的参数
params_list = sys.argv
print(params_list)
if len(params_list) == 1:
port = 8080
else:
# 判断参数格式是否正确
if len(params_list) != 2:
print(f"指定的参数格式错误!正确格式:python3 xxx.py 端口号")
return
#判断端口号是否是一个数字
if not params_list[1].isdigit():
print("启动失败,端口号必须是一个纯数字")
return
port = int(params_list[1])
# 创建WebServer类的对象
# 对象.start() 启动Web服务器
ws = WebServer(port)
ws.start()
if __name__ == '__main__':
main()
# 指定端口运行: python.exe WSgame.py 8000
# 默认端口运行: python.exe WSgame.py