Python实现http动态服务器,有两种方法,一种是基本的实现方式,可扩展性差,但是是实现服务器的基础,第二种实现不修改服务器和架构代码而确保可以在多个架构下运行web服务器。都使用WSGI(Web Server Gateway Interface)。
1.第一种。说明:通过类的方式,实现功能,但是架构代码和服务器没有分离。有两个文件分别是HttpServer.py主文件,以及wsgi_python_program文件夹 下的hello.py
HttpServer.py:
import socketimport reimport sysfrom multiprocessing import ProcessPORT = 8080sys.path.insert(1, "./wsgi_python_program")# 用户能够获取的网页数据存放目录HTML_ROOT_DIR = "./html"class HttpServer(object): def __init__(self): self.listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) def bind(self, port): address = ("", port) self.listen_sock.bind(address) self.listen_sock.listen(128) def start(self): while True: client_sock, client_addr = self.listen_sock.accept() print("客户端%s已连接" % (client_addr,)) p = Process(target=self.handle_client, args=(client_sock, client_addr)) p.start() # 释放client_sock client_sock.close() def start_response(self, status_code, response_headers): """ 用来接收响应状态码与响应头 :param status_code: "200 OK" 状态码 :param response_headers: [("Server", "MyServer"), ("Content-Type", "text")] 响应头 :return: None """ resp_start_line = "HTTP/1.0 " + status_code + "\r\n" # 起始行 # 遍历处理response_headers,形成响应头 resp_headers = "" for header_name, header_value in response_headers: resp_headers += (header_name + ": " + header_value + "\r\n") # 将拼接好的响应报文前半部分保存 self.resp_start_line_headers = resp_start_line + resp_headers def handle_client(self, c_sock, c_addr): """ 子进程处理客户端 :param c_sock: socket类型对象 处理客户端通信用到的socket对象 :param c_addr: 元组 (ip, port) 客户端的地址信息 :return: None """ # 接收客户端发送过来的请求数据, 即http请求报文数据 http_req_data = c_sock.recv(1024) print("客户端 %s 发送的HTTP请求报文:\n %s" % (c_addr, http_req_data.decode())) # 解析客户端的请求报文 http_req_data_str = http_req_data.decode() # 对http_req_data_str按照"\r\n"分隔符进行拆分 req_start_line = http_req_data_str.split("\r\n")[0] # 请求的起始行 # 使用正则表达是从起始行中提出请求的文件名 # GET /index.html HTTP/1.1 match_result = re.match(r"(\w+) +(/\S*) +", req_start_line) req_method = match_result.group(1) file_path = match_result.group(2) print("file_path:", file_path) # 构造一个字典,用来保存解析的数据 environ = { "PATH_INFO": file_path, "REQUEST_METHOD": req_method } # 如果用户的请求的是/主路径,返回主页信息 if file_path.endswith(".py"): file_path = file_path[1:-3] mod = __import__(file_path) response_body = mod.application(environ, self.start_response) resp_data = self.resp_start_line_headers + "\r\n" + response_body c_sock.send(resp_data.encode()) else: if file_path == "/": file_path = "/index.html" # 打开文件 # file_path = "/index.html" try: file = open(HTML_ROOT_DIR + file_path, "rb") except IOError: # 表示用户请求的文件不存在,要返回404响应报文 # 构造响应报文 resp_start_line = "HTTP/1.0 404 Not Found\r\n" # 响应起始行 resp_headers = "Server: MyServer\r\n" # 响应头 resp_headers += "Content-Type: text\r\n" resp_body = "file not exist" # 响应体 http_resp_data = resp_start_line + resp_headers + "\r\n" + resp_body print("传回给客户端的响应HTTP报文:\n %s" % http_resp_data) # 传回给客户端响应数据 c_sock.send(http_resp_data.encode()) else: # 表示请求的文件存在, # 读取文件内容, bytes类型 file_data = file.read() # 关闭文件 file.close() # 构造响应报文 resp_start_line = "HTTP/1.0 200 OK\r\n" # 响应起始行 resp_headers = "Server: MyServer\r\n" # 响应头 resp_headers += "Content-Type: text/html\r\n" http_resp_data = (resp_start_line + resp_headers + "\r\n").encode() + file_data # 传回给客户端响应数据 c_sock.send(http_resp_data) # 关闭socket c_sock.close()def main(): http_server = HttpServer() http_server.bind(PORT) http_server.start()if __name__ == '__main__': main()
wsgi_python_program文件夹下的hello.py
import timedef application(environ, start_response): status_code = "200 OK" response_headers = ([("Server", "MyServer"), ("Content-Type", "text/html")]) start_response(status_code, response_headers) return time.ctime()
2.第二种。说明:有三个文件,分别是MyHttpServer文件,即HttpServer服务器主文件,MyFramework.py文件,封装成一个简单的框架。
urls是请求的地址路由列表,可以添加多个。实现服务器和架构代码分离。
MyHttpServer.py文件
import socketimport refrom multiprocessing import Processimport MyFrameworkPORT = 9000class HttpServer(object): def __init__(self, app): self.listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.app = app def bind(self, port): address = ("", port) self.listen_sock.bind(address) self.listen_sock.listen(128) def start(self): while True: client_sock, client_addr = self.listen_sock.accept() print("客户端%s已连接" % (client_addr,)) p = Process(target=self.handle_client, args=(client_sock, client_addr)) p.start() # 释放client_sock client_sock.close() def start_response(self, status_code, response_headers): resp_start_line = "HTTP/1.0 " + status_code + "\r\n" # 起始行 # 遍历处理response_headers,形成响应头 resp_headers = "" for header_name, header_value in response_headers: resp_headers += (header_name + ": " + header_value + "\r\n") # 将拼接好的响应报文前半部分保存 self.resp_start_line_headers = resp_start_line + resp_headers def handle_client(self, c_sock, c_addr): """ 子进程处理客户端 :param c_sock: socket类型对象 处理客户端通信用到的socket对象 :param c_addr: 元组 (ip, port) 客户端的地址信息 :return: None """ # 接收客户端发送过来的请求数据, 即http请求报文数据 http_req_data = c_sock.recv(1024) # 解析客户端的请求报文 http_req_data_str = http_req_data.decode() # 对http_req_data_str按照"\r\n"分隔符进行拆分 req_start_line = http_req_data_str.split("\r\n")[0] # 请求的起始行 # 使用正则表达是从起始行中提出请求的文件名 # GET /index.html HTTP/1.1 match_result = re.match(r"(\w+) +(/\S*) +", req_start_line) req_method = match_result.group(1) file_path = match_result.group(2) print("file_path:", file_path) # 构造一个字典,用来保存解析的数据 environ = { "PATH_INFO": file_path, "REQUEST_METHOD": req_method } # 如果用户的请求的是/主路径,返回主页信息 response_body = self.app(environ, self.start_response) resp_data = self.resp_start_line_headers + "\r\n" c_sock.send(resp_data.encode() + response_body) # 关闭socket c_sock.close()def main(): http_server = HttpServer(MyFramework.app) http_server.bind(PORT) http_server.start()if __name__ == '__main__': main()
MyFramework.py
import timePORT = 8080# 用户能够获取的网页数据存放目录HTML_ROOT_DIR = "./html"class Application(object): def __init__(self, urls): self.urls = urls def __call__(self, environ, start_response): file_path = environ["PATH_INFO"] if file_path.startswith("/static"): # 表示用户请求的是静态文件 # path == "/static/index.html" file_path = file_path[7:] # 切取文件路径 if file_path == "/": file_path = "/index.html" try: file = open(HTML_ROOT_DIR + file_path, "rb") except IOError: # 表示用户请求的文件不存在,要返回404响应报文 status_code = "404 Not Found" # 响应状态码 response_headers = [("Server", "MyServer"), ("Content-Type", "text")] # 响应头 start_response(status_code, response_headers) return b"file not exist" else: file_data = file.read() file.close() status_code = "200 OK" # 响应状态码 response_headers = [("Server", "MyServer"), ("Content-Type", "text/html")] # 响应头 start_response(status_code, response_headers) return file_data else: # 表示用户请求的是动态程序 for view_path, view_fun in self.urls: if view_path == file_path: response_body = view_fun(environ, start_response) return response_body.encode() # 循环执行后,程序仍然没有返回,表示用户请求的路径没有找到,所以需要返回404错误 status_code = "404 Not Found" # 响应状态码 response_headers = [("Server", "MyServer"), ("Content-Type", "text")] # 响应头 start_response(status_code, response_headers) return b"program not exist"def say_hello(environ, start_response): status_code = "200 OK" response_headers = ([("Server", "MyServer"), ("Content-Type", "text/html")]) start_response(status_code, response_headers) return time.ctime()urls = [("/hello", say_hello)] # 路由列表app = Application(urls)
html文件下下index.html文件
你好陌生人
我在学习python