Python 实现 Web 静态服务器(HTTP 协议)

news2025/6/13 6:15:43

目录

  • 一、在本地启动 HTTP 服务器
    • 1. Windows 下安装 node.js
      • 1)下载安装包
      • 2)配置环境变量
      • 3)安装镜像
      • 4)node.js 的常用命令
    • 2. 安装 http-server 服务
    • 3. 使用 http-server 开启服务
      • 1)使用 http-server
      • 2)详解 http-server [path] [options]
  • 二、Web 静态服务器
    • 1. 显示固定的页面
      • 1)Python 代码
      • 2)结果展示
    • 2. 显示请求的页面
      • 1)Python 代码
      • 2)结果展示
    • 3. 多进程显示页面
    • 4. 多线程显示页面
    • 5. 非阻塞模式显示页面
    • 6. 利用 epoll 显示页面(Linux 下运行)
    • 7. 利用 gevent 显示页面


有关 HTTP 的基础知识:【应用层 IV(万维网WWW)【★★】】

一、在本地启动 HTTP 服务器

1. Windows 下安装 node.js

1)下载安装包

node.js 下载网址:【node.js 中文网】

  • 进入官网,根据需求下载安装包,注意:

    • .msi 是 Windows 系统下的一种安装包文件格式,这种文件类型包含了某个软件或程序的所有安装信息和必要文件,用户只需按照提示进行安装,即可成功将软件或程序部署到 Windows 系统中。

    • .zip 是程序的压缩包,不需要进行安装,解压即可。

  • 根据下图安装软件,可以自行修改安装路径。


  • 安装好后,Win + X 点击 “系统” → “高级系统设置” → “环境变量(N)…” 并双击 “系统变量(S)” 下的 PATH 可以看到新增了一项 D:\nodejs\(设置的安装路径)。

  • Win + R 输入 cmd 进入命令提示符,输入 node -vnpm -v ,如果输出版本号,则说明 node.js 安装成功。

2)配置环境变量

  • 在安装路径 D:\nodejs\ 下新建两个文件夹 “node_global” 和 “node_cache” ,并复制它们的路径。

  • 以管理员身份运行 cmd ,输入以下两条指令:

npm config set prefix "D:\nodejs\node_global"
npm config set cache "D:\nodejs\node_cache"
  • 可以通过以下两条指令查看配置的路径:
npm config get prefix
npm config get cache
  • Win + X 点击 “系统” → “高级系统设置” → “环境变量(N)…”

    1. 双击 “用户变量(U)” 下的 PATH ,将 C:\Users\[用户名]\AppData\Roaming\npm 修改成 D:\nodejs\node_global

    2. 双击 “系统变量(S)” 下的 PATH ,新建 D:\nodejs\node_global\node_modulesD:\nodejs\node_cache ,最后一路点击确定即可。

注:此时 node_global 文件夹下并无 node_modules 文件夹,没有关系,先进行设置即可。

  • 配置完成后,全局安装一个 express 模块进行测试是否配置成功:以管理员身份运行 cmd ,输入指令 npm install express -g ,其中 -g 代表全局安装,此时 node_global 文件夹下才会自动创建一个名为 node_modules 的文件夹。

3)安装镜像

  • 以管理员身份运行 cmd ,输入指令 npm config set registry https://registry.npmmirror.com 安装淘宝镜像。

  • 输入指令 npm config get registry 查看是否安装成功。

【可选】如果想要使用 cnpm 命令行工具代替默认的 npm ,则进行以下操作:

  • 以管理员身份运行 cmd ,输入指令 npm install -g cnpm --registry=https://registry.npmmirror.com

  • 输入指令 cnpm -v 查看是否安装成功。

4)node.js 的常用命令

  • 检查版本
# 检查 Node.js 版本
node -v

# 检查 npm 版本
npm -v
  • 初始化项目
# 初始化一个新的 Node.js 项目
npm init
  • 安装和卸载包
# 安装特定版本的 Node.js 包
npm install <package-name>@<version>

# 全局安装 Node.js 包
npm install -g <package-name>

# 卸载 Node.js 包
npm uninstall <package-name>
  • 查看已安装的包
# 查看全局安装的 Node.js 包
npm list -g --depth 0

# 查看已安装的本地包
npm ls
  • 更新包
# 更新所有全局安装的 Node.js 包
npm update -g

# 更新特定 Node.js 包
npm update <package-name>
  • 运行代码和启动应用程序
# 运行 Node.js 文件
node <filename.js>

# 使用 nodemon 启动应用程序(自动重启)
nodemon <filename.js>

# 在浏览器中打开应用程序
npm start

# 指定环境变量启动应用程序
NODE_ENV=production node <filename.js>

参考文章:
【node.js安装及环境配置超详细教程【Windows系统安装包方式】】
【2024最新版Node.js下载安装及环境配置教程【保姆级】】
【Node.js安装及环境配置超详细教程【Windows系统】】

2. 安装 http-server 服务

http-server 是一个简单且快速的零配置命令行静态文件服务器,主要用于本地快速启动一个静态文件服务,它通常用于开发和测试环境。

  • 以管理员身份运行 cmd ,输入指令 npm install http-server -g 进行全局安装 http-server 服务。

  • 输入命令 http-server -v 可查看该服务是否安装成功。

3. 使用 http-server 开启服务

1)使用 http-server

  • Win + R 输入 cmd 进入命令提示符,通过 cd 操作将磁盘路径改至需要开启服务的路径下。

  • 输入命令 http-server -p 7890 指定端口开启服务器。

默认的访问地址是:http://127.0.0.1:8080

  • 启动成功可以通过 http://127.0.0.1:7890 进行访问。

  • 按下 Ctrl + C 终止服务。

2)详解 http-server [path] [options]

以下是 http-server 常用的一些命令和参数:

参数说明示例
[path]指定服务器根目录,默认当前目录http-server ./public
-p or --port <port>指定监听端口,默认 8080http-server -p 3000
-a or --address <address>绑定的地址,默认 0.0.0.0(所有地址)http-server -a 127.0.0.1
-c or --cache <time>缓存时间(秒)http-server -c 3600
-c-1-1 表示禁用缓存http-server -c-1
-d or --directory启用目录列表显示http-server -d
-i or --index <file>指定默认首页文件http-server -i index.html
-h or --help显示帮助信息http-server -h
-o or --open启动时自动在浏览器打开http-server -o
-g or --gzip启用 gzip 压缩http-server -g
-e or --ext <extension>设置默认扩展名,默认 htmlhttp-server -e htm
-s or --silent静默模式,不输出任何日志信息http-server -s
-r or --robots <file>指定 robots.txt 文件位置http-server -r ./robots.txt
--cors启用 CORS 跨域http-server --cors
-S or --ssl启用 HTTPShttp-server --ssl --cert ./cert.pem --key ./key.pem
-C or --cert <file>SSL 证书文件路径http-server -S -C ./cert.pem -K ./key.pem
-K or --key <file>SSL 私钥文件路径http-server -S -C ./cert.pem -K ./key.pem
-U or --utf8对 URL 使用 UTF-8 编码http-server -U
-P or --proxy <url>代理未找到的请求到指定 URLhttp-server -P http://example.com

参考文章:【http-server使用,启动本地服务器 & 使用serve包本地启动】

二、Web 静态服务器

1. 显示固定的页面

1)Python 代码

# 服务器端
import socket


def service_client(client_socket):
    # 1. 接收浏览器发送过来的请求 ,即 http 请求
    recv_data = client_socket.recv(4096).decode("utf-8")
    request_header_lines = recv_data.splitlines()
    for line in request_header_lines:
        print(line)
    # 2. 返回 http 格式的数据,给浏览器
    response_headers = "HTTP/1.1 200 OK\r\n"  # 200 表示找到这个资源
    response_headers += "\r\n"  # 用一个空的行与 body 进行隔开
    response_body = "hello world"
    # 将 response header 和 response body 发送给浏览器
    response = response_headers + response_body
    client_socket.send(response.encode("utf-8"))
    client_socket.close()


def main():
    # 1. 创建 TCP 套接字
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 确保端口复用
    # 设置服务器端 4 次挥手之后资源能够立即释放,这样就保证下次运行程序时 可以立即绑定 8888 端口
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    # 2. 绑定 IP 和端口号,127.0.0.1 是本机对自己的网络地址,即本地回环地址
    ip = "127.0.0.1"
    port = 8888
    server_socket.bind((ip, port))
    print("The ip and port used by the HTTP server : (%s : %s)" % (ip, port))
    # 3. 变为监听套接字
    server_socket.listen(128)
    while True:
        # 4. 等待新客户端的链接
        client_socket, client_addr = server_socket.accept()
        # print(f'---Client [{client_addr[0]}:{client_addr[1]}] Link Success---')
        # 5. 为这个客户端服务
        service_client(client_socket)


if __name__ == "__main__":
    main()

实现步骤:

  1. 运行上述服务器端代码。
  2. 在浏览器的地址栏里输入 “http://127.0.0.1:8888” 并回车。

2)结果展示

、

右击浏览器页面,选择 “检查” → “网络” ,输入 Ctrl + R 刷新纪录,再单击想要查看的名称就可以查看详细的标头信息。

2. 显示请求的页面

1)Python 代码

import socket
import re
import time


# 服务器端
def service_client(new_socket, time_start):
    # 1. 接收浏览器发送过来的请求 ,即 http 请求
    request = new_socket.recv(4096).decode("utf-8")
    # 得到头部的每一行
    request_lines = request.splitlines()
    if request_lines:
        # 使用正则表达式获取请求的 url
        ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
        # 例如:'GET /how-to-learn-qt.html HTTP/1.1'
        if ret:
            file_name = ret.group(1)
            # 例如:/how-to-learn-qt.html
            if file_name == "/":
                file_name = "/index.html"
            print(">" * 30, file_name)  # 输出请求的 url
            for request_line in request_lines:
                print(request_line)  # 输出请求
            # 2. 返回 http 格式的数据,给浏览器
            try:
                f = open("./../html" + file_name, "rb")  # 打开请求的资源
            # 找不到资源就返回 404 Not Found
            except FileNotFoundError:
                response = "HTTP/1.1 404 NOT FOUND\r\n"  # header
                response += "\r\n"
                response += "------File Not Found-----"  # body
                new_socket.send(response.encode("utf-8"))
            else:
                response = "HTTP/1.1 200 OK\r\n"  # header
                response += "\r\n"
                html_content = f.read()  # body
                f.close()
                new_socket.settimeout(5)
                try:
                    # 2.1 将 response header 发送给浏览器
                    new_socket.send(response.encode("utf-8"))
                    # 2.2 将 response body 发送给浏览器
                    new_socket.send(html_content)
                # 发送超时
                except socket.timeout:
                    response = "HTTP/1.1 504 Gateway Timeout\r\n"  # header
                    response += "\r\n"
                    response += "------Send Timeout-----"  # body
                    new_socket.send(response.encode("utf-8"))
    # 关闭套接字
    new_socket.close()
    time_end = time.time()
    print(f'The total time spent is {time_end - time_start} seconds.')
    print('-' * 50)


def main():
    # 1. 创建 TCP 套接字
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 确保端口复用
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    # 2. 绑定 IP 和端口号
    ip = "127.0.0.1"
    port = 7890
    tcp_server_socket.bind((ip, port))
    print("The ip and port used by the HTTP server : (%s : %s)" % (ip, port))
    # 3. 变为监听套接字
    tcp_server_socket.listen(128)
    while True:
        # 4. 等待新客户端的链接
        new_socket, client_addr = tcp_server_socket.accept()
        # print(f'---Client [{client_addr[0]}:{client_addr[1]}] Link Success---')
        time_start = time.time()
        # 5. 为这个客户端服务
        service_client(new_socket, time_start)
    # 关闭监听套接字
    # tcp_server_socket.close()


if __name__ == "__main__":
    main()

HTML 资源下载:【Python 实现 Web 静态服务器中需要使用到的 HTML 资源】

实现步骤:

  1. Win + R 输入 cmd 进入命令提示符,通过 cd 操作将磁盘路径改至需要开启服务的路径下,并输入命令 http-server -p 7890 指定 7890 端口开启服务器。
  2. 运行上述服务器端代码。
  3. 在浏览器的地址栏里输入 “http://127.0.0.1:7890” 并回车。

2)结果展示

3. 多进程显示页面

Python 代码:

import socket
import re
import multiprocessing
import time


# 服务器端
def service_client(new_socket, time_start):
    # 1. 接收浏览器发送过来的请求 ,即 http 请求
    request = new_socket.recv(4096).decode("utf-8")
    # 得到头部的每一行
    request_lines = request.splitlines()
    if request_lines:
        # 使用正则表达式获取请求的 url
        ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
        # 例如:'GET /how-to-learn-qt.html HTTP/1.1'
        if ret:
            file_name = ret.group(1)
            # 例如:/how-to-learn-qt.html
            if file_name == "/":
                file_name = "/index.html"
            print(">" * 30, file_name)  # 输出请求的 url
            for request_line in request_lines:
                print(request_line)  # 输出请求
            # 2. 返回 http 格式的数据,给浏览器
            try:
                f = open("./../html" + file_name, "rb")  # 打开请求的资源
            # 找不到资源就返回 404 Not Found
            except FileNotFoundError:
                response = "HTTP/1.1 404 NOT FOUND\r\n"  # header
                response += "\r\n"
                response += "------File Not Found-----"  # body
                new_socket.send(response.encode("utf-8"))
            else:
                response = "HTTP/1.1 200 OK\r\n"  # header
                response += "\r\n"
                html_content = f.read()  # body
                f.close()
                new_socket.settimeout(5)
                try:
                    # 2.1 将 response header 发送给浏览器
                    new_socket.send(response.encode("utf-8"))
                    # 2.2 将 response body 发送给浏览器
                    new_socket.send(html_content)
                # 发送超时
                except socket.timeout:
                    response = "HTTP/1.1 504 Gateway Timeout\r\n"  # header
                    response += "\r\n"
                    response += "------Send Timeout-----"  # body
                    new_socket.send(response.encode("utf-8"))
    # 关闭子进程
    new_socket.close()
    time_end = time.time()
    print(f'The total time spent is {time_end - time_start} seconds.')
    print('-' * 50)


def main():
    # 1. 创建 TCP 套接字
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 确保端口复用
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    # 2. 绑定 IP 和端口号
    ip = "127.0.0.1"
    port = 7890
    tcp_server_socket.bind((ip, port))
    print("The ip and port used by the HTTP server : (%s : %s)" % (ip, port))
    # 3. 变为监听套接字
    tcp_server_socket.listen(128)
    while True:
        # 4. 等待新客户端的链接
        new_socket, client_addr = tcp_server_socket.accept()
        # print(f'---Client [{client_addr[0]}:{client_addr[1]}] Link Success---')
        time_start = time.time()
        # 5. 为这个客户端服务
        p = multiprocessing.Process(target=service_client, args=(new_socket, time_start))
        p.start()
        # 关闭父进程
        new_socket.close()
    # 关闭监听套接字
    # tcp_server_socket.close()


if __name__ == "__main__":
    main()

实现步骤:

  1. 运行上述服务器端代码。
  2. 在浏览器的地址栏里输入 “http://127.0.0.1:7890” 并回车。

4. 多线程显示页面

Python 代码:

import socket
import re
import threading
import time


# 服务器端
def service_client(new_socket, time_start):
    # 1. 接收浏览器发送过来的请求 ,即 http 请求
    request = new_socket.recv(4096).decode("utf-8")
    # 得到头部的每一行
    request_lines = request.splitlines()
    if request_lines:
        # 使用正则表达式获取请求的 url
        ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
        # 例如:'GET /how-to-learn-qt.html HTTP/1.1'
        if ret:
            file_name = ret.group(1)
            # 例如:/how-to-learn-qt.html
            if file_name == "/":
                file_name = "/index.html"
            print(">" * 30, file_name)  # 输出请求的 url
            for request_line in request_lines:
                print(request_line)  # 输出请求
            # 2. 返回 http 格式的数据,给浏览器
            try:
                f = open("./../html" + file_name, "rb")  # 打开请求的资源
            # 找不到资源就返回 404 Not Found
            except FileNotFoundError:
                response = "HTTP/1.1 404 NOT FOUND\r\n"  # header
                response += "\r\n"
                response += "------File Not Found-----"  # body
                new_socket.send(response.encode("utf-8"))
            else:
                response = "HTTP/1.1 200 OK\r\n"  # header
                response += "\r\n"
                html_content = f.read()  # body
                f.close()
                new_socket.settimeout(5)
                try:
                    # 2.1 将 response header 发送给浏览器
                    new_socket.send(response.encode("utf-8"))
                    # 2.2 将 response body 发送给浏览器
                    new_socket.send(html_content)
                # 发送超时
                except socket.timeout:
                    response = "HTTP/1.1 504 Gateway Timeout\r\n"  # header
                    response += "\r\n"
                    response += "------Send Timeout-----"  # body
                    new_socket.send(response.encode("utf-8"))
    # 关闭子线程
    new_socket.close()
    time_end = time.time()
    print(f'The total time spent is {time_end - time_start} seconds.')
    print('-' * 50)


def main():
    # 1. 创建 TCP 套接字
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 确保端口复用
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    # 2. 绑定 IP 和端口号
    tcp_server_socket.bind(("127.0.0.1", 7890))
    # 3. 变为监听套接字
    tcp_server_socket.listen(128)
    while True:
        # 4. 等待新客户端的链接
        new_socket, client_addr = tcp_server_socket.accept()
        # print(f'---Client [{client_addr[0]}:{client_addr[1]}] Link Success---')
        time_start = time.time()
        # 5. 为这个客户端服务
        p = threading.Thread(target=service_client, args=(new_socket, time_start))
        p.start()
        # 多线程时,new_socket 传递给子线程以后,主线程不能关闭
        # new_socket.close()
    # 关闭监听套接字
    # tcp_server_socket.close()


if __name__ == "__main__":
    main()

实现步骤:

  1. 运行上述服务器端代码。
  2. 在浏览器的地址栏里输入 “http://127.0.0.1:7890” 并回车。

5. 非阻塞模式显示页面

Python 代码:

import time
import socket
import sys
import re


# 定义一个 WSGI 服务器的类
class WSGIServer(object):
    def __init__(self, ip, port, documents_root):
        # 1. 创建套接字
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 确保端口复用
        self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 2. 绑定本地信息
        self.server_socket.bind((ip, port))
        # 3. 变为监听套接字
        self.server_socket.listen(128)
        # 4. 设置非阻塞
        self.server_socket.setblocking(False)
        # 客户列表
        self.client_socket_list = list()
        # 静态资源的路径
        self.documents_root = documents_root

    def run_forever(self):  # 运行服务器
        while True:
            # 等待对方链接
            try:
                new_socket, new_addr = self.server_socket.accept()
            # 未有浏览器链接
            except BlockingIOError:
                pass
            else:
                time_start = time.time()
                # 设置非阻塞
                new_socket.setblocking(False)
                # 添加客户至客户列表
                self.client_socket_list.append([new_socket, new_addr, time_start])
                print(f'New client ({new_addr[0]} : {new_addr[1]}) link success')
            # 遍历列表中的连接,如果有浏览器发过来数据,那么就处理
            for client_socket in self.client_socket_list:
                try:
                    request = client_socket[0].recv(4096).decode('utf-8')
                except BlockingIOError:
                    pass
                else:
                    if request:  # 有数据就处理数据
                        print(f'Client [{client_socket[1][1]}] is working')
                        self.deal_with_request(request, client_socket[0])
                    else:  # 客户与浏览器断开
                        client_socket[0].close()
                        time_end = time.time()
                        print(f'Client [{client_socket[1][1]}] finish request')
                        print(f'The total time spent is {time_end - client_socket[2]} seconds.')
                        self.client_socket_list.remove(client_socket)

    def deal_with_request(self, request, client_socket):  # 处理数据
        request_lines = request.splitlines()
        # 例如:'GET /how-to-learn-qt.html HTTP/1.1'
        ret = re.match(r"([^/]*)([^ ]+)", request_lines[0])
        if ret:
            # 例如:/how-to-learn-qt.html
            file_name = ret.group(2)
            if file_name == "/":
                file_name = "/index.html"
            print(">" * 30, file_name)
            for i, line in enumerate(request_lines):
                print(i, line)
            # 读取文件数据
            try:
                f = open(self.documents_root + file_name, "rb")
            except PermissionError:
                response_body = "file not found,Please enter the correct URL"
                response_header = "HTTP/1.1 404 not found\r\n"
                response_header += "Content-Type: text/html; charset=utf-8\r\n"
                response_header += "Content-Length: %d\r\n" % (len(response_body))
                response_header += "\r\n"
                # 将 header 返回给浏览器
                client_socket.send(response_header.encode('utf-8'))
                # 将 body 返回给浏览器
                client_socket.send(response_body.encode("utf-8"))
            else:
                content = f.read()
                f.close()
                response_body = content
                response_header = "HTTP/1.1 200 OK\r\n"
                response_header += "Content-Length: %d\r\n" % (len(response_body))
                response_header += "\r\n"
                # 将 header 和 body 返回给浏览器
                client_socket.send(response_header.encode('utf-8') + response_body)


# 设置服务器服务静态资源时的路径
DOCUMENTS_ROOT = "./../html"


def main():  # 控制 web 服务器整体
    if len(sys.argv) == 3:
        ip = sys.argv[1]
        port = sys.argv[2]
        if port.isdigit():
            port = int(port)
            print("The ip and port used by the HTTP server : (%s : %s)" % (ip, port))
            http_server = WSGIServer(ip, port, DOCUMENTS_ROOT)
            http_server.run_forever()
        else:
            print('The port number was entered incorrectly')
    else:
        print("Run the command : python3 4.non-blocking.py 127.0.0.1 7890")


if __name__ == "__main__":
    main()

实现步骤:

  1. 图形化界面或命令行传参 “127.0.0.1 7890”
  2. 运行上述服务器端代码。
  3. 在浏览器的地址栏里输入 “http://127.0.0.1:7890” 并回车。

“BlockingIOError: [WinError 10035] 无法立即完成一个非阻止性套接字操作” 的解决方法见:【Python BlockingIOError 阻塞错误】

6. 利用 epoll 显示页面(Linux 下运行)

Python 代码:

# !/usr/bin/python
# -*- coding:utf-8 -*-

import socket
import select
import re

BASE_PATH = './../html'


# 服务器端
def service_client(new_socket, request):
    request_lines = request.splitlines()
    if not request_lines:
        return
    ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
    if ret:
        file_name = ret.group(1)
        if file_name == "/":
            file_name = "/index.html"
        print("*" * 50, file_name)
        print(request)
        # 2. 返回 http 格式的数据,给浏览器
        try:
            f = open(BASE_PATH + file_name, "rb")
        except FileNotFoundError:
            response = "HTTP/1.1 404 NOT FOUND\r\n"
            response += "\r\n"
            response += "------file not found-----"
            new_socket.send(response.encode("utf-8"))
        else:
            html_content = f.read()
            f.close()
            response_body = html_content
            response_header = "HTTP/1.1 200 OK\r\n"
            response_header += "Content-Length:%d\r\n" % len(response_body)
            response_header += "\r\n"
            response = response_header.encode("utf-8") + response_body
            new_socket.send(response)


def main():
    # 1. 创建套接字
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 确保端口复用
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    # 2. 绑定 IP 和端口号
    ip = "192.168.200.128"
    port = 7890
    tcp_server_socket.bind((ip, port))
    print("The ip and port used by the HTTP server : (%s : %s)" % (ip, port))
    # 3. 变为监听套接字
    tcp_server_socket.listen(128)
    # 将套接字变为非堵塞
    tcp_server_socket.setblocking(False)
    # 4. 创建一个epoll对象
    epl = select.epoll()
    # 将监听套接字对应的 fd 注册到 epoll 中
    # 注册并监控 tcp_server_socket, select.EPOLLIN 为可读事件
    epl.register(tcp_server_socket.fileno(), select.EPOLLIN)
    fd_event_dict = dict()
    while True:
        # 默认会堵塞,直到 os 监测到数据到来 通过事件通知方式告诉这个程序,此时才会解堵塞
        # 轮询注册的事件
        fd_event_list = epl.poll()
        # [(fd, event), (套接字对应的文件描述符, 这个文件描述符到底是什么事件)]
        for fd, event in fd_event_list:
            # 等待新客户端的链接
            if fd == tcp_server_socket.fileno():
                new_socket, client_addr = tcp_server_socket.accept()
                # 注册并监控 new_socket
                epl.register(new_socket.fileno(), select.EPOLLIN)
                fd_event_dict[new_socket.fileno()] = new_socket
            # 判断已经链接的客户端是否有数据发送过来
            elif event == select.EPOLLIN:
                recv_data = fd_event_dict[fd].recv(4096).decode("utf-8")
                # 有数据则处理数据,通过不遍历来定位 socket
                if recv_data:
                    service_client(fd_event_dict[fd], recv_data)
                # 客户与浏览器断开
                else:
                    fd_event_dict[fd].close()
                    # 取消注册
                    epl.unregister(fd)
                    # 从字典中移除
                    del fd_event_dict[fd]
    # 关闭监听套接字
    # tcp_server_socket.close()


if __name__ == "__main__":
    main()

实现步骤:

  1. 开启虚拟机,然后配置 PyCharm 连接远程服务器,并切换 Python 解释器。
  2. 将需要运行的 .py 代码和文件部署上传至 Linux 的文件夹下(右键文件 → “部署” → “上传到…”)。
  3. 运行上述服务器端代码(注意这时的 IP 地址并非本地回环地址,而是 Liunx 的 IPv4 地址)。
  4. 在浏览器的地址栏里输入 “http://192.168.200.128:7890” 并回车。

配置 PyCharm 连接远程服务器的步骤见:【Python-简单网络编程 I】

7. 利用 gevent 显示页面

Python 代码:

import gevent
from gevent import monkey

monkey.patch_all()

import socket
import re

BASE_PATH = './../html'

def service_client(new_socket):
    # 接收 http 请求
    request = new_socket.recv(4096).decode('utf-8')
    if request:
        request_lines = request.splitlines()
        ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
        if ret:
            file_name = ret.group(1)
            if file_name == "/":
                file_name = "/index.html"
            print(">" * 30, file_name)
            print(request)  # 输出请求
            try:
                f = open(BASE_PATH + file_name, "rb")
            except FileNotFoundError:
                response = "HTTP/1.1 404 NOT FOUND\r\n"
                response += "\r\n"
                response += "-------file not found-------"
                new_socket.send(response)
            else:
                html_content = f.read()
                f.close()
                response = "HTTP/1.1 200 OK\r\n"
                response += "\r\n"
                new_socket.send(response.encode('utf-8'))
                new_socket.send(html_content)
    new_socket.close()


def main():
    # 1. 初始化
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 确保端口复用
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    # 2. 绑定 IP 和端口号
    ip = "127.0.0.1"
    port = 7890
    tcp_server_socket.bind((ip, port))
    print("The ip and port used by the HTTP server : (%s : %s)" % (ip, port))
    # 3. 变为监听套接字
    tcp_server_socket.listen(128)
    while True:
        # 4. 等待新客户端的链接
        new_socket, socket_addr = tcp_server_socket.accept()
        # print(f'New client ({socket_addr[0]} : {socket_addr[1]}) link success')
        # 创建一个普通的 greenlet 对象并切换
        gevent.spawn(service_client, new_socket)
    # tcp_server_socket.close()


if __name__ == '__main__':
    main()

实现步骤:

  1. 运行上述服务器端代码。
  2. 在浏览器的地址栏里输入 “http://127.0.0.1:7890” 并回车。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2407106.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PHP 8.5 即将发布:管道操作符、强力调试

前不久&#xff0c;PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5&#xff01;作为 PHP 语言的又一次重要迭代&#xff0c;PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是&#xff0c;借助强大的本地开发环境 ServBay&am…

【Linux系统】Linux环境变量:系统配置的隐形指挥官

。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量&#xff1a;setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…

Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)

引言 工欲善其事&#xff0c;必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后&#xff0c;我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集&#xff0c;就像是连接开发者与 Chromium 庞大代码库的智能桥梁…

脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)

一、OpenBCI_GUI 项目概述 &#xff08;一&#xff09;项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台&#xff0c;其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言&#xff0c;首次接触 OpenBCI 设备时&#xff0c;往…

Unity UGUI Button事件流程

场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…

Web后端基础(基础知识)

BS架构&#xff1a;Browser/Server&#xff0c;浏览器/服务器架构模式。客户端只需要浏览器&#xff0c;应用程序的逻辑和数据都存储在服务端。 优点&#xff1a;维护方便缺点&#xff1a;体验一般 CS架构&#xff1a;Client/Server&#xff0c;客户端/服务器架构模式。需要单独…

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)

前言&#xff1a; 双亲委派机制对于面试这块来说非常重要&#xff0c;在实际开发中也是经常遇见需要打破双亲委派的需求&#xff0c;今天我们一起来探索一下什么是双亲委派机制&#xff0c;在此之前我们先介绍一下类的加载器。 目录 ​编辑 前言&#xff1a; 类加载器 1. …

c++第七天 继承与派生2

这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分&#xff1a;派生类构造函数与析构函数 当创建一个派生类对象时&#xff0c;基类成员是如何初始化的&#xff1f; 1.当派生类对象创建的时候&#xff0c;基类成员的初始化顺序 …

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台

淘宝扭蛋机小程序系统的开发&#xff0c;旨在打造一个互动性强的购物平台&#xff0c;让用户在购物的同时&#xff0c;能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机&#xff0c;实现旋转、抽拉等动作&#xff0c;增…

Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storms…

毫米波雷达基础理论(3D+4D)

3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文&#xff1a; 一文入门汽车毫米波雷达基本原理 &#xff1a;https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?

在工业自动化持续演进的今天&#xff0c;通信网络的角色正变得愈发关键。 2025年6月6日&#xff0c;为期三天的华南国际工业博览会在深圳国际会展中心&#xff08;宝安&#xff09;圆满落幕。作为国内工业通信领域的技术型企业&#xff0c;光路科技&#xff08;Fiberroad&…

MySQL:分区的基本使用

目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区&#xff08;Partitioning&#xff09;是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分&#xff08;分区&#xff09;可以独立存储、管理和优化&#xff0c;…

HubSpot推出与ChatGPT的深度集成引发兴奋与担忧

上周三&#xff0c;HubSpot宣布已构建与ChatGPT的深度集成&#xff0c;这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋&#xff0c;但同时也存在一些关于数据安全的担忧。 许多网络声音声称&#xff0c;这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…

Golang——7、包与接口详解

包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…

给网站添加live2d看板娘

给网站添加live2d看板娘 参考文献&#xff1a; stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下&#xff0c;文章也主…

手机平板能效生态设计指令EU 2023/1670标准解读

手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读&#xff0c;综合法规核心要求、最新修正及企业合规要点&#xff1a; 一、法规背景与目标 生效与强制时间 发布于2023年8月31日&#xff08;OJ公报&…

解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用

在工业制造领域&#xff0c;无损检测&#xff08;NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统&#xff0c;以非接触式光学麦克风技术为核心&#xff0c;打破传统检测瓶颈&#xff0c;为半导体、航空航天、汽车制造等行业提供了高灵敏…

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障

关键领域软件测试的"安全密码"&#xff1a;Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力&#xff0c;从金融交易到交通管控&#xff0c;这些关乎国计民生的关键领域…