Wireshark捕获HTTP GET图片请求但Socket的recv()无法接收
问题根源分析
- HTTP响应不规范:你的
response.txt仅包含HTTP/1.1 200 OK状态行,缺少HTTP协议要求的核心响应头——比如Content-Type(告知浏览器返回内容的类型)、Content-Length(告知浏览器响应体的字节长度),以及Connection头。Chrome在HTTP/1.1下默认启用持久连接(Keep-Alive),如果服务器未明确标记响应结束或指定连接状态,浏览器会一直等待剩余数据,导致后续请求的数据流被阻塞。 - 未处理连接断开场景:
conn.recv()在客户端主动断开连接时会返回空字节串b'',你的代码未对该情况做判断,会陷入死循环,无法正确识别新请求的边界。 - 请求解析逻辑脆弱:假设每次
recv()都能完整接收一个HTTP请求,但TCP是流式传输,请求可能被拆分为多个数据包到达;同时,当请求行解析失败时(比如line01member长度不足),代码仅打印错误却继续执行,会导致后续open(token)抛出未捕获的异常。 - 文件路径处理错误:当请求图片
/w3logo.jpg时,代码直接用token = line01member[1][1:]得到w3logo.jpg,但未指定正确的文件目录,会在当前工作目录下查找,而非HTML文件所在的目录,大概率导致文件找不到。
修复后的代码
import socket import os import sys HOST = '192.168.43.157' PORT = 80 # 网站根目录,将HTML、图片等资源统一放在这里 WEB_ROOT = r"C:\Users\Totz Tech\Videos" def get_content_type(file_path): # 根据文件后缀返回对应的Content-Type ext = os.path.splitext(file_path)[1].lower() if ext == '.html': return 'text/html; charset=utf-8' elif ext in ('.jpg', '.jpeg'): return 'image/jpeg' elif ext == '.png': return 'image/png' else: return 'application/octet-stream' s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print('# Socket created') try: s.bind((HOST, PORT)) except socket.error as msg: print(f'# Bind failed: {msg}') sys.exit() print('# Socket bind complete') s.listen(10) print('# Socket now listening') while True: # 循环等待新连接,支持多客户端/同一客户端的多次请求 conn, addr = s.accept() print(f'# Connected to {addr[0]}:{addr[1]}') try: # 接收请求数据(简单处理,假设请求不超过1024字节) data = conn.recv(1024) if not data: conn.close() continue request = data.decode('utf-8') # 解析请求行 request_lines = request.split('\r\n') if not request_lines: conn.close() continue request_line = request_lines[0].strip() if not request_line: conn.close() continue parts = request_line.split() if len(parts) < 2: # 返回400错误:无效请求 response = b'HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\nConnection: close\r\n\r\n' conn.send(response) conn.close() continue method, path, _ = parts if method != 'GET': # 仅处理GET请求 response = b'HTTP/1.1 405 Method Not Allowed\r\nContent-Length: 0\r\nConnection: close\r\n\r\n' conn.send(response) conn.close() continue # 处理根路径请求 if path == '/': file_path = os.path.join(WEB_ROOT, 'mypage.html') else: # 防止路径遍历攻击,限制仅访问WEB_ROOT内的文件 file_path = os.path.join(WEB_ROOT, path.lstrip('/')) if not os.path.abspath(file_path).startswith(os.path.abspath(WEB_ROOT)): response = b'HTTP/1.1 403 Forbidden\r\nContent-Length: 0\r\nConnection: close\r\n\r\n' conn.send(response) conn.close() continue # 检查文件是否存在 if not os.path.exists(file_path) or not os.path.isfile(file_path): response = b'HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\nConnection: close\r\n\r\n' conn.send(response) conn.close() continue # 读取文件内容 with open(file_path, 'rb') as f: file_content = f.read() # 构建完整的HTTP响应 content_type = get_content_type(file_path) response_headers = ( f'HTTP/1.1 200 OK\r\n' f'Content-Type: {content_type}\r\n' f'Content-Length: {len(file_content)}\r\n' f'Connection: close\r\n' # 关闭连接,避免持久连接导致的阻塞 f'\r\n' # 空行分隔响应头和响应体 ).encode('utf-8') # 发送响应 conn.send(response_headers + file_content) print(f'Sent {file_path} to {addr[0]}') except Exception as e: print(f'Error handling request: {e}') finally: # 确保连接关闭 conn.close() s.close()
关键修复点说明
- 规范HTTP响应:添加
Content-Type、Content-Length和Connection: close头,明确告知浏览器响应结束并关闭连接,避免持久连接导致的阻塞问题。 - 异常与边界处理:判断
recv()返回空的情况,处理无效请求、非GET请求等场景,避免死循环和未捕获异常。 - 安全路径控制:用
WEB_ROOT统一管理静态资源,防止路径遍历攻击,确保仅访问指定目录内的文件。 - 多类型资源支持:根据文件后缀返回正确的
Content-Type,让浏览器能正确解析HTML、图片等资源。 - 循环接受连接:外层添加循环,支持多个客户端或同一客户端的多次请求(每次请求后关闭连接)。
内容的提问来源于stack exchange,提问作者The Mr. Totardo
相关产品推荐
相关产品推荐

