第一个实现版本
import socket
import os
from urllib import parse
def main():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(("", 8088))
server_socket.listen(5)
while True:
print("waiting for connect")
client_socket, client_address = server_socket.accept()
print("client_socket = " + str(client_socket))
print("client_address = " + str(client_address))
data = client_socket.recv(1024)
print(data.decode("utf-8"))
request_path = data.decode("utf-8").split("\r\n")[0].split(" ")[1]
print("请求的链接地址:" + request_path)
# 修改 / 请求路径为 /index.html
if request_path == "/":
request_path = "/index.html"
print("修改后的链接地址:" + request_path)
# 删除请求路径中后面的 ?传递的参数
flag = request_path.find("?")
if flag != -1:
request_path = request_path[0: flag]
print("修改后的链接地址:" + request_path)
# 将请求路径中存在的 % 编码转换为中文
flag = request_path.find("%")
if flag != -1:
request_path = parse.unquote(request_path)
print("修改后的链接地址:" + request_path)
print()
response_data = "HTTP/1.1 200 ok\r\n\r\n" # 返回状态
try:
with open(os.path.dirname(os.path.dirname(__file__)) + "/Python高级-全部(html版)" + request_path, 'rb') as f:
d = f.read()
response_data = response_data.encode("gbk") + d
except FileNotFoundError as e:
response_data = response_data.encode("gbk") + "文件无法找到".encode("gbk")
client_socket.send(response_data)
client_socket.close()
print("关闭一个" + str(client_address) + "连接")
server_socket.close()
if __name__ == '__main__':
main()
请求路径为:http://127.0.0.1:8088/01day/02_操作系统的发展史(科普章节).html.html?id=100
打印结果:
waiting for connect
client_socket = <socket.socket fd=560, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8088), raddr=('127.0.0.1', 1082)>
client_address = ('127.0.0.1', 1082)
GET /01day/02_%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E7%9A%84%E5%8F%91%E5%B1%95%E5%8F%B2%EF%BC%88%E7%A7%91%E6%99%AE%E7%AB%A0%E8%8A%82%EF%BC%89.html?id=100 HTTP/1.1
Host: 127.0.0.1:8088
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,**;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
请求的链接地址:/01day/02_%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E7%9A%84%E5%8F%91%E5%B1%95%E5%8F%B2%EF%BC%88%E7%A7%91%E6%99%AE%E7%AB%A0%E8%8A%82%EF%BC%89.html?id=100
修改后的链接地址:/01day/02_%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E7%9A%84%E5%8F%91%E5%B1%95%E5%8F%B2%EF%BC%88%E7%A7%91%E6%99%AE%E7%AB%A0%E8%8A%82%EF%BC%89.html
修改后的链接地址:/01day/02_操作系统的发展史(科普章节).html
效果截图,中文路径、Get方式请求是OK的,图片、css等也没有问题
第二个版本,多进程实现http服务器
import socket
import os
from urllib import parse
import multiprocessing
def main():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(("", 8088))
server_socket.listen(5)
while True:
print("waiting for connect")
try:
client_socket, client_address = server_socket.accept()
multiprocessing.Process(target=handle, args=(client_socket, client_address)).start()
# 对于客户端的socket,主进程有一个指向,子进程也有一个指向,
# 仅仅是在子进程中关闭socket并不会真正的关闭,所以需要在主进程中再次关闭socket
# 要不然浏览器会一直在堵塞(谷歌浏览器的小圆圈一直在转)
client_socket.close()
except Exception as e:
server_socket.close()
def handle(client_socket, client_address):
print("client_socket = " + str(client_socket))
print("client_address = " + str(client_address))
data = client_socket.recv(1024)
print(data.decode("utf-8"))
request_path = data.decode("utf-8").split("\r\n")[0].split(" ")[1]
print("请求的链接地址:" + request_path)
# 修改 / 请求路径为 /index.html
if request_path == "/":
request_path = "/index.html"
print("修改后的链接地址:" + request_path)
# 删除请求路径中后面的 ?传递的参数
flag = request_path.find("?")
if flag != -1:
request_path = request_path[0: flag]
print("修改后的链接地址:" + request_path)
# 将请求路径中存在的 % 编码转换为中文
flag = request_path.find("%")
if flag != -1:
request_path = parse.unquote(request_path)
print("修改后的链接地址:" + request_path)
print()
response_data = "HTTP/1.1 200 ok\r\n\r\n" # 返回状态
try:
with open(os.path.dirname(os.path.dirname(__file__)) + "/Python高级-全部(html版)" + request_path, 'rb') as f:
d = f.read()
response_data = response_data.encode("gbk") + d
except FileNotFoundError as e:
response_data = response_data.encode("gbk") + "文件无法找到".encode("gbk")
client_socket.send(response_data)
client_socket.close()
print("关闭一个" + str(client_address) + "连接")
if __name__ == '__main__':
main()
第三个版本,多线程实现http服务器
import socket
import os
from urllib import parse
import threading
def main():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(("", 8088))
server_socket.listen(5)
while True:
print("waiting for connect")
try:
client_socket, client_address = server_socket.accept()
threading.Thread(target=handle, args=(client_socket, client_address)).start()
except Exception as e:
server_socket.close()
def handle(client_socket, client_address):
print("client_socket = " + str(client_socket))
print("client_address = " + str(client_address))
data = client_socket.recv(1024)
print(data.decode("utf-8"))
request_path = data.decode("utf-8").split("\r\n")[0].split(" ")[1]
print("请求的链接地址:" + request_path)
# 修改 / 请求路径为 /index.html
if request_path == "/":
request_path = "/index.html"
print("修改后的链接地址:" + request_path)
# 删除请求路径中后面的 ?传递的参数
flag = request_path.find("?")
if flag != -1:
request_path = request_path[0: flag]
print("修改后的链接地址:" + request_path)
# 将请求路径中存在的 % 编码转换为中文
flag = request_path.find("%")
if flag != -1:
request_path = parse.unquote(request_path)
print("修改后的链接地址:" + request_path)
print()
response_data = "HTTP/1.1 200 ok\r\n\r\n" # 返回状态
try:
with open(os.path.dirname(os.path.dirname(__file__)) + "/Python高级-全部(html版)" + request_path, 'rb') as f:
d = f.read()
response_data = response_data.encode("gbk") + d
except FileNotFoundError as e:
response_data = response_data.encode("gbk") + "文件无法找到".encode("gbk")
client_socket.send(response_data)
client_socket.close()
print("关闭一个" + str(client_address) + "连接")
if __name__ == '__main__':
main()
第四个版本,协程实现http服务器(效率最高)
import socket
import os
from urllib import parse
import gevent
from gevent import monkey
# 必须加上这一句,要不然gevent切换不了
monkey.patch_all()
def main():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(("", 8088))
server_socket.listen(5)
while True:
print("waiting for connect")
try:
client_socket, client_address = server_socket.accept()
gevent.spawn(handle, client_socket, client_address)
except Exception as e:
server_socket.close()
def handle(client_socket, client_address):
print("client_socket = " + str(client_socket))
print("client_address = " + str(client_address))
data = client_socket.recv(1024)
print(data.decode("utf-8"))
request_path = data.decode("utf-8").split("\r\n")[0].split(" ")[1]
print("请求的链接地址:" + request_path)
# 修改 / 请求路径为 /index.html
if request_path == "/":
request_path = "/index.html"
print("修改后的链接地址:" + request_path)
# 删除请求路径中后面的 ?传递的参数
flag = request_path.find("?")
if flag != -1:
request_path = request_path[0: flag]
print("修改后的链接地址:" + request_path)
# 将请求路径中存在的 % 编码转换为中文
flag = request_path.find("%")
if flag != -1:
request_path = parse.unquote(request_path)
print("修改后的链接地址:" + request_path)
print()
response_data = "HTTP/1.1 200 ok\r\n\r\n" # 返回状态
try:
with open(os.path.dirname(os.path.dirname(__file__)) + "/Python高级-全部(html版)" + request_path, 'rb') as f:
d = f.read()
response_data = response_data.encode("gbk") + d
except FileNotFoundError as e:
response_data = response_data.encode("gbk") + "文件无法找到".encode("gbk")
client_socket.send(response_data)
client_socket.close()
print("关闭一个" + str(client_address) + "连接")
if __name__ == '__main__':
main()
自定义WSGI服务器
用的是上面进程实现http服务器代码修改的
模版文件放在/template,/static 放css、js文件,/dynamic 放mini_frame.py文件
实现的整体的效果是可以读取模版文件,并返回给客户端
import multiprocessing
import socket
import sys
from urllib import parse
from dynamic import mini_frame # 这个是自定义模块,代码在下面
class WSGIServer():
def __init__(self, port, app):
self.header = "HTTP/1.1 "
self.app = app
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server_socket.bind(("", int(port)))
self.server_socket.listen(5)
def run_forever(self):
while True:
print("waiting for connect")
try:
client_socket, client_address = self.server_socket.accept()
multiprocessing.Process(target=self.handle, args=(client_socket, client_address)).start()
# 对于客户端的socket,主进程有一个指向,子进程也有一个指向,
# 仅仅是在子进程中关闭socket并不会真正的关闭,所以需要在主进程中再次关闭socket
client_socket.close()
except Exception as e:
self.server_socket.close()
def handle(self, client_socket, client_address):
print("client_socket = " + str(client_socket))
print("client_address = " + str(client_address))
data = client_socket.recv(1024)
print(data.decode("utf-8"))
request_path = data.decode("utf-8").split("\r\n")[0].split(" ")[1]
print("请求的链接地址:" + request_path)
# 修改 / 请求路径为 /index.html
if request_path == "/":
request_path = "/index.html"
print("修改后的链接地址:" + request_path)
# 删除请求路径中后面的 ?传递的参数
flag = request_path.find("?")
if flag != -1:
request_path = request_path[0: flag]
print("修改后的链接地址:" + request_path)
# 将请求路径中存在的 % 编码转换为中文
flag = request_path.find("%")
if flag != -1:
request_path = parse.unquote(request_path)
print("修改后的链接地址:" + request_path)
print()
response_data = ""
if not request_path.endswith(".py"):
response_data = self.header + "200 ok\r\n"
if request_path.endswith(".css"):
response_data += "content-type: text/css" + "\r\n\r\n"
if request_path.endswith(".js"):
response_data += "content-type: application/javascript" + "\r\n\r\n"
try:
with open("./" + request_path, 'r', encoding="utf-8") as f:
d = f.read()
response_data = response_data + d
except FileNotFoundError as e:
response_data += "文件无法找到"
else:
env = dict()
env["request_path"] = request_path
body = mini_frame.application(env, self.start_response)
response_data = self.header + body
client_socket.send(response_data.encode("utf-8"))
client_socket.close()
print("关闭一个" + str(client_address) + "连接")
def start_response(self, status, headers):
self.header = self.header + status
for h in headers:
self.header = self.header +"\r\n" + h[0] + ":" + h[1]
self.header = self.header + "\r\n\r\n"
print(self.header)
def main():
if len(sys.argv) == 3:
port = sys.argv[1]
frame_app_name = sys.argv[2]
print("您输入的端口为:" + port +", 框架为:" + frame_app_name)
else:
print("输入无效,使用默认端口:8088, 默认框架:mini_frame:application")
frame_app_name = "mini_frame:application"
port = 8088
frame_name = frame_app_name.split(":")[0]
app_name = frame_app_name.split(":")[1]
要将模块所在的目录添加到sys路径中,才能找得到模块
sys.path.append("./dynamic")
由文件名动态导入模块
frame = __import__(frame_name)
app = getattr(frame, app_name)
server = WSGIServer(port, app)
server.run_forever()
if __name__ == '__main__':
main()
mini_frame.py 文件
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html;charset=utf-8')])
request_path = environ["request_path"]
if request_path == "/index.py":
body = index()
elif request_path == "/login.py":
body = login()
elif request_path == "/center.py":
body = center()
else:
body = "页面飞到了外星球"
return body
def index():
with open("./templates/index.html", "r", encoding="utf-8") as f:
data = f.read()
return data
def login():
return "登陆页面"
def center():
with open("./templates/center.html", "r", encoding="utf-8") as f:
data = f.read()
return data