Python 网络编程实战:用 Socket 实现一个 HTTP 代理服务器Python

1. 环境准备

我们将使用 Python 3.8+ 的标准库 socketthreading 实现一个简单的 HTTP 正向代理。无需安装第三方包。请确保你的终端可以运行 Python 脚本。

复制代码
python3 --version

建议在项目目录下创建 proxy.py 文件,按步骤逐步添加代码。

2. 实现代理服务器

代理的核心逻辑:监听客户端连接,解析 HTTP 请求中的目标主机和端口,然后与目标建立 TCP 连接并转发数据。这里我们仅处理 GETPOST 请求,忽略 CONNECT 方法(HTTPS 代理留作扩展)。

2.1 创建服务器 Socket

复制代码
import socket
import threading

def handle_client(client_sock):
    # 接收请求头
    request = b''
    while b'\r\n\r\n' not in request:
        data = client_sock.recv(4096)
        if not data:
            break
        request += data
    if not request:
        client_sock.close()
        return
    
    # 解析请求行
    lines = request.split(b'\r\n')
    method, url, _ = lines[0].split(b' ')
    if method not in (b'GET', b'POST'):
        client_sock.send(b'HTTP/1.1 405 Method Not Allowed\r\n\r\n')
        client_sock.close()
        return
    
    # 解析 host 和 port
    from urllib.parse import urlparse
    parsed = urlparse(url.decode())
    host = parsed.hostname or 'localhost'
    port = parsed.port or 80
    
    # 连接目标服务器
    try:
        remote_sock = socket.create_connection((host, port), timeout=5)
    except Exception as e:
        client_sock.send(b'HTTP/1.1 502 Bad Gateway\r\n\r\n')
        client_sock.close()
        return
    
    # 转发请求
    remote_sock.sendall(request)
    
    # 双向转发数据
    def forward(src, dst):
        while True:
            try:
                data = src.recv(4096)
                if not data:
                    break
                dst.sendall(data)
            except:
                break
        dst.shutdown(socket.SHUT_WR)
    
    t1 = threading.Thread(target=forward, args=(client_sock, remote_sock))
    t2 = threading.Thread(target=forward, args=(remote_sock, client_sock))
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    client_sock.close()
    remote_sock.close()

def start_proxy(host='127.0.0.1', port=8080):
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind((host, port))
    server.listen(5)
    print(f'Proxy running on {host}:{port}')
    while True:
        client_sock, _ = server.accept()
        threading.Thread(target=handle_client, args=(client_sock,)).start()

if __name__ == '__main__':
    start_proxy()

2.2 关键点说明

  • 使用 recv(4096) 循环读取,直到遇到 \r\n\r\n 表示请求头结束。
  • 解析 URL 提取主机和端口,默认端口 80。
  • 双线程转发数据,实现双向通信。

3. 验证效果

运行代理服务器:

复制代码
python3 proxy.py

在另一个终端,用 curl 通过代理访问一个 HTTP 网站(如 httpbin.org):

复制代码
curl -x http://127.0.0.1:8080 http://httpbin.org/get

你应该会看到 httpbin 返回的 JSON 数据,其中 headers 字段会包含你的代理地址。如果代理转发成功,说明实现正确。

3.1 常见问题

  • 如果报错 Connection refused,检查目标端口是否正确。
  • 如果请求被拒绝,检查 method 是否只允许 GET/POST。

4. 扩展建议

你可以尝试添加 HTTPS 支持(CONNECT 方法)、访问日志、缓存等功能。这个简单代理是理解网络通信的绝佳起点。

相关推荐
小花皮猪5 天前
2026 代理网络选型指南:住宅/机房/ISP/移动代理怎么选?(附 Bright Data / Oxylabs / Decodo 实测对比)
爬虫·数据采集·代理
不叫猫先生5 天前
2026 Web Scraping 代理服务商怎么选?住宅代理与代理网络对比指南
爬虫·网络代理·代理·住宅代理
binqian6 天前
Nginx 多 Worker 进程监听同一端口场景下 Linux 内核 Socket 创建管理与请求寻址机制
socket
...6227 天前
WinForm中Socket通信服务端客户端代码实现讲解
tcp/ip·c#·socket
易岳群8 天前
搞清 Socket、TCP、MQTT 的通信关系:从底层协议到物联网实战
网络协议·tcp/ip·socket·通信协议关系
ly768913 天前
Spring 中的 @Configuration 与 @Component 差异:为何代理时机决定 Bean 生命周期行为
java·后端·spring·注解·代理·bean生命周期
深念Y1 个月前
局域网 HTTP 请求转发 502 问题排查记录
网络·网络协议·http·网络安全·代理·转发·中转
在世修行1 个月前
激光打标机TCP Socket通信协议:KZ指令集实战
tcp/ip·socket·自定义协议
神龙斗士2401 个月前
Socket编程:客户端与服务器通信全解析(网络编程)
运维·服务器·网络·socket