一:返回固定页面的静态web服务器:
1:初始化web服务器:创建套接字—> 设置端口复用—>绑定本地端口—>设置监听模式
2:循环接受用户:accpet —>接受用户数据---->recv
3:读取服务器固定页面信息
4:构造响应报文
5:向客户端发送指定页面信息
6:关闭套接字
import socket
def main():
# 1:初始化服务器
tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
tcp_socket.bind(("",7788))
tcp_socket.listen(128)
# 2:循环接收用户
while True:
new_socket, client_msg = tcp_socket.accept()
print("客户端%s发来请求",client_msg)
recv_data = new_socket.recv(1024).decode("utf-8")
print(recv_data)
# 3:读取服务器指定文件
with open("static/index.html","rb") as file:
send_data = file.read()
# 4:构造相应报文
response_line = "HTTP:/1.1 200 ok\r\n"
response_head = "NBNB:ASD\r\n"
response_data = (response_line + response_head+"\r\n").encode("utf-8")+send_data
#5: 发送给客户端
new_socket.send(response_data)
# 6关闭套接字
new_socket.close()
if __name__ == '__main__':
main()
二:返回指定页面的静态web服务器
思路:要想返回指定页面就需要,分析请求报文:请求报文格式:请求行(请求方式 请求地址 HTTP协议版本)
所以我只需要使用空格将字符串分割一下就好了。分割的第二部分就是请求地址。
#得到请求后分割字符串,设置分割两次
request_list = recv_data.split(" ",maxsplit=2)
# 取出资源路径部分
request_addr = request_list[1]
# 3:读取服务器指定文件
#将指定路径加进去
with open("static"+ request_addr,"rb") as file:
send_data = file.read()
import socket
def main():
# 1:初始化服务器
tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
tcp_socket.bind(("",7788))
tcp_socket.listen(128)
# 2:循环接收用户
while True:
new_socket, client_msg = tcp_socket.accept()
print("客户端%s发来请求",client_msg)
recv_data = new_socket.recv(1024).decode("utf-8")
print(recv_data)
#得到请求后分割字符串,设置分割两次
request_list = recv_data.split(" ",maxsplit=2)
# 取出资源路径部分
request_addr = request_list[1]
# 3:读取服务器指定文件
#将指定路径加进去
with open("static"+ request_addr,"rb") as file:
send_data = file.read()
# 4:构造相应报文
response_line = "HTTP:/1.1 200 ok\r\n"
response_head = "NBNB:ASD\r\n"
response_data = (response_line + response_head+"\r\n").encode("utf-8")+send_data
#5: 发送给客户端
new_socket.send(response_data)
# 6关闭套接字
new_socket.close()
if __name__ == '__main__':
main()
三:增加默认页面:
思路:如果我输入127.0.0.1:7788/ 会报错,因为服务器找不到这个路径。所以需要设置默认路径。
解决方式就是判断截取到的信息是不是“/” 如果是替换成"/index.html"
request_addr = request_list[1]
if request_addr == "/":
request_addr = "/index.html"
import socket
def main():
# 1:初始化服务器
tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
tcp_socket.bind(("",7788))
tcp_socket.listen(128)
# 2:循环接收用户
while True:
new_socket, client_msg = tcp_socket.accept()
print("客户端%s发来请求",client_msg)
recv_data = new_socket.recv(1024).decode("utf-8")
print(recv_data)
request_list = recv_data.split(" ",maxsplit=2)
request_addr = request_list[1]
if request_addr == "/":
request_addr = "/index.html"
# 3:读取服务器指定文件
with open("static"+ request_addr,"rb") as file:
send_data = file.read()
# 4:构造相应报文
response_line = "HTTP:/1.1 200 ok\r\n"
response_head = "NBNB:ASD\r\n"
response_data = (response_line + response_head+"\r\n").encode("utf-8")+send_data
#5: 发送给客户端
new_socket.send(response_data)
# 6关闭套接字
new_socket.close()
if __name__ == '__main__':
main()
四:解决找不到报错问题:
问题:如果服务器中没有这个文件,那么在with open("static"+ request_addr,"rb") as file: send_data = file.read()
这个地方就会报错。导致服务器直接挂掉了。
解决办法:给这段代码加try ... except...else...finally
进行捕获,并且让他出错后返回到错误页面。
核心代码:
try:
with open("static"+ request_addr,"rb") as file:
send_data = file.read()
except Exception as e:
# 4:构造相应报文
response_line = "HTTP:/1.1 200 ok\r\n"
response_head = "NBNB:ASD\r\n"
with open("static/error.html", "rb") as file:
send_data = file.read()
response_data = (response_line + response_head+"\r\n").encode("utf-8")+send_data
#5: 发送给客户端
new_socket.send(response_data)
else:
# 4:构造相应报文
response_line = "HTTP:/1.1 404 NOT FOUND\r\n"
response_head = "NBNB:ASD\r\n"
response_data = (response_line + response_head + "\r\n").encode("utf-8") + send_data
# 5: 发送给客户端
new_socket.send(response_data)
finally:
# 6关闭套接字
new_socket.close()
import socket
def main():
# 1:初始化服务器
tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
tcp_socket.bind(("",7788))
tcp_socket.listen(128)
# 2:循环接收用户
while True:
new_socket, client_msg = tcp_socket.accept()
print("客户端%s发来请求",client_msg)
recv_data = new_socket.recv(1024).decode("utf-8")
print(recv_data)
request_list = recv_data.split(" ",maxsplit=2)
request_addr = request_list[1]
if request_addr == "/":
request_addr = "/index.html"
# 3:读取服务器指定文件
try:
with open("static"+ request_addr,"rb") as file:
send_data = file.read()
except Exception as e:
# 4:构造相应报文
response_line = "HTTP:/1.1 200 ok\r\n"
response_head = "NBNB:ASD\r\n"
with open("static/error.html", "rb") as file:
send_data = file.read()
response_data = (response_line + response_head+"\r\n").encode("utf-8")+send_data
#5: 发送给客户端
new_socket.send(response_data)
else:
# 4:构造相应报文
response_line = "HTTP:/1.1 404 NOT FOUND\r\n"
response_head = "NBNB:ASD\r\n"
response_data = (response_line + response_head + "\r\n").encode("utf-8") + send_data
# 5: 发送给客户端
new_socket.send(response_data)
finally:
# 6关闭套接字
new_socket.close()
if __name__ == '__main__':
main()
五:解决客户端异常关闭问题:
问题:如果客户端突然关闭,会导致recv_data = new_socket.recv(1024).decode("utf-8")
这个地方解阻塞,此时服务器会收到来自客户端的空数据,此时request_addr = request_list[1]
这里就会拿不到数据,导致下标越界。
解决方案:
判断是不是空数据,如果是关闭套接字,然后直接退出。
核心代码:
recv_data = new_socket.recv(1024).decode("utf-8")
if len(recv_data) == 0:
new_socket.close()
continue
import socket
def main():
# 1:初始化服务器
tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
tcp_socket.bind(("",7788))
tcp_socket.listen(128)
# 2:循环接收用户
while True:
new_socket, client_msg = tcp_socket.accept()
print("客户端%s发来请求",client_msg)
recv_data = new_socket.recv(1024).decode("utf-8")
if len(recv_data) == 0:
new_socket.close()
continue
print(recv_data)
request_list = recv_data.split(" ",maxsplit=2)
request_addr = request_list[1]
if request_addr == "/":
request_addr = "/index.html"
# 3:读取服务器指定文件
try:
with open("static"+ request_addr,"rb") as file:
send_data = file.read()
except Exception as e:
# 4:构造相应报文
response_line = "HTTP:/1.1 200 ok\r\n"
response_head = "NBNB:ASD\r\n"
with open("static/error.html", "rb") as file:
send_data = file.read()
response_data = (response_line + response_head+"\r\n").encode("utf-8")+send_data
#5: 发送给客户端
new_socket.send(response_data)
else:
# 4:构造相应报文
response_line = "HTTP:/1.1 404 NOT FOUND\r\n"
response_head = "NBNB:ASD\r\n"
response_data = (response_line + response_head + "\r\n").encode("utf-8") + send_data
# 5: 发送给客户端
new_socket.send(response_data)
finally:
# 6关闭套接字
new_socket.close()
if __name__ == '__main__':
main()
六:多任务版本
问题:现在是服务器只有服务完第一个客户端,并且关闭new_socket才能为下一个客户端服务。
解决办法:让服务客户端的另外创建一个线程,单独服务。这样主线程还可以继续让客户端连接。
并且,为了保证服务器断掉,必成功,就要保证子线程守护主线程。
核心代码:
while True:
new_socket, client_msg = tcp_socket.accept()
print("客户端%s发来请求",client_msg)
t1 = threading.Thread(target=service_client, args=(new_socket,))
t1.daemon = True
t1.start()
import socket
import threading
def service_client(new_socket):
recv_data = new_socket.recv(1024).decode("utf-8")
if len(recv_data) == 0:
new_socket.close()
return
print(recv_data)
request_list = recv_data.split(" ", maxsplit=2)
request_addr = request_list[1]
if request_addr == "/":
request_addr = "/index.html"
# 3:读取服务器指定文件
try:
with open("static" + request_addr, "rb") as file:
send_data = file.read()
except Exception as e:
# 4:构造相应报文
response_line = "HTTP:/1.1 200 ok\r\n"
response_head = "NBNB:ASD\r\n"
with open("static/error.html", "rb") as file:
send_data = file.read()
response_data = (response_line + response_head + "\r\n").encode("utf-8") + send_data
# 5: 发送给客户端
new_socket.send(response_data)
else:
# 4:构造相应报文
response_line = "HTTP:/1.1 404 NOT FOUND\r\n"
response_head = "NBNB:ASD\r\n"
response_data = (response_line + response_head + "\r\n").encode("utf-8") + send_data
# 5: 发送给客户端
new_socket.send(response_data)
finally:
# 6关闭套接字
new_socket.close()
def main():
# 1:初始化服务器
tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
tcp_socket.bind(("",7788))
tcp_socket.listen(128)
# 2:循环接收用户
while True:
new_socket, client_msg = tcp_socket.accept()
print("客户端%s发来请求",client_msg)
t1 = threading.Thread(target=service_client, args=(new_socket,))
t1.daemon = True
t1.start()
if __name__ == '__main__':
main()
七:改版成面向对象的格式----搭建静态web服务器
思路:主程序可以定义一个搭建服务器的对象,然后调用它的start() 方法就可以了。
搭建服务器的类:1: init初始化方法 2:start方法 3子线程处理的方法。
import socket
import threading
class Create_Web_Service(object):
def __init__(self):
# 1:初始化服务器
self.tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.tcp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
self.tcp_socket.bind(("", 7788))
self.tcp_socket.listen(128)
def start(self):
while True:
new_socket, client_msg = self.tcp_socket.accept()
print("客户端%s发来请求", client_msg)
t1 = threading.Thread(target=self.handle_client, args=(new_socket,))
t1.daemon = True
t1.start()
@staticmethod
def handle_client(new_socket):
recv_data = new_socket.recv(1024).decode("utf-8")
if len(recv_data) == 0:
new_socket.close()
return
print(recv_data)
request_list = recv_data.split(" ", maxsplit=2)
request_addr = request_list[1]
if request_addr == "/":
request_addr = "/index.html"
# 3:读取服务器指定文件
try:
with open("static" + request_addr, "rb") as file:
send_data = file.read()
except Exception as e:
# 4:构造相应报文
response_line = "HTTP:/1.1 200 ok\r\n"
response_head = "NBNB:ASD\r\n"
with open("static/error.html", "rb") as file:
send_data = file.read()
response_data = (response_line + response_head + "\r\n").encode("utf-8") + send_data
# 5: 发送给客户端
new_socket.send(response_data)
else:
# 4:构造相应报文
response_line = "HTTP:/1.1 404 NOT FOUND\r\n"
response_head = "NBNB:ASD\r\n"
response_data = (response_line + response_head + "\r\n").encode("utf-8") + send_data
# 5: 发送给客户端
new_socket.send(response_data)
finally:
# 6关闭套接字
new_socket.close()
def main():
s = Create_Web_Service()
s.start()
if __name__ == '__main__':
main()
八:静态Web服务器-命令行启动动态绑定端口号:
因为命令行启动需要两个参数,所以要判断输入的参数是不是两位,此外还要判断端口是不是整数,如果都符合则将命令行输入的端口替换原来的7788.
1:获取执行python程序的终端命令行参数
sys.argv:
2:判断参数的类型,设置端口号必须是整型
if not sys.argv[1].isdigit():
print("启动命令如下: python3 xxx.py 9090")
return
port = int(sys.argv[1])
3:给Web服务器类的初始化方法添加一个端口号参数,用于绑定端口号
def __init__(self, port):
self.tcp_server_socket.bind((“”, port))
import socket
import threading
import sys
class Create_Web_Service(object):
def __init__(self, port):
# 1:初始化服务器
self.tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.tcp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
self.tcp_socket.bind(("", port))
self.tcp_socket.listen(128)
def start(self):
while True:
new_socket, client_msg = self.tcp_socket.accept()
print("客户端%s发来请求", client_msg)
t1 = threading.Thread(target=self.handle_client, args=(new_socket,))
t1.daemon = True
t1.start()
@staticmethod
def handle_client(new_socket):
recv_data = new_socket.recv(1024).decode("utf-8")
if len(recv_data) == 0:
new_socket.close()
return
print(recv_data)
request_list = recv_data.split(" ", maxsplit=2)
request_addr = request_list[1]
if request_addr == "/":
request_addr = "/index.html"
# 3:读取服务器指定文件
try:
with open("static" + request_addr, "rb") as file:
send_data = file.read()
except Exception as e:
# 4:构造相应报文
print(e)
response_line = "HTTP:/1.1 200 ok\r\n"
response_head = "NBNB:ASD\r\n"
with open("static/error.html", "rb") as file:
send_data = file.read()
response_data = (response_line + response_head + "\r\n").encode("utf-8") + send_data
# 5: 发送给客户端
new_socket.send(response_data)
else:
# 4:构造相应报文
response_line = "HTTP:/1.1 404 NOT FOUND\r\n"
response_head = "NBNB:ASD\r\n"
response_data = (response_line + response_head + "\r\n").encode("utf-8") + send_data
# 5: 发送给客户端
new_socket.send(response_data)
finally:
# 6关闭套接字
new_socket.close()
def main():
print(sys.argv)
if len(sys.argv) != 2:
print("请输入输入两个单词:一个地址,一个端口.")
return
if not sys.argv[1].isdigit():
print("请输入输入两个单词:一个地址,一个端口.")
return
port = int(sys.argv[1])
s = Create_Web_Service(port)
s.start()
if __name__ == '__main__':
main()