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

1. 环境准备

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

复制代码
python3 --version

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

2. 实现代理服务器

代理的核心逻辑:监听客户端连接,解析 HTTP 请求中的目标主机和端口,然后与目标建立 TCP 连接并转发数据。这里我们仅处理 GET 和 POST 请求,忽略 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 方法)、访问日志、缓存等功能。这个简单代理是理解网络通信的绝佳起点。

相关推荐
cpolar技术支持2 天前
网关加 gzip,同时开启 chunked,下载回来的包解不开:把两层编码的解压顺序与长度核对做成只读页
python·http·网络编程·socket·内网穿透
bkspiderx3 天前
基于Socket的广播通信及C语言实现详解
c语言·开发语言·socket·广播·socket的广播通信
狮子座的男孩7 天前
解决:使用Burp Suite工具实现防重放攻击的过程及遇到的代理等问题
经验分享·web开发·工具·代理·burpsuite·网络开发·防重放攻击
Patrick在香港10 天前
第 16,216 次连接之后,它不干了
python·网络编程·socket·tcp·连接池
vivo互联网技术11 天前
域名埋点零侵入实时校验实践:基于代理转发与规则驱动的工具设计
前端·测试·监控·代理·埋点
weixin_5716674115 天前
Windows 通过 SSH 反向隧道向 Ubuntu 转发代理
linux·运维·windows·代理
张张123y15 天前
后端第一课:输入 URL 后发生了什么?我用手写 Socket 把全链路跑通了
python·websocket·ai·socket·url·后端开发
似是燕归来17 天前
Rtthread OS 功能开发之 socket udp协议程序开发
网络·stm32·网络协议·udp·socket·rtthread
蒸鱼Yuzheng1 个月前
自动化 Socket 协议设计:消息序列、心跳、幂等重试与背压
自动化测试·python·websocket·socket·分布式系统