fix: SOCKS5 隧道连接超时

修复子线程事件循环中 asyncio.wait_for 包装 open_connection 导致的读取取消问题。
改用 socket 层面的 timeout 设置,不再用 wait_for 包装。

同时修复 _reply 中 writer.write 不需要 await 的问题。
This commit is contained in:
Your Name
2026-07-16 18:50:43 +08:00
parent a65ee75894
commit 4fbb63e0b9
+13 -10
View File
@@ -4,6 +4,7 @@ import logging
import struct
import time
import uuid
import socket as _socket
import ipaddress
from datetime import datetime, timezone
@@ -127,11 +128,11 @@ class ConnectionHandler:
methods = header[2:2 + n_methods]
# 检查是否支持
if METHOD_USERPASS in methods and self.config.auth_method == "userpass":
await self.writer.write(bytes([SOCKS_VERSION, METHOD_USERPASS]))
self.writer.write(bytes([SOCKS_VERSION, METHOD_USERPASS]))
elif METHOD_NO_AUTH in methods:
await self.writer.write(bytes([SOCKS_VERSION, METHOD_NO_AUTH]))
self.writer.write(bytes([SOCKS_VERSION, METHOD_NO_AUTH]))
else:
await self.writer.write(bytes([SOCKS_VERSION, METHOD_NOT_ACCEPTED]))
self.writer.write(bytes([SOCKS_VERSION, METHOD_NOT_ACCEPTED]))
return False
await self.writer.drain()
return True
@@ -177,14 +178,14 @@ class ConnectionHandler:
log.warning("认证失败: %s (user=%s)", self.src_ip, username)
await self.user_service.log_event("auth_fail", username,
self.src_ip, "密码错误或账号不可用")
await self.writer.write(bytes([1, 0x01])) # auth fail
self.writer.write(bytes([1, 0x01])) # auth fail
await self.writer.drain()
return False
self.username = username
log.info("[%s] 认证成功: %s (from %s)", self.conn_id, username, self.src_ip)
await self.user_service.log_event("auth_success", username, self.src_ip)
await self.writer.write(bytes([1, 0x00])) # auth ok
self.writer.write(bytes([1, 0x00])) # auth ok
await self.writer.drain()
return True
@@ -273,11 +274,13 @@ class ConnectionHandler:
# 连接目标
try:
reader, writer = await asyncio.wait_for(
asyncio.open_connection(addr, port),
timeout=self.config.timeout
)
except (asyncio.TimeoutError, OSError) as e:
# 注意:不用 asyncio.wait_for 包装 open_connection
# 在子线程事件循环中 wait_for 会导致连接成功后读取被取消
sock = _socket.socket(_socket.AF_INET, _socket.SOCK_STREAM)
sock.settimeout(self.config.timeout)
sock.connect((addr, port))
reader, writer = await asyncio.open_connection(sock=sock)
except (OSError, Exception) as e:
log.warning("[%s] 无法连接 %s:%d: %s", self.conn_id, addr, port, e)
rep = REP_CONN_REFUSED if "refused" in str(e).lower() else REP_HOST_UNREACHABLE
await self._reply(rep)