python 简单的web静态服务器
原理步骤:

web静态服务器:显示固定页面
准备名为web_server的py文件
#coding=utf-8
import socket
def handle_client(client_socket):
"为一个客户端进行服务"
# 1. 返回http格式的数据response给浏览器,分别准备header和body
recv_data = client_socket.recv(1024).decode("utf-8")
print(recv_data)
# 2. 返回http格式的数据response给浏览器,分别准备header和body
response_headers = "HTTP/1.1 200 OK\r\n" # 200表示找到这个资源
response_headers += "\r\n" # 用一个空的行与body进行隔开
response_body = "hello xinglu"
response = response_headers + response_body
# 3. 发送组织好的数据并关闭套接字
client_socket.send(response.encode("utf-8"))
client_socket.close()
def main():
"""作为程序的主控制入口"""
# 1. 创建套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 设置套接字4次挥手关闭连接后,不用等待TIME_WAIT,端口立刻重用
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 3. 绑定ip端口
tcp_server_socket.bind(("", 7788))
# 4. 变为监听套接字
tcp_server_socket.listen(128)
while True:
# 5. 等待客户端的链接
client_socket, client_addr = tcp_server_socket.accept()
# 6.为客户端服务
handle_client(client_socket)
if __name__ == "__main__":
main()
然后运行py文件,用浏览器访问ip+端口即可(代码中绑定的端口为7788)
web静态服务器:显示需要的页面
准备名为web_server的py文件
import socket
import re
def handle_client(client_socket):
"""为一个客户端进行服务"""
# 1.接收浏览器发送过来的请求,即http请求 GET / HTTP/1.1
recv_data = client_socket.recv(1024).decode("utf-8")
request_header_lines = recv_data.splitlines()
for line in request_header_lines:
print(line)
# 2, 获取浏览器需要访问的页面
http_request_line = request_header_lines[0]
file_name = re.match("[^/]+(/[^ ]*)", http_request_line).group(1)
print("client request file is: %s" % file_name)
# 3. 如果没有指定需要访问的页面,那么返回默认页面index.html
if file_name == "/":
file_name = DOCUMENTS_ROOT + "/index.html"
else:
file_name = DOCUMENTS_ROOT + file_name
print("the path of the file is: %s" % file_name)
print("-"*50)
# 4. 打开对应页面,返回http格式的数据response给浏览器,分别准备header和body
response_header = None
response_body = None
try:
f = open(file_name, "rb")
except IOError:
# 读取页面失败,即没有这个页面,返回404
response_header = "HTTP/1.1 404 not found\r\n"
response_header += "\r\n"
response_body = "ERROR 404: file not found"
else:
response_header = "HTTP/1.1 200 OK\r\n" # 200表示找到这个资源
response_header += "\r\n" # 用一个空的行与body隔开
response_body = f.read()
f.close()
finally:
# 因为头信息在组织的时候,是按照字符串组织的,需要进行编码
response = response_header.encode("utf-8") + response_body
client_socket.send(response)
client_socket.close()
def main():
"""作为程序的主控制入口"""
# 1. 创建套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 设置套接字4次挥手关闭连接后,不用等待TIME_WAIT,端口立刻重用
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 3. 绑定ip端口
tcp_server_socket.bind(("", 7788))
# 4. 变为监听套接字
tcp_server_socket.listen(128)
while True:
# 5. 等待客户端的链接
client_socket, client_addr = tcp_server_socket.accept()
# 6.为客户端服务
handle_client(client_socket)
DOCUMENTS_ROOT = "./html"
if __name__ == "__main__":
main()
再准备如下名为index.html的页面,路径为py文件同级目录的html文件夹下
.
├── web_server.py
├── html
└───── index.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>我眼中的世界</title>
<script src="http://www.jq22.com/jquery/jquery-1.10.2.js"></script>
<style>
*, *:before, *:after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background: #15181A;
font-family: "Open Sans", Helvetica, Arial, sans-serif;
}
.skw-pages {
overflow: hidden;
position: relative;
height: 100vh;
}
.skw-page {
position: absolute;
left: 0;
top: 0;
width: 100%;
}
.skw-page__half {
position: absolute;
top: 0;
width: 50%;
height: 100vh;
-webkit-transition: -webkit-transform 1s;
transition: transform 1s;
}
.skw-page__half--left {
left: 0;
-webkit-transform: translate3d(-32.4vh, 100%, 0);
transform: translate3d(-32.4vh, 100%, 0);
}
.skw-page__half--right {
left: 50%;
-webkit-transform: translate3d(32.4vh, -100%, 0);
transform: translate3d(32.4vh, -100%, 0);
}
.skw-page.active .skw-page__half {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
.skw-page__skewed {
overflow: hidden;
position: absolute;
top: 0;
width: 140%;
height: 100%;
-webkit-transform: skewX(-18deg);
-ms-transform: skewX(-18deg);
transform: skewX(-18deg);
background: #000;
}
.skw-page__half--left .skw-page__skewed {
left: -40%;
}
.skw-page__half--right .skw-page__skewed {
right: -40%;
}
.skw-page__content {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-webkit-align-items: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-flex-flow: column wrap;
-ms-flex-flow: column wrap;
flex-flow: column wrap;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
padding: 0 30%;
color: #fff;
-webkit-transform: skewX(18deg);
-ms-transform: skewX(18deg);
transform: skewX(18deg);
-webkit-transition: -webkit-transform 1s, opacity 1s;
transition: transform 1s, opacity 1s;
background-size: cover;
}
.skw-page__half--left .skw-page__content {
padding-left: 30%;
padding-right: 30%;
-webkit-transform-origin: 100% 0;
-ms-transform-origin: 100% 0;
transform-origin: 100% 0;
}
.skw-page__half--right .skw-page__content {
padding-left: 30%;
padding-right: 30%;
-webkit-transform-origin: 0 100%;
-ms-transform-origin: 0 100%;
transform-origin: 0 100%;
}
.skw-page.inactive .skw-page__content {
opacity: 0.5;
-webkit-transform: skewX(18deg) scale(0.95);
-ms-transform: skewX(18deg) scale(0.95);
transform: skewX(18deg) scale(0.95);
}
.skw-page__heading {
margin-bottom: 15px;
text-transform: uppercase;
font-size: 25px;
text-align: center;
}
.skw-page__description {
font-size: 18px;
text-align: center;
}
.skw-page__link {
color: #FFA0A0;
}
.skw-page-1 .skw-page__half--left .skw-page__content {
background-image: url("https://i.loli.net/2019/12/15/24vOJfGh7wySXcC.jpg");
}
.skw-page-1 .skw-page__half--right .skw-page__content {
background: #292929;
}
.skw-page-2 .skw-page__half--left .skw-page__content {
background: #292929;
}
.skw-page-2 .skw-page__half--right .skw-page__content {
background-image: url("https://i.loli.net/2019/12/15/aizHDLwV2JOo94P.jpg");
}
.skw-page-3 .skw-page__half--left .skw-page__content {
background-image: url("https://i.loli.net/2019/12/15/yKu3NGwCBYqbUrh.jpg");
}
.skw-page-3 .skw-page__half--right .skw-page__content {
background: #292929;
}
.skw-page-4 .skw-page__half--left .skw-page__content {
background: #292929;
}
.skw-page-4 .skw-page__half--right .skw-page__content {
background-image: url("https://i.loli.net/2019/12/15/A3UdjqcGDMTFtnK.jpg");
}
.skw-page-5 .skw-page__half--left .skw-page__content {
background-image: url("https://i.loli.net/2019/12/15/A1s6zc29DbNO8Ho.jpg");
}
.skw-page-5 .skw-page__half--right .skw-page__content {
background: #292929;
}
.skw-page-6 .skw-page__half--left .skw-page__content {
background: #292929;
}
.skw-page-6 .skw-page__half--right .skw-page__content {
background-image: url("https://i.loli.net/2019/12/15/8mYk1NHjefGMWzs.jpg");
}
.skw-page-7 .skw-page__half--left .skw-page__content {
background-image: url("https://i.loli.net/2019/12/15/uYb41PdfwyDpnMl.jpg");
}
.skw-page-7 .skw-page__half--right .skw-page__content {
background: #292929;
}
</style>
</head>
<body>
<div class="skw-pages">
<div class="skw-page skw-page-1 active">
<div class="skw-page__half skw-page__half--left">
<div class="skw-page__skewed">
<div class="skw-page__content"></div>
</div>
</div>
<div class="skw-page__half skw-page__half--right">
<div class="skw-page__skewed">
<div class="skw-page__content">
<h2 class="skw-page__heading">这是我眼中的世界</h2>
<p class="skw-page__description">为了方便观看,请把窗口宽度缩小一半,然后按 ⬇ 键</p>
</div>
</div>
</div>
</div>
<div class="skw-page skw-page-2">
<div class="skw-page__half skw-page__half--left">
<div class="skw-page__skewed">
<div class="skw-page__content">
<h2 class="skw-page__heading">你在路上随便碰到的一个路人</h2>
<p class="skw-page__description">都是别人做梦都想见到的人 ⬇ </p>
</div>
</div>
</div>
<div class="skw-page__half skw-page__half--right">
<div class="skw-page__skewed">
<div class="skw-page__content"></div>
</div>
</div>
</div>
<div class="skw-page skw-page-3">
<div class="skw-page__half skw-page__half--left">
<div class="skw-page__skewed">
<div class="skw-page__content"></div>
</div>
</div>
<div class="skw-page__half skw-page__half--right">
<div class="skw-page__skewed">
<div class="skw-page__content">
<h2 class="skw-page__heading">你的眼中 有数不尽的星光</h2>
<p class="skw-page__description">嘴角上扬 星光便散落心上 ⬇</p>
</div>
</div>
</div>
</div>
<div class="skw-page skw-page-4">
<div class="skw-page__half skw-page__half--left">
<div class="skw-page__skewed">
<div class="skw-page__content">
<h2 class="skw-page__heading">山野千里</h2>
<p class="skw-page__description">想见你 ⬇</p>
</div>
</div>
</div>
<div class="skw-page__half skw-page__half--right">
<div class="skw-page__skewed">
<div class="skw-page__content"></div>
</div>
</div>
</div>
<div class="skw-page skw-page-5">
<div class="skw-page__half skw-page__half--left">
<div class="skw-page__skewed">
<div class="skw-page__content"></div>
</div>
</div>
<div class="skw-page__half skw-page__half--right">
<div class="skw-page__skewed">
<div class="skw-page__content">
<h2 class="skw-page__heading">见到你的每一刻</h2>
<p class="skw-page__description">就像每周五的下午 ⬇</p>
</div>
</div>
</div>
</div>
<div class="skw-page skw-page-6">
<div class="skw-page__half skw-page__half--left">
<div class="skw-page__skewed">
<div class="skw-page__content">
<h2 class="skw-page__heading">从遇见你开始</h2>
<p class="skw-page__description">凛冬散尽 星河长明 ⬇</p>
</div>
</div>
</div>
<div class="skw-page__half skw-page__half--right">
<div class="skw-page__skewed">
<div class="skw-page__content"></div>
</div>
</div>
</div>
<div class="skw-page skw-page-7">
<div class="skw-page__half skw-page__half--right">
<div class="skw-page__skewed">
<div class="skw-page__content">
<h2 class="skw-page__heading">我希望与你的一生</h2>
<p class="skw-page__description">所求皆如愿 所行化坦途 多喜乐 长安宁 ⬇</p>
</div>
</div>
</div>
<div class="skw-page__half skw-page__half--left">
<div class="skw-page__skewed">
<div class="skw-page__content"></div>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function() {
var curPage = 1;
var numOfPages = $(".skw-page").length;
var animTime = 1000;
var scrolling = false;
var pgPrefix = ".skw-page-";
function pagination() {
scrolling = true;
$(pgPrefix + curPage).removeClass("inactive").addClass("active");
$(pgPrefix + (curPage - 1)).addClass("inactive");
$(pgPrefix + (curPage + 1)).removeClass("active");
setTimeout(function() {
scrolling = false;
}, animTime);
};
function navigateUp() {
if (curPage === 1) return;
curPage--;
pagination();
};
function navigateDown() {
if (curPage === numOfPages) return;
curPage++;
pagination();
};
$(document).on("mousewheel DOMMouseScroll", function(e) {
if (scrolling) return;
if (e.originalEvent.wheelDelta > 0 || e.originalEvent.detail < 0) {
navigateUp();
} else {
navigateDown();
}
});
$(document).on("keydown", function(e) {
if (scrolling) return;
if (e.which === 38) {
navigateUp();
} else if (e.which === 40) {
navigateDown();
}
});
});</script>
</body>
</html>
然后运行py文件,用浏览器访问ip+端口即可(代码中绑定的端口为7788)
将python代码修改为面向对象的形式
import socket
import re
class WSGIServer(object):
"""web静态服务器:返回需要的页面"""
def __init__(self, server_addr):
"""初始化方法:创建并配置服务器套接字"""
# 1. 创建套接字
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 设置套接字4次挥手关闭连接后,不用等待TIME_WAIT,端口立刻重用
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 3. 绑定ip端口
self.server_socket.bind(server_addr)
# 4. 变为监听套接字
self.server_socket.listen(128)
def accept_client(self):
"""实例方法:循环等待服务器接收客户端的连接"""
while True:
# 5. 等待客户端的连接
client_socket, client_addr = self.server_socket.accept()
# 6.为客户端服务
self.handle_client(client_socket)
@staticmethod
def handle_client(client_socket):
"""静态方法:为一个客户端进行服务"""
# 1.接收浏览器发送过来的请求,即http请求 GET / HTTP/1.1
recv_data = client_socket.recv(1024).decode("utf-8")
request_header_lines = recv_data.splitlines()
for line in request_header_lines:
print(line)
# 2, 获取浏览器需要访问的页面
http_request_line = request_header_lines[0]
file_name = re.match("[^/]+(/[^ ]*)", http_request_line).group(1)
print("client request file is: %s" % file_name)
# 3. 如果没有指定需要访问的页面,那么返回默认页面index.html
if file_name == "/":
file_name = DOCUMENTS_ROOT + "/index.html"
else:
file_name = DOCUMENTS_ROOT + file_name
print("the path of the file is: %s" % file_name)
print("-" * 50)
# 4. 打开对应页面,返回http格式的数据response给浏览器,分别准备header和body
response_header = None
response_body = None
try:
f = open(file_name, "rb")
except IOError:
# 读取页面失败,即没有这个页面,返回404
response_header = "HTTP/1.1 404 not found\r\n"
response_header += "\r\n"
response_body = "ERROR 404: file not found"
else:
response_header = "HTTP/1.1 200 OK\r\n" # 200表示找到这个资源
response_header += "\r\n" # 用一个空的行与body隔开
response_body = f.read()
f.close()
finally:
# 因为头信息在组织的时候,是按照字符串组织的,需要进行编码
response = response_header.encode("utf-8") + response_body
client_socket.send(response)
client_socket.close()
def main():
"""作为程序的主控制入口"""
httpd = WSGIServer(SERVER_ADDR)
print("web Server: Serving HTTP on port %d ...\n" % PORT)
httpd.accept_client()
DOCUMENTS_ROOT = "./html"
SERVER_ADDR = (HOST, PORT) = "", 7788
if __name__ == "__main__":
main()
web静态服务器:单线程非阻塞
import socket
import re
import time
class WSGIServer(object):
"""web静态服务器:返回需要的页面"""
def __init__(self, server_addr):
"""初始化方法:创建并配置服务器套接字"""
# 1. 创建套接字
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 设置套接字4次挥手关闭连接后,不用等待TIME_WAIT,端口立刻重用
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 3. 绑定ip端口
self.server_socket.bind(server_addr)
# 4. 变为监听套接字
self.server_socket.listen(128)
self.server_socket.setblocking(False)
self.client_socket_list = list()
def accept_client(self):
"""实例方法:循环等待服务器接收客户端的连接"""
while True:
time.sleep(0.5) # for test
# 5. 等待客户端的连接
try:
client_socket, client_addr = self.server_socket.accept()
except Exception as ret:
print("客户端连接的Exception:", ret)
else:
client_socket.setblocking(False)
self.client_socket_list.append(client_socket)
for client_socket in self.client_socket_list:
try:
recv_data = client_socket.recv(1024).decode("utf-8")
except Exception as ret:
print("客户端发送消息的Exception", ret)
else:
if recv_data:
# 6.为客户端服务
self.handle_client(recv_data, client_socket)
else:
client_socket.close()
self.client_socket_list.remove(client_socket)
@staticmethod
def handle_client(recv_data, client_socket):
"""静态方法:为一个客户端进行服务"""
# 1.接收浏览器发送过来的请求,即http请求 GET / HTTP/1.1
request_header_lines = recv_data.splitlines()
for line in request_header_lines:
print(line)
# 2, 获取浏览器需要访问的页面
http_request_line = request_header_lines[0]
file_name = re.match("[^/]+(/[^ ]*)", http_request_line).group(1)
print("client request file is: %s" % file_name)
# 3. 如果没有指定需要访问的页面,那么返回默认页面index.html
if file_name == "/":
file_name = DOCUMENTS_ROOT + "/index.html"
else:
file_name = DOCUMENTS_ROOT + file_name
print("the path of the file is: %s" % file_name)
print("-" * 50)
# 4. 打开对应页面,返回http格式的数据response给浏览器,分别准备header和body
response_header = None
response_body = None
try:
f = open(file_name, "rb")
except IOError:
# 读取页面失败,即没有这个页面,返回404
response_header = "HTTP/1.1 404 not found\r\n"
response_header += "\r\n"
response_body = "ERROR 404: file not found"
else:
response_header = "HTTP/1.1 200 OK\r\n" # 200表示找到这个资源
response_header += "\r\n" # 用一个空的行与body隔开
response_body = f.read()
f.close()
finally:
# 因为头信息在组织的时候,是按照字符串组织的,需要进行编码
response = response_header.encode("utf-8") + response_body
client_socket.send(response)
client_socket.close()
def main():
"""作为程序的主控制入口"""
httpd = WSGIServer(SERVER_ADDR)
print("web Server: Serving HTTP on port %d ...\n" % PORT)
httpd.accept_client()
DOCUMENTS_ROOT = "./html"
SERVER_ADDR = (HOST, PORT) = "", 7788
if __name__ == "__main__":
main()
web静态服务器:多进程、多线程、协程
多线程,变更实例方法代码:
import multiprocessing
class WSGIServer(object):
def accept_client(self):
"""实例方法:循环等待服务器接收客户端的连接"""
while True:
# 5. 等待客户端的连接
client_socket, client_addr = self.server_socket.accept()
# 6.使用多进程为客户端服务
new_process = multiprocessing.Process(target=self.handle_client, args=(client_socket, ))
new_process.start()
# 因为子进程已经复制了父进程的套接字等资源,所以父进程调用close不会将他们对应的这个链接关闭,如果不调用close会陷入等待
client_socket.close()
多线程, 变更实例方法代码:
import threading
class WSGIServer(object):
def accept_client(self):
"""实例方法:循环等待服务器接收客户端的连接"""
while True:
# 5. 等待客户端的连接
client_socket, client_addr = self.server_socket.accept()
# 6.使用多线程为客户端服务
new_thread = threading.Thread(target=self.handle_client, args=(client_socket, ))
new_thread.start()
# 如果使用线程则不能关闭套接字,因为线程不会复制进程的资源
# client_socket.close()
协程,变更实例方法代码:
import gevent
from gevent import monkey
monkey.patch_all()
class WSGIServer(object):
def accept_client(self):
"""实例方法:循环等待服务器接收客户端的连接"""
while True:
# 5. 等待客户端的连接
client_socket, client_addr = self.server_socket.accept()
# 6.使用协程为客户端服务
gevent.spawn(self.handle_client, client_socket)
长连接、短连接
在HTTP/1.0中,默认使用的是短连接。
也就是说,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。
如果客户端访问的某个HTML或其他类型的Web资源,如JavaScript文件、图像文件、CSS文件等。
当浏览器每遇到这样一个Web资源,就会建立一个HTTP会话。
但从HTTP/1.1起,默认使用长连接,用以保持连接特性。
在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的 TCP连接不会关闭。
如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。
Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。
短连接对于服务器来说管理较为简单,存在的连接都是有用的连接,不需要额外的控制手段。
但如果客户请求频繁,将在TCP的建立和关闭操作上浪费时间和带宽。
长连接和短连接的产生在于client和server采取的关闭策略,具体的应用场景采用具体的策略,没有十全十美的选择,只有合适的选择。
上述代码都属于短连接,因为每次调用handle_client静态方法都会执行client_socket.close()关闭套接字从而结束会话。因此想要改为长连接只需要注释掉client_socket.close() 并且再头文件组织里加入Content-Length来标记body的长度,告诉服务器发完了本次数据,但不需要关闭连接。
# 将以上代码的header组织部分替换如下,然后注释掉client_socket.close()
# 4. 打开对应页面,返回http格式的数据response给浏览器,分别准备header和body
response_header = None
response_body = None
try:
f = open(file_name, "rb")
except IOError:
# 读取页面失败,即没有这个页面,返回404
response_body = "ERROR 404: file not found"
response_header = "HTTP/1.1 404 not found\r\n"
response_header += "Content-Length:%d\r\n" % len(response_body) # 用一个空的行与body隔开
response_header += "\r\n"
else:
response_body = f.read()
f.close()
response_header = "HTTP/1.1 200 OK\r\n" # 200表示找到这个资源
response_header += "Content-Length: %d\r\n" % len(response_body) # 用一个空的行与body隔开
response_header += "\r\n" # 用一个空的行与body隔开
finally:
# 因为头信息在组织的时候,是按照字符串组织的,需要进行编码
response = response_header.encode("utf-8") + response_body
client_socket.send(response)
# client_socket.close()
epoll模式
import socket
import re
import select
class WSGIServer(object):
"""web静态服务器:返回需要的页面"""
def __init__(self, server_addr):
"""初始化方法:创建并配置服务器套接字"""
# 1. 创建套接字
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 设置套接字4次挥手关闭连接后,不用等待TIME_WAIT,端口立刻重用
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 3. 绑定ip端口
self.server_socket.bind(server_addr)
# 4. 变为监听套接字
self.server_socket.listen(128)
# 5. 创建一个epoll对象
self.epoll = select.epoll()
# 6. 将tcp服务器套接字的文件描述符fd(fileno)加入到epoll中进行监听
self.epoll.register(self.server_socket.fileno(), select.EPOLLIN|select.EPOLLET)
# 7. 创建添加的fd对应的套接字的字典
self.fd_socket = dict()
def accept_client(self):
"""实例方法:循环等待服务器接收客户端的连接"""
while True:
# 7. epoll 进行 fd 扫描的地方 -- 未指定超时时间则为阻塞等待
epoll_list = self.epoll.poll()
# 对事件进行判断
for fd, event in epoll_list:
# 如果是服务器套接字可以收数据, 那么意味着可以进行accept
if fd == self.server_socket.fileno():
client_socket, client_addr = self.server_socket.accept()
# 向着epoll中注册 连接 socket 的可读事件
self.epoll.register(client_socket.fileno(), select.EPOLLIN|select.EPOLLET)
# 记录这个信息
self.fd_socket[client_socket.fileno()] = client_socket
# 接收到数据
elif event == select.EPOLLIN:
recv_data = self.fd_socket[fd].recv(1024).decode("utf-8")
if recv_data:
self.handle_client(recv_data, self.fd_socket[fd])
else:
# 在epoll中注销客户端的信息
self.epoll.unregister(fd)
# 关闭客户端的文件句柄
self.fd_socket[fd].close()
# 在字典中删除已关闭客户端相关的信息
del self.fd_socket[fd]
@staticmethod
def handle_client(recv_data, client_socket):
"""静态方法:为一个客户端进行服务"""
# 1.接收浏览器发送过来的请求,即http请求 GET / HTTP/1.1
request_header_lines = recv_data.splitlines()
for line in request_header_lines:
print(line)
# 2, 获取浏览器需要访问的页面
http_request_line = request_header_lines[0]
file_name = re.match("[^/]+(/[^ ]*)", http_request_line).group(1)
print("client request file is: %s" % file_name)
# 3. 如果没有指定需要访问的页面,那么返回默认页面index.html
if file_name == "/":
file_name = DOCUMENTS_ROOT + "/index.html"
else:
file_name = DOCUMENTS_ROOT + file_name
print("the path of the file is: %s" % file_name)
print("-" * 50)
# 4. 打开对应页面,返回http格式的数据response给浏览器,分别准备header和body
response_header = None
response_body = None
try:
f = open(file_name, "rb")
except IOError:
# 读取页面失败,即没有这个页面,返回404
response_body = "ERROR 404: file not found"
response_header = "HTTP/1.1 404 not found\r\n"
response_header += "Content-Length: %d\r\n" % len(response_body) # 用一个空的行与body隔开
response_header += "\r\n"
else:
response_body = f.read()
f.close()
response_header = "HTTP/1.1 200 OK\r\n" # 200表示找到这个资源
response_header += "Content-Length: %d\r\n" % len(response_body) # 用一个空的行与body隔开
response_header += "\r\n" # 用一个空的行与body隔开
finally:
response = response_header.encode("utf-8") + response_body
client_socket.send(response)
# client_socket.close()
def main():
"""作为程序的主控制入口"""
httpd = WSGIServer(SERVER_ADDR)
print("web Server: Serving HTTP on port %d ...\n" % PORT)
httpd.accept_client()
DOCUMENTS_ROOT = "./html"
SERVER_ADDR = (HOST, PORT) = "", 7788
if __name__ == "__main__":
main()
网友评论