普通写法,静态web服务器:
- 先创建TCP服务器套接字,然后等待客户端(这里是浏览器)请求连接。
- 客户端发起请求,用线程来处理连接的建立,这样可以实现多任务(也就是并发)
- 连接后根据请求发送指定页面
补充:设置守护主线程:当主线程退出,子线程立马全死
1 import socket 2 import os 3 import threading 4 5 # 处理客户端的请求 6 def handle_client_request(new_socket): 7 # 代码执行到此,说明连接建立成功 8 # 连接客户端的请求信息 9 recv_data = new_socket.recv(4096) 10 # 判断接收到的数据长度是否为0 11 if len(recv_data) == 0: # 用来解决客户端一连接服务器就关闭的现象 12 new_socket.close() 13 return 14 15 # 对二进制数据进行解码 16 recv_content = recv_data.decode("UTF-8") 17 print(recv_data) 18 19 # 对数据按照空格进行分割(maxsplit=2表示分割两次,得到3个。其格式为元组) 20 request_list = recv_content.split(" ", maxsplit=2) 21 # 获取请求的资源路径 22 request_path = request_list[1] 23 print(request_path) 24 25 # 判断请求的是否是根目录,如果是根目录设置返回的信息 26 if request_path == "/": 27 request_path = "/index.html" 28 29 # 判断该路径的文件是否存在,有两种方法 30 # 方法一:os.path.exists 31 # os.path.exists("static/" + request_path) 32 # 方法二:try-except异常抛出的4件套 33 34 try: 35 # 打开文件读取文件中的数据,提示:这里使用rb模式,兼容打开图片文件(用二进制的方式) 36 with open("static" + request_path, "rb") as file: # 这里的file表示打开文件的对象 37 file_data = file.read() 38 # 提示:with open 关闭文件这步操作不用程序来完成,系统帮我们完成 39 40 except Exception as e: 41 # 代码执行到此,说明没有请求的该文件,返回404状态信息 42 # 响应行 43 response_line = "HTTP/1.1 404 Not Found " 44 # 响应头 45 response_header = "Server:PWS/1.0 " 46 # 空行 47 48 # 读取404页面数据 49 with open("static/error.html", "rb") as file: 50 file_data = file.read() 51 # 响应体 52 response_body = file_data 53 54 # 把数据封装成http,响应报文格式的数据 55 response = (response_line + 56 response_header + 57 " ").encode("UTF-8") + response_body 58 59 # 发送给浏览器的响应报文数据 60 new_socket.send(response) 61 62 else: 63 # 代码执行到此,说明文件存在,返回200状态信息 64 # 响应行 65 response_line = "HTTP/1.1 200 OK " 66 # 响应头 67 response_header = "Server: pws/1.0 " 68 # 空行 69 # 响应体 70 response_body = file_data 71 72 # 把数据封装成http,响应报文格式的数据 73 response = (response_line + 74 response_header + 75 " ").encode("UTF-8") + response_body # 响应行/头是字符串,响应体是二进制。 76 # 这里把响应行/头也改成二进制,再连接起来 77 # 发送给浏览器的响应报文数据 78 new_socket.send(response) 79 80 finally: 81 # 关闭服务于客户端的套接字 82 new_socket.close() 83 84 85 def main(): 86 # 创建TCP服务器套接字 87 tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 88 # 设置端口号复用,程序退出端口号立即释放 89 tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) 90 # 绑定端口号 91 tcp_server_socket.bind(("", 8000)) 92 # 设置监听 93 tcp_server_socket.listen(128) 94 # 循环等待接受客户端的连接请求 95 while True: 96 # 等待接受客户端的连接请求 97 new_socket, ip_port = tcp_server_socket.accept() 98 # 当客户端和服务器建立连接,创建子线程 99 sub_thread = threading.Thread(target=handle_client_request, args=(new_socket,)) 100 # 设置守护主线程 101 sub_thread.setDaemon(True) 102 # 启动子线程执行的对应任务 103 sub_thread.start() 104 105 106 if __name__ == '__main__': 107 main()
面向对象的写法:
- 用类进行封装,
- TCP服务器套接字直接写在__init__里面,这样默认就会创建
1 import socket 2 import os 3 import threading 4 5 6 # Http协议的web服务器类 7 class HttpWebServer(object): 8 def __init__(self): 9 # 创建TCP服务器套接字 10 tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 11 # 设置端口号复用,程序退出端口号立即释放 12 tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) 13 # 绑定端口号 14 tcp_server_socket.bind(("", 8000)) 15 # 设置监听 16 tcp_server_socket.listen(128) 17 # 把TCP服务器的套接字做为web服务器对象的属性 18 self.tcp_server_socket = tcp_server_socket 19 20 # 处理客户端请求 21 @staticmethod 22 def handle_client_request(new_socket): 23 # 代码执行到此,说明连接建立成功 24 # 连接客户端的请求信息 25 recv_data = new_socket.recv(4096) 26 # 判断接收到的数据长度是否为0 27 if len(recv_data) == 0: # 用来解决客户端一连接服务器就关闭的现象 28 new_socket.close() 29 return 30 31 # 对二进制数据进行解码 32 recv_content = recv_data.decode("UTF-8") 33 print(recv_data) 34 35 # 对数据按照空格进行分割(maxsplit=2表示分割两次,得到3个。其格式为元组) 36 request_list = recv_content.split(" ", maxsplit=2) 37 # 获取请求的资源路径 38 request_path = request_list[1] 39 print(request_path) 40 41 # 判断请求的是否是根目录,如果是根目录设置返回的信息 42 if request_path == "/": 43 request_path = "/index.html" 44 45 # 判断该路径的文件是否存在,有两种方法 46 # 方法一:os.path.exists 47 # os.path.exists("static/" + request_path) 48 # 方法二:try-except异常抛出的4件套 49 50 try: 51 # 打开文件读取文件中的数据,提示:这里使用rb模式,兼容打开图片文件(用二进制的方式) 52 with open("static" + request_path, "rb") as file: # 这里的file表示打开文件的对象 53 file_data = file.read() 54 # 提示:with open 关闭文件这步操作不用程序来完成,系统帮我们完成 55 56 except Exception as e: 57 # 代码执行到此,说明没有请求的该文件,返回404状态信息 58 # 响应行 59 response_line = "HTTP/1.1 404 Not Found " 60 # 响应头 61 response_header = "Server:PWS/1.0 " 62 # 空行 63 64 # 读取404页面数据 65 with open("static/error.html", "rb") as file: 66 file_data = file.read() 67 # 响应体 68 response_body = file_data 69 70 # 把数据封装成http,响应报文格式的数据 71 response = (response_line + 72 response_header + 73 " ").encode("UTF-8") + response_body 74 75 # 发送给浏览器的响应报文数据 76 new_socket.send(response) 77 78 else: 79 # 代码执行到此,说明文件存在,返回200状态信息 80 # 响应行 81 response_line = "HTTP/1.1 200 OK " 82 # 响应头 83 response_header = "Server: pws/1.0 " 84 # 空行 85 # 响应体 86 response_body = file_data 87 88 # 把数据封装成http,响应报文格式的数据 89 response = (response_line + 90 response_header + 91 " ").encode("UTF-8") + response_body # 响应行/头是字符串,响应体是二进制。 92 # 这里把响应行/头也改成二进制,再连接起来 93 # 发送给浏览器的响应报文数据 94 new_socket.send(response) 95 96 finally: 97 # 关闭服务于客户端的套接字 98 new_socket.close() 99 100 # 启动服务器的方法 101 def start(self): 102 # 循环等待接受客户端的连接请求 103 while True: 104 # 等待接受客户端的连接请求 105 new_socket, ip_port = self.tcp_server_socket.accept() 106 # 当客户端和服务器建立连接,创建子线程 107 sub_thread = threading.Thread(target=self.handle_client_request, args=(new_socket,)) 108 # 设置守护主线程 109 sub_thread.setDaemon(True) 110 # 启动子线程执行的对应任务 111 sub_thread.start() 112 113 def main(): 114 # 创建web服务器 115 web_server = HttpWebServer() 116 # 启动服务器 117 web_server.start() 118 119 if __name__ == '__main__': 120 main()