You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Python Socket的HTTP Server多请求复用连接问题求助

Python Socket HTTP服务器持久连接(Keep-Alive)问题解决

问题核心

基于Python Socket实现HTTP服务器时,已在响应中添加Connection: keep-alive和Content-Length头,客户端请求也携带Connection: keep-alive,但浏览器仍通过新连接请求HTML关联的CSS、图片等资源,原连接recv持续返回空字符串。

关键解决步骤

1. 严格遵循HTTP响应格式

HTTP响应头必须以\r\n\r\n作为结束标记,这是协议规定的头与响应体的分隔符。如果缺少这个标记,浏览器会认为响应不完整,直接关闭连接,不会复用当前连接发送后续请求。

示例响应头构建:

headers = [
    'HTTP/1.1 200 OK',
    f'Content-Length: {len(content_bytes)}',
    'Connection: keep-alive',
    f'Content-Type: {content_type}',
    ''  # 空行对应\r\n,最终join后会生成\r\n\r\n
]
response_head = '\r\n'.join(headers).encode('utf-8')

2. 准确计算Content-Length

Content-Length的值必须是响应体的字节数,而非字符数:

  • 处理文本文件(HTML/CSS)时,读取后要编码为字节再计算长度;
  • 处理二进制文件(PNG/JPG)时,用rb模式读取,直接取len(content_bytes)。

错误示例(字符数代替字节数):

# 错误:文本字符串的长度不等于字节数(比如含中文时)
Content-Length: {len(content_str)}

正确示例:

with open(file_path, 'rb') as f:
    content_bytes = f.read()
# 直接用字节长度
Content-Length: {len(content_bytes)}

3. 完整读取HTTP请求

在持久连接下,浏览器会连续发送多个请求,不能仅调用一次recv(1024),需循环读取直到获取完整的请求头(以\r\n\r\n结尾):

def read_full_request(connection):
    req_data = b''
    while b'\r\n\r\n' not in req_data:
        chunk = connection.recv(1024)
        if not chunk:  # 客户端主动关闭连接
            return None
        req_data += chunk
    return req_data.decode('utf-8')

4. 连接内循环处理请求

在单个连接的处理逻辑中,加入循环持续接收并处理请求,直到客户端关闭连接:

def handle_connection(connection):
    while True:
        req = read_full_request(connection)
        if not req:
            connection.close()
            break
        
        # 解析请求路径、处理资源逻辑
        # ...
        
        # 构建并发送响应(确保格式正确)
        connection.sendall(response_head + content_bytes)

5. 用多线程处理连接

主循环在accept新连接后,启动单独线程处理该连接的循环请求,避免阻塞主循环接受新连接:

import socket
import threading

def main():
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind(('localhost', 8080))
    server_socket.listen(5)
    
    while True:
        conn, addr = server_socket.accept()
        threading.Thread(target=handle_connection, args=(conn,)).start()

if __name__ == '__main__':
    main()

6. 正确设置Content-Type

根据资源类型设置对应的Content-Type,否则浏览器无法正确解析资源,可能触发新连接请求:

  • .html → text/html; charset=utf-8
  • .css → text/css
  • .png → image/png
  • .jpg → image/jpeg

可以用字典映射快速获取:

import os

CONTENT_TYPES = {
    '.html': 'text/html; charset=utf-8',
    '.css': 'text/css',
    '.png': 'image/png',
    '.jpg': 'image/jpeg'
}
# 根据文件后缀获取类型
ext = os.path.splitext(file_path)[1].lower()
content_type = CONTENT_TYPES.get(ext, 'application/octet-stream')

验证方法

  1. 用浏览器开发者工具(Network面板)查看每个请求的Connection头,确认是否复用了同一连接(查看Remote Address是否一致);
  2. 在服务器端打印连接的fileno(),确认后续请求是否来自同一连接ID。

内容的提问来源于stack exchange,提问作者Huynh Cuong

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 14:01:38