将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
核心问题分析与修复方案
同步connect阻塞事件循环
原代码用同步s.connect()会直接阻塞Asyncio事件循环,导致后续IO操作无法执行。必须先将socket设为非阻塞,再用异步连接方法await loop.sock_connect(s, (host, port))。while循环逻辑错误,过早关闭连接
原代码的finally块在每次循环迭代都会执行s.close(),直接切断与目标服务器的连接,导致数据无法正常传输。finally块应仅在循环退出后执行,而非每次循环。双向数据传输需并发处理
原串行读写的方式在SSL握手时会导致阻塞,需用两个异步任务分别处理客户端→服务器、服务器→客户端的数据流,实现并发转发。错误处理不完善
异步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
相关产品推荐
相关产品推荐

