4. HTTP客户端
任务说明
本案例使用Python原生socket实现一个HTTP/1.1客户端,向指定服务器发送GET请求,并解析响应中的状态行、响应头与正文内容。通过该案例理解HTTP协议在TCP层之上的报文格式与解析方法。
核心实现
import socket
def http_get(host, path="/", port=80, timeout=5):
"""发送HTTP GET请求并解析响应,返回(状态行, 响应头dict, 正文bytes)"""
# 1. 建立TCP连接
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)
sock.connect((host, port))
# 2. 构造并发送HTTP GET请求
request = (
f"GET {path} HTTP/1.1\r\n"
f"Host: {host}\r\n"
"Connection: close\r\n" # 响应结束后关闭连接
"User-Agent: socket-demo/1.0\r\n"
"\r\n"
)
sock.sendall(request.encode("utf-8"))
# 3. 接收全部响应数据
response = b""
while True:
chunk = sock.recv(4096)
if not chunk:
break
response += chunk
sock.close()
# 4. 解析响应:分割头部与正文
header_end = response.find(b"\r\n\r\n")
if header_end == -1:
raise ValueError("无效的HTTP响应:未找到头部结束标记")
header_data = response[:header_end].decode("utf-8", errors="replace")
body = response[header_end + 4:] # 跳过\r\n\r\n
# 5. 解析状态行与响应头
lines = header_data.split("\r\n")
status_line = lines[0]
headers = {}
for line in lines[1:]:
if ":" in line:
key, value = line.split(":", 1)
headers[key.strip().lower()] = value.strip()
return status_line, headers, body
if __name__ == "__main__":
# 示例:请求httpbin.org的/get接口(返回请求方IP等JSON信息)
status, headers, body = http_get("httpbin.org", "/get")
print("状态行:", status)
print("Content-Type:", headers.get("content-type"))
print("正文前200字节:", body[:200].decode("utf-8", errors="replace"))
运行与验证
运行方式:直接执行脚本(需联网):
python http_client.py
验证方法:程序输出应包含:
- 状态行以
HTTP/1.1 200 OK开头; Content-Type为application/json;- 正文为JSON格式,包含
"url": "http://httpbin.org/get"字段。
若网络不可用,可改为请求本地服务验证:在另一终端运行 python -m http.server 8000,然后将调用改为 http_get("127.0.0.1", "/", 8000),预期状态行为 HTTP/1.0 200 OK,正文为目录文件列表HTML。
关键说明
- 使用
Connection: close简化接收逻辑——服务器发送完响应后主动断开,客户端据此判断接收完毕; - 响应头解析统一转为小写键名,便于无差别访问;
- 正文按原始字节返回,由调用方根据
Content-Type决定解码方式,避免二进制内容被错误解码。