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

将Python Socket Server转换为Asyncio时HTTPS代理失效求助

Asyncio代理处理SSL握手超时问题的解决建议

我将基于Socket Server的多线程HTTP代理转换为基于Asyncio的单线程代理后,HTTP请求处理正常,但在处理SSL握手或执行while循环时,服务器无法交换数据,无任何报错提示,仅浏览器显示请求超时。以下是可正常运行的Socket代码和存在问题的Asyncio代码,求解决建议:

可运行的Socket代码

def proxy(host, port, conn, request):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        # If successful, send 200 code response
        s.connect((host, port))
        reply = "HTTP/1.0 200 Connection established\r\n"
        reply += "Proxy-agent: Proxy\r\n"
        reply += "\r\n"
        conn.sendall(reply.encode())
        s.setblocking(False)
        print("  HTTPS Connection Established")
        while True:
            try:
                s.sendall(conn.recv(4096))
            except socket.error as err:
                pass
            try:
                reply = s.recv(4096)
                conn.sendall(reply)
            except socket.error as e:
                s.close()
            finally:
                print("  Request of client  completed...")
                try:
                    s.close()
                except NameError: 
                    print("  Connection was already closed...")
    except socket.error as err:
        pass

无法运行的Asyncio代码

async def proxy(host, port, conn, request):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
    try:
        # If successful, send 200 code response
        s.connect((host, port))
        reply = "HTTP/1.0 200 Connection established\r\n"
        reply += "Proxy-agent: Proxy\r\n"
        reply += "\r\n"
        await loop.sock_sendall(conn, reply.encode())
        s.setblocking(False)
        print("  HTTPS Connection Established")
        while True:
            try:
                req_ = await loop.sock_recv(conn, 4096)
                await loop.sock_sendall(s, req_)
            except socket.error as err:
                s.close()
            try:
                ans_ = await loop.sock_recv(s, 4096)
                await loop.sock_sendall(conn, ans_)
            except socket.error as e:
                s.close()
            finally:
                print("  Request of client  completed...")
                try:
                    s.close()
                except NameError: 
                    print("  Connection was already closed...")
    except socket.error as err:
        pass

核心问题分析与修复方案

  1. 同步connect阻塞事件循环
    原代码用同步s.connect()会直接阻塞Asyncio事件循环,导致后续IO操作无法执行。必须先将socket设为非阻塞,再用异步连接方法await loop.sock_connect(s, (host, port))。

  2. while循环逻辑错误,过早关闭连接
    原代码的finally块在每次循环迭代都会执行s.close(),直接切断与目标服务器的连接,导致数据无法正常传输。finally块应仅在循环退出后执行,而非每次循环。

  3. 双向数据传输需并发处理
    原串行读写的方式在SSL握手时会导致阻塞,需用两个异步任务分别处理客户端→服务器、服务器→客户端的数据流,实现并发转发。

  4. 错误处理不完善
    异步IO操作抛出的异常可能是OSError或asyncio.CancelledError,而非仅socket.error;同时需判断recv返回空字节的情况(表示连接已断开)。

修改后的Asyncio代码示例

import asyncio
import socket

async def forward_data(reader_sock, writer_sock):
    try:
        while True:
            data = await asyncio.get_event_loop().sock_recv(reader_sock, 4096)
            if not data:
                break
            await asyncio.get_event_loop().sock_sendall(writer_sock, data)
    except (OSError, asyncio.CancelledError):
        pass
    finally:
        try:
            reader_sock.close()
        except:
            pass
        try:
            writer_sock.close()
        except:
            pass

async def proxy(host, port, conn, request):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setblocking(False)
    try:
        # 异步连接目标服务器
        await asyncio.get_event_loop().sock_connect(s, (host, port))
        
        # 返回连接建立响应
        reply = "HTTP/1.0 200 Connection established\r\n"
        reply += "Proxy-agent: Proxy\r\n"
        reply += "\r\n"
        await asyncio.get_event_loop().sock_sendall(conn, reply.encode())
        
        print("  HTTPS Connection Established")
        
        # 创建并发任务处理双向数据转发
        task_client_to_server = asyncio.create_task(forward_data(conn, s))
        task_server_to_client = asyncio.create_task(forward_data(s, conn))
        
        # 等待任一连接断开
        await asyncio.wait([task_client_to_server, task_server_to_client], return_when=asyncio.FIRST_COMPLETED)
        
        # 清理未完成的任务
        task_client_to_server.cancel()
        task_server_to_client.cancel()
        await asyncio.gather(task_client_to_server, task_server_to_client, return_exceptions=True)
        
        print("  Request of client completed...")
    except OSError as err:
        print(f"  Connection error: {err}")
    finally:
        try:
            s.close()
        except:
            pass
        try:
            conn.close()
        except:
            pass

额外注意事项

  • 确保客户端连接conn也是非阻塞状态,否则异步读写会出错
  • 生产环境需添加详细日志,方便排查异常
  • HTTPS代理仅需转发原始字节流,无需处理SSL握手细节,客户端会与目标服务器直接完成握手

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 08:40:56