Python自带简易静态web服务器搭建

静态web服务器程序指的是可以根据浏览器的请求报文,在响应报文中返回对应静态文件的程序

静态文件是指数据不需要动态生成的文件。
天气预报网站的数据会实时更新,这种网页就是动态网页。静态web服务器程序为静态网页服务。

http.server模块

http.server是Python自带的一个模块

在终端中进入想要作为web服务器静态文件集的文件夹,运行

python -m http.server port

就可以快速搭建一个简单的静态web服务器

其中

  • -m代表运行模块
  • port是端口号

如:
在这里插入图片描述

将8000作为静态web服务器程序的端口号

在浏览器中输入http://localhost:8000进入/主页。因为没有特别指定页面,所以显示如下

在这里插入图片描述

输入http://localhost:8000/welcome.html获取welcome页面。

在这里插入图片描述

可以看到响应行中服务器名称为SimpleHTTP/0.6 Python/3.10.3.

在这里插入图片描述


DIY简易静态web服务器程序搭建

其实静态web服务器程序也仅仅只是一个服务端程序而已。它的功能也是接受客户端程序的数据,并向客户端程序发送数据。它也基于TCP协议。只不过比较特殊的是,静态web服务器程序接受和发送的数据格式已经被HTTP协议规定好了。

浏览器程序发送类似以下格式的请求报文:

POST / HTTP/1.1\r\n   (请求行)
Host: www.baidu.com\r\n   (请求头信息)
....
\r\n  (空行)
username=hello&pass=hello\r\n   (请求体)

静态web服务器程序回之以类似以下格式的响应报文:

HTTP/1.1 200 OK\r\n   (响应行)
Server: BWS/1.1\r\n    (响应头信息)
...
\r\n   (空行)
<!DOCTYPE html><html lang=“en”> …</html>    (响应体)
普通版
步骤
  1. 创建静态web服务器程序的套接字并绑定端口号

  2. 设置监听

  3. 循环接受浏览器连接请求

  4. 一旦成功建立连接,创建一个子线程

  5. 在子线程中接受浏览器发送的请求报文

  6. 在子线程中分析请求报文并编写对应响应报文

  7. 在子线程中向浏览器发送响应报文

  8. 在子线程中断开与浏览器的连接

具体代码
import socket
import threading


def handle(handle_socket: "socket.socket"):
    """与浏览器进行数据通信的子线程"""
    # 5.在子线程中接受浏览器发送的请求报文
    request_data = handle_socket.recv(4096)

    # 6.在子线程中分析请求报文并编写对应响应报文
    if len(request_data) == 0:
        print("浏览器已断开连接...")
        handle_socket.close()
        return

    request_content = request_data.decode("utf-8")
    print(request_content)
    # 以\r\n分割各项信息
    request_list = request_content.split("\r\n")
    # 提取请求的资源路径
    request_line = request_list[0]
    request_line_list = request_line.split(" ")
    request_method, request_path, request_version = request_line_list

    # 首页
    if request_path == "/":
        request_path = "/welcome.html"

    # 响应行与响应头信息置空
    response_line = response_header = ""

    # 根据请求路径准备好响应行和响应体
    try:
        with open("." + request_path, "rb") as request_file:
            response_body = request_file.read()

    except (FileExistsError, FileNotFoundError):
        response_line += f"{request_version} 404 Not Found\r\n"
        with open("./404.html", "rb") as request_file:
            response_body = request_file.read()

    else:
        response_line += f"{request_version} 200 OK\r\n"

    finally:
        # 准备好响应头信息
        response_header += "Server: MyWebServer1.0\r\n"
        # 7.在子线程中向浏览器发送响应报文
        response_data = (response_line + response_header + "\r\n").encode("utf-8") + response_body
        handle_socket.send(response_data)
        # 8.在子线程中断开与浏览器的连接
        handle_socket.close()


def main():
    # 1.创建静态web服务器程序的套接字并绑定端口号
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    server_socket.bind(("localhost", 8888))

    # 2.设置监听
    server_socket.listen(128)

    # 3.循环接受浏览器连接请求
    while True:
        new_socket, address = server_socket.accept()
        print("已连接", address, sep=" from ")

        # 4.一旦成功建立连接,创建一个子线程
        sub_thread = threading.Thread(target=handle, args=(new_socket,), daemon=True)
        sub_thread.start()


if __name__ == "__main__":
    main()
面向对象版

对普通版进行抽象即可。

代码
import socket
import threading


class MyWebServer(object):
    def __init__(self,port):
        """初始化:创建套接字"""
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
        self.server_socket.bind(("localhost", port))
        self.server_socket.listen(128)

    def start(self):
        """启动:建立连接,并开启子线程"""
        while True:
            new_socket, address = self.server_socket.accept()
            print("已连接", address, sep=" from ")

            # 4.一旦成功建立连接,创建一个子线程
            sub_thread = threading.Thread(target=self.handle, args=(new_socket,), daemon=True)
            sub_thread.start()

    @staticmethod
    def handle(handle_socket):
        """利用子线程收发http格式数据"""
        request_data = handle_socket.recv(4096)

        if len(request_data) == 0:
            print("浏览器已断开连接...")
            handle_socket.close()
            return

        request_content = request_data.decode("utf-8")
        print(request_content)
        # 以\r\n分割各项信息
        request_list = request_content.split("\r\n")
        # 提取请求的资源路径
        request_line = request_list[0]
        request_line_list = request_line.split(" ")
        request_method, request_path, request_version = request_line_list

        # 首页
        if request_path == "/":
            request_path = "/welcome.html"

        # 响应行与响应头信息置空
        response_line = response_header = ""

        # 根据请求路径准备好响应行和响应体
        try:
            with open("." + request_path, "rb") as request_file:
                response_body = request_file.read()

        except (FileExistsError, FileNotFoundError):
            response_line += f"{request_version} 404 Not Found\r\n"
            with open("./404.html", "rb") as request_file:
                response_body = request_file.read()

        else:
            response_line += f"{request_version} 200 OK\r\n"

        finally:
            # 准备好响应头信息
            response_header += "Server: MyWebServer1.0\r\n"
            # 向浏览器发送响应报文
            response_data = (response_line + response_header + "\r\n").encode("utf-8") + response_body
            handle_socket.send(response_data)
            # 断开与浏览器的连接
            handle_socket.close()


def main():
    my_web_server=MyWebServer(8888)
    my_web_server.start()


if __name__ == "__main__":
    main()

命令行版

我们可以在终端中通过命令行执行python文件

命令行参数
python 参数1 参数2 参数3...
  • python代表用python解释器进行执行,Linux系统可能需要特意指定python3
  • 参数1一般是要执行的python文件名
  • 参数2,3…一般是传给要运行的python程序的参数。

如我们的案例需要的:

python MyWebServer.py 8888
在代码中访问
import sys

print(sys.argv)
#["MyWebServer.py","8888"]
  • 用到了sys模块的argv变量(argument values),它是一个由运行时实际的命令行参数组成的列表。
代码

为了像Python自带的http.server模块一样可以在命令行中运行,我们对main函数作如下改变:

import sys

...

def main()
    #命令行参数个数不对
    if len(sys.argv) != 2:
        print("执行错误。正确格式为python MyWebServer.py 8888(参数个数应为2...)")
        return

    # 端口号是否为整数
    if not sys.argv[1].isdigit():
        print("执行错误。正确格式为python MyWebServer.py 8888(端口号应为整数...)")
        return
    
    port=int(sys.argv[1])
    my_web_server=MyWebServer(port)
    my_web_server.start()
    
...

在这里插入图片描述

在这里插入图片描述

效果

不输入请求资源地址,默认进入主页(welcome.html)

在这里插入图片描述

输入指定资源路径,进入指定页面(advantages.html)

在这里插入图片描述

输入不存在资源路径,进入404页面

在这里插入图片描述


Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐