Python实现web动态服务器

本文介绍两种用Python实现HTTP服务器的方法。第一种为基本实现,第二种实现了服务器与框架代码的分离,便于维护。文章详细展示了代码结构及工作流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 Python实现http动态服务器,有两种方法,一种是基本的实现方式,可扩展性差,但是是实现服务器的基础,第二种实现不修改服务器和架构代码而确保可以在多个架构下运行web服务器。都使用WSGI(Web Server Gateway Interface)。

1.第一种。说明:通过类的方式,实现功能,但是架构代码和服务器没有分离。有两个文件分别是HttpServer.py主文件,以及wsgi_python_program文件夹     下的hello.py

HttpServer.py:

import socket
import re
import sys
from multiprocessing import Process

PORT = 8080
sys.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 time


def 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 socket
import re
from multiprocessing import Process
import MyFramework

PORT = 9000


class 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 time

PORT = 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文件

<html>
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <h1 style="color:red;">你好陌生人</h1>
        <h3>我在学习python</h3>
        <script type="text/javascript">
            alert("hello python!");
        </script>
    </body>
</html>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值