proxynt 2.0.26__tar.gz → 2.0.32__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {proxynt-2.0.26 → proxynt-2.0.32}/PKG-INFO +1 -1
- {proxynt-2.0.26 → proxynt-2.0.32}/client/tcp_forward_client.py +12 -30
- {proxynt-2.0.26 → proxynt-2.0.32}/client/udp_forward_client.py +11 -37
- {proxynt-2.0.26 → proxynt-2.0.32}/common/speed_limit.py +2 -1
- {proxynt-2.0.26 → proxynt-2.0.32}/constant/message_type_constnat.py +1 -4
- {proxynt-2.0.26 → proxynt-2.0.32}/constant/system_constant.py +1 -1
- {proxynt-2.0.26 → proxynt-2.0.32}/proxynt.egg-info/PKG-INFO +1 -1
- {proxynt-2.0.26 → proxynt-2.0.32}/proxynt.egg-info/SOURCES.txt +0 -2
- {proxynt-2.0.26 → proxynt-2.0.32}/run_client.py +18 -86
- {proxynt-2.0.26 → proxynt-2.0.32}/server/tcp_forward_client.py +8 -16
- {proxynt-2.0.26 → proxynt-2.0.32}/server/udp_forward_client.py +4 -11
- {proxynt-2.0.26 → proxynt-2.0.32}/server/websocket_handler.py +39 -195
- proxynt-2.0.26/client/data_connection_manager.py +0 -250
- proxynt-2.0.26/server/session_manager.py +0 -288
- {proxynt-2.0.26 → proxynt-2.0.32}/LICENSE +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/MANIFEST.in +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/__init__.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/client/__init__.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/client/abstract_tunnel.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/client/clear_nonce_task.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/client/heart_beat_task.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/client/kcp_tunnel_impl.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/client/n4_tunnel_manager.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/client/quic_tunnel_impl.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/client/tunnel_protocol.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/__init__.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/cert_utils.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/crypto/__init__.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/crypto/table.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/encrypt_utils.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/kcp.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/logger_factory.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/n4_protocol.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/n4_punch.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/nat_serialization.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/pool.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/register_append_data.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/websocket/__init__.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/websocket/_abnf.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/websocket/_app.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/websocket/_cookiejar.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/websocket/_core.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/websocket/_exceptions.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/websocket/_handshake.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/websocket/_http.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/websocket/_logging.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/websocket/_socket.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/websocket/_ssl_compat.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/websocket/_url.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/websocket/_utils.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/websocket/_wsdump.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/websocket/tests/__init__.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/websocket/tests/echo-server.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/websocket/tests/test_abnf.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/websocket/tests/test_app.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/websocket/tests/test_cookiejar.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/websocket/tests/test_http.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/websocket/tests/test_url.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/common/websocket/tests/test_websocket.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/constant/__init__.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/context/__init__.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/context/context_utils.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/entity/__init__.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/entity/client_config_entity.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/entity/message/__init__.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/entity/message/message_entity.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/entity/message/push_config_entity.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/entity/message/tcp_over_websocket_message.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/entity/server_config_entity.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/exceptions/__init__.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/exceptions/duplicated_name.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/exceptions/invalid_password.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/exceptions/replay_error.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/exceptions/signature_error.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/p2ptest/__init__.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/p2ptest/client.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/p2ptest/n4.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/proxynt.egg-info/dependency_links.txt +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/proxynt.egg-info/entry_points.txt +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/proxynt.egg-info/requires.txt +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/proxynt.egg-info/top_level.txt +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/run_server.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/server/__init__.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/server/admin_http_handler.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/server/n4.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/server/n4_signal_service.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/server/task/__init__.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/server/task/check_cookie_task.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/server/task/clear_nonce_task.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/server/task/heart_beat_task.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/server/template/__init__.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/server/template/base.html +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/server/template/css/fonts/element-icons.woff +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/server/template/css/index.css +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/server/template/ele_index.html +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/server/template/js/axios.min.js +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/server/template/js/index.js +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/server/template/js/vue.min.js +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/server/template/login.html +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/setup.cfg +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/setup.py +0 -0
- {proxynt-2.0.26 → proxynt-2.0.32}/test_exchange.py +0 -0
|
@@ -146,9 +146,6 @@ class TcpForwardClient:
|
|
|
146
146
|
# N4 Tunnel Manager for P2P data transfer
|
|
147
147
|
self.tunnel_manager = None
|
|
148
148
|
|
|
149
|
-
# 多连接支持:DataConnectionManager(由 run_client 注入)
|
|
150
|
-
self.data_conn_manager = None
|
|
151
|
-
|
|
152
149
|
# Pending data buffer: uid -> list of (data, timestamp)
|
|
153
150
|
# Used to buffer P2P data that arrives before connection is established
|
|
154
151
|
self.pending_data: Dict[bytes, list] = {}
|
|
@@ -303,16 +300,22 @@ class TcpForwardClient:
|
|
|
303
300
|
except OSError:
|
|
304
301
|
recv = b''
|
|
305
302
|
|
|
303
|
+
# --- 发送端限速:在发送前等待 ---
|
|
304
|
+
if data.speed_limiter and recv:
|
|
305
|
+
wait_time = data.speed_limiter.acquire(len(recv))
|
|
306
|
+
if wait_time > 0:
|
|
307
|
+
time.sleep(wait_time)
|
|
308
|
+
|
|
306
309
|
# --- 无缝切换逻辑 ---
|
|
307
310
|
if self.tunnel_manager:
|
|
308
311
|
# 尝试走 P2P 隧道
|
|
309
312
|
if self.tunnel_manager.send_data(connection.uid, recv):
|
|
310
313
|
# 如果发送成功返回 True,逻辑结束
|
|
311
|
-
if not recv:
|
|
314
|
+
if not recv: # 本地连接关闭,通知隧道
|
|
312
315
|
self.close_connection(each)
|
|
313
316
|
return
|
|
314
317
|
|
|
315
|
-
|
|
318
|
+
# --- 回退/初始逻辑:走 WebSocket ---
|
|
316
319
|
send_message: MessageEntity = {
|
|
317
320
|
'type_': MessageTypeConstant.WEBSOCKET_OVER_TCP,
|
|
318
321
|
'data': {
|
|
@@ -323,21 +326,9 @@ class TcpForwardClient:
|
|
|
323
326
|
}
|
|
324
327
|
}
|
|
325
328
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
if wait_time > 0:
|
|
330
|
-
time.sleep(wait_time)
|
|
331
|
-
|
|
332
|
-
# 多连接模式:优先使用数据连接
|
|
333
|
-
message_bytes = NatSerialization.dumps(send_message, ContextUtils.get_password(), self.compress_support)
|
|
334
|
-
if self.data_conn_manager and self.data_conn_manager.is_ready():
|
|
335
|
-
if not self.data_conn_manager.send_by_uid(connection.uid, message_bytes):
|
|
336
|
-
# 数据连接发送失败,回退到控制连接
|
|
337
|
-
connection.sender.enqueue_message(message_bytes)
|
|
338
|
-
else:
|
|
339
|
-
# 单连接模式:使用控制连接
|
|
340
|
-
connection.sender.enqueue_message(message_bytes)
|
|
329
|
+
connection.sender.enqueue_message(
|
|
330
|
+
NatSerialization.dumps(send_message, ContextUtils.get_password(), self.compress_support)
|
|
331
|
+
)
|
|
341
332
|
|
|
342
333
|
if not recv:
|
|
343
334
|
try:
|
|
@@ -496,16 +487,7 @@ class TcpForwardClient:
|
|
|
496
487
|
}
|
|
497
488
|
}
|
|
498
489
|
start_time = time.time()
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
# 多连接模式:优先使用数据连接
|
|
502
|
-
if self.data_conn_manager and self.data_conn_manager.is_ready():
|
|
503
|
-
if not self.data_conn_manager.send_by_uid(connection.uid, message_bytes):
|
|
504
|
-
# 数据连接发送失败,回退到控制连接
|
|
505
|
-
self.ws.send(message_bytes, websocket.ABNF.OPCODE_BINARY)
|
|
506
|
-
else:
|
|
507
|
-
# 单连接模式
|
|
508
|
-
self.ws.send(message_bytes, websocket.ABNF.OPCODE_BINARY)
|
|
490
|
+
self.ws.send(NatSerialization.dumps(send_message, ContextUtils.get_password(), self.compress_support), websocket.ABNF.OPCODE_BINARY)
|
|
509
491
|
LoggerFactory.get_logger().debug(f'Send to websocket cost time {time.time() - start_time}')
|
|
510
492
|
|
|
511
493
|
def send_by_uid(self, uid: bytes, msg: bytes):
|
|
@@ -53,9 +53,6 @@ class UdpForwardClient:
|
|
|
53
53
|
self.c2c_listeners: Dict[str, socket.socket] = {} # rule_name → listener socket
|
|
54
54
|
self.c2c_uid_to_rule: Dict[bytes, str] = {} # UID → rule_name
|
|
55
55
|
|
|
56
|
-
# 多连接支持:DataConnectionManager(由 run_client 注入)
|
|
57
|
-
self.data_conn_manager = None
|
|
58
|
-
|
|
59
56
|
def set_running(self, running: bool):
|
|
60
57
|
"""Set running state"""
|
|
61
58
|
self.running = running
|
|
@@ -134,9 +131,6 @@ class UdpForwardClient:
|
|
|
134
131
|
protocol = rule['protocol']
|
|
135
132
|
speed_limit = rule.get('speed_limit', 0.0)
|
|
136
133
|
|
|
137
|
-
# 创建限速器
|
|
138
|
-
speed_limiter = SpeedLimiter(speed_limit) if speed_limit > 0 else None
|
|
139
|
-
|
|
140
134
|
# Check if using direct mode (target_ip + target_port) or service mode (target_service)
|
|
141
135
|
use_direct_mode = 'target_ip' in rule and 'target_port' in rule
|
|
142
136
|
|
|
@@ -211,22 +205,10 @@ class UdpForwardClient:
|
|
|
211
205
|
'ip_port': f"{source_addr[0]}:{source_addr[1]}"
|
|
212
206
|
}
|
|
213
207
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if wait_time > 0:
|
|
219
|
-
time.sleep(wait_time)
|
|
220
|
-
|
|
221
|
-
message_bytes = NatSerialization.dumps(send_message, ContextUtils.get_password(), self.compress_support)
|
|
222
|
-
# 多连接模式:优先使用数据连接
|
|
223
|
-
if self.data_conn_manager and self.data_conn_manager.is_ready():
|
|
224
|
-
if not self.data_conn_manager.send_by_uid(uid, message_bytes):
|
|
225
|
-
# 数据连接发送失败,回退到控制连接
|
|
226
|
-
self.ws.send(message_bytes, websocket.ABNF.OPCODE_BINARY)
|
|
227
|
-
else:
|
|
228
|
-
# 单连接模式
|
|
229
|
-
self.ws.send(message_bytes, websocket.ABNF.OPCODE_BINARY)
|
|
208
|
+
self.ws.send(
|
|
209
|
+
NatSerialization.dumps(send_message, ContextUtils.get_password(), self.compress_support),
|
|
210
|
+
websocket.ABNF.OPCODE_BINARY
|
|
211
|
+
)
|
|
230
212
|
LoggerFactory.get_logger().debug(f'C2C UDP data forwarded: {rule_name} UID: {uid.hex()}, len: {len(data)}')
|
|
231
213
|
|
|
232
214
|
except OSError as e:
|
|
@@ -264,6 +246,12 @@ class UdpForwardClient:
|
|
|
264
246
|
|
|
265
247
|
def _handle_udp_data(self, conn: UdpSocketConnection, data: bytes, addr):
|
|
266
248
|
"""Handle received UDP data"""
|
|
249
|
+
# --- 发送端限速:在发送前等待 ---
|
|
250
|
+
if conn.speed_limiter and data:
|
|
251
|
+
wait_time = conn.speed_limiter.acquire(len(data))
|
|
252
|
+
if wait_time > 0:
|
|
253
|
+
time.sleep(wait_time)
|
|
254
|
+
|
|
267
255
|
# Construct UDP message and send to public server via WebSocket
|
|
268
256
|
send_message: MessageEntity = {
|
|
269
257
|
'type_': MessageTypeConstant.WEBSOCKET_OVER_UDP,
|
|
@@ -279,21 +267,7 @@ class UdpForwardClient:
|
|
|
279
267
|
LoggerFactory.get_logger().debug(f'Sending UDP to WebSocket, uid: {conn.uid}, len: {len(data)}')
|
|
280
268
|
|
|
281
269
|
try:
|
|
282
|
-
|
|
283
|
-
if conn.speed_limiter and data:
|
|
284
|
-
wait_time = conn.speed_limiter.acquire(len(data))
|
|
285
|
-
if wait_time > 0:
|
|
286
|
-
time.sleep(wait_time)
|
|
287
|
-
|
|
288
|
-
message_bytes = NatSerialization.dumps(send_message, ContextUtils.get_password(), self.compress_support)
|
|
289
|
-
# 多连接模式:优先使用数据连接
|
|
290
|
-
if self.data_conn_manager and self.data_conn_manager.is_ready():
|
|
291
|
-
if not self.data_conn_manager.send_by_uid(conn.uid, message_bytes):
|
|
292
|
-
# 数据连接发送失败,回退到控制连接
|
|
293
|
-
self.ws.send(message_bytes, websocket.ABNF.OPCODE_BINARY)
|
|
294
|
-
else:
|
|
295
|
-
# 单连接模式
|
|
296
|
-
self.ws.send(message_bytes, websocket.ABNF.OPCODE_BINARY)
|
|
270
|
+
self.ws.send(NatSerialization.dumps(send_message, ContextUtils.get_password(), self.compress_support), websocket.ABNF.OPCODE_BINARY)
|
|
297
271
|
except Exception as e:
|
|
298
272
|
LoggerFactory.get_logger().error(f"Failed to send UDP to WebSocket: {e}")
|
|
299
273
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import time
|
|
2
2
|
import threading
|
|
3
|
+
from typing import Tuple
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
class SpeedLimiter:
|
|
@@ -75,7 +76,7 @@ class SpeedLimiter:
|
|
|
75
76
|
"""兼容旧接口,等同于 acquire 但不返回等待时间"""
|
|
76
77
|
self.acquire(data_len)
|
|
77
78
|
|
|
78
|
-
def is_exceed(self):
|
|
79
|
+
def is_exceed(self) -> Tuple[bool, float]:
|
|
79
80
|
"""兼容旧接口,返回 (是否超速, 剩余量)"""
|
|
80
81
|
if self.max_speed <= 0:
|
|
81
82
|
return False, 0
|
|
@@ -28,7 +28,4 @@ class MessageTypeConstant:
|
|
|
28
28
|
P2P_PRE_CONNECT = 'g'
|
|
29
29
|
P2P_EXCHANGE = 'h' # UDP exchange to establish NAT mapping
|
|
30
30
|
P2P_PEER_INFO = 'i' # Server sends peer's actual UDP port
|
|
31
|
-
P2P_PUNCH_REQUEST = 'j' # Request to initiate P2P hole punching
|
|
32
|
-
|
|
33
|
-
# 多连接支持
|
|
34
|
-
JOIN_SESSION = 'k' # 数据连接加入会话(携带 session_token)
|
|
31
|
+
P2P_PUNCH_REQUEST = 'j' # Request to initiate P2P hole punching
|
|
@@ -8,7 +8,6 @@ setup.py
|
|
|
8
8
|
./client/__init__.py
|
|
9
9
|
./client/abstract_tunnel.py
|
|
10
10
|
./client/clear_nonce_task.py
|
|
11
|
-
./client/data_connection_manager.py
|
|
12
11
|
./client/heart_beat_task.py
|
|
13
12
|
./client/kcp_tunnel_impl.py
|
|
14
13
|
./client/n4_tunnel_manager.py
|
|
@@ -75,7 +74,6 @@ setup.py
|
|
|
75
74
|
./server/admin_http_handler.py
|
|
76
75
|
./server/n4.py
|
|
77
76
|
./server/n4_signal_service.py
|
|
78
|
-
./server/session_manager.py
|
|
79
77
|
./server/tcp_forward_client.py
|
|
80
78
|
./server/udp_forward_client.py
|
|
81
79
|
./server/websocket_handler.py
|
|
@@ -49,7 +49,6 @@ from client.udp_forward_client import UdpForwardClient
|
|
|
49
49
|
from client.heart_beat_task import HeatBeatTask
|
|
50
50
|
from client.tcp_forward_client import TcpForwardClient
|
|
51
51
|
from client.n4_tunnel_manager import N4TunnelManager, get_pair_key
|
|
52
|
-
from client.data_connection_manager import DataConnectionManager
|
|
53
52
|
from common import websocket
|
|
54
53
|
from common.logger_factory import LoggerFactory
|
|
55
54
|
from common.nat_serialization import NatSerialization
|
|
@@ -152,19 +151,6 @@ class WebsocketClient:
|
|
|
152
151
|
self.public_ip: str = None
|
|
153
152
|
self.public_port: int = None
|
|
154
153
|
|
|
155
|
-
# 多连接支持
|
|
156
|
-
self.multi_connection_enabled: bool = config_data.get('multi_connection', True)
|
|
157
|
-
self.num_data_channels: int = config_data.get('num_data_channels', 4)
|
|
158
|
-
server_url = config_data['server']['url']
|
|
159
|
-
if self.compress_support:
|
|
160
|
-
sep = '&' if '?' in server_url else '?'
|
|
161
|
-
server_url += sep + 'c=' + json.dumps(self.compress_support)
|
|
162
|
-
self.data_conn_manager: DataConnectionManager = DataConnectionManager(
|
|
163
|
-
server_url=server_url,
|
|
164
|
-
compress_support=self.compress_support,
|
|
165
|
-
on_message_callback=self._on_data_connection_message
|
|
166
|
-
)
|
|
167
|
-
|
|
168
154
|
# Get server info for N4 signaling
|
|
169
155
|
server_url = config_data['server']['url']
|
|
170
156
|
parsed = urlparse(server_url)
|
|
@@ -223,7 +209,14 @@ class WebsocketClient:
|
|
|
223
209
|
self.tunnel_manager.register_uid(uid, source_client)
|
|
224
210
|
LoggerFactory.get_logger().info(f'Registered UID {uid.hex()} to peer {source_client}')
|
|
225
211
|
|
|
226
|
-
|
|
212
|
+
# 优先使用消息中携带的限速配置(C2C场景),否则使用本地配置
|
|
213
|
+
speed_limit = data.get('speed_limit', 0.0)
|
|
214
|
+
if speed_limit > 0:
|
|
215
|
+
speed_limiter = SpeedLimiter(speed_limit)
|
|
216
|
+
else:
|
|
217
|
+
speed_limiter = name_to_speed_limiter.get(name)
|
|
218
|
+
|
|
219
|
+
self.forward_client.create_socket(name, uid, data['ip_port'], speed_limiter)
|
|
227
220
|
|
|
228
221
|
# Handle UDP messages
|
|
229
222
|
elif msg_type == MessageTypeConstant.WEBSOCKET_OVER_UDP:
|
|
@@ -240,7 +233,15 @@ class WebsocketClient:
|
|
|
240
233
|
data: TcpOverWebsocketMessage = message_data['data']
|
|
241
234
|
uid = data['uid']
|
|
242
235
|
name = data['name']
|
|
243
|
-
|
|
236
|
+
|
|
237
|
+
# 优先使用消息中携带的限速配置(C2C场景),否则使用本地配置
|
|
238
|
+
speed_limit = data.get('speed_limit', 0.0)
|
|
239
|
+
if speed_limit > 0:
|
|
240
|
+
speed_limiter = SpeedLimiter(speed_limit)
|
|
241
|
+
else:
|
|
242
|
+
speed_limiter = name_to_speed_limiter.get(name)
|
|
243
|
+
|
|
244
|
+
self.udp_forward_client.create_udp_socket(name, uid, data['ip_port'], speed_limiter)
|
|
244
245
|
|
|
245
246
|
# Handle Heartbeat / Config
|
|
246
247
|
elif msg_type == MessageTypeConstant.PING:
|
|
@@ -254,20 +255,6 @@ class WebsocketClient:
|
|
|
254
255
|
if d.get('speed_limit'):
|
|
255
256
|
name_to_speed_limiter[d['name']] = SpeedLimiter(d['speed_limit'])
|
|
256
257
|
|
|
257
|
-
# 多连接支持:如果服务端返回了 session_token,建立数据连接
|
|
258
|
-
session_token = push_config.get('session_token')
|
|
259
|
-
if session_token and self.multi_connection_enabled:
|
|
260
|
-
LoggerFactory.get_logger().info(
|
|
261
|
-
f'Multi-connection enabled, establishing {self.num_data_channels} data connections'
|
|
262
|
-
)
|
|
263
|
-
self.data_conn_manager.setup_data_connections(
|
|
264
|
-
session_token=session_token,
|
|
265
|
-
num_channels=self.num_data_channels
|
|
266
|
-
)
|
|
267
|
-
# 将 DataConnectionManager 注入到 forward_client
|
|
268
|
-
self.forward_client.data_conn_manager = self.data_conn_manager
|
|
269
|
-
self.udp_forward_client.data_conn_manager = self.data_conn_manager
|
|
270
|
-
|
|
271
258
|
# Setup Listeners
|
|
272
259
|
c2c_rules = push_config.get('client_to_client_rules', [])
|
|
273
260
|
if c2c_rules:
|
|
@@ -360,52 +347,6 @@ class WebsocketClient:
|
|
|
360
347
|
# Start hole punching with peer info
|
|
361
348
|
self.tunnel_manager.receive_peer_info(peer_name, peer_ip, peer_port)
|
|
362
349
|
|
|
363
|
-
def _on_data_connection_message(self, message_data: MessageEntity):
|
|
364
|
-
"""处理数据连接收到的消息(和控制连接消息处理逻辑相同)"""
|
|
365
|
-
try:
|
|
366
|
-
msg_type = message_data['type_']
|
|
367
|
-
|
|
368
|
-
# Handle TCP messages
|
|
369
|
-
if msg_type == MessageTypeConstant.WEBSOCKET_OVER_TCP:
|
|
370
|
-
data: TcpOverWebsocketMessage = message_data['data']
|
|
371
|
-
uid = data['uid']
|
|
372
|
-
name = data['name']
|
|
373
|
-
b = data['data']
|
|
374
|
-
|
|
375
|
-
create_result = self.forward_client.create_socket(name, uid, data['ip_port'], name_to_speed_limiter.get(name))
|
|
376
|
-
if create_result:
|
|
377
|
-
self.forward_client.send_by_uid(uid, b)
|
|
378
|
-
|
|
379
|
-
# Handle UDP messages
|
|
380
|
-
elif msg_type == MessageTypeConstant.WEBSOCKET_OVER_UDP:
|
|
381
|
-
data: TcpOverWebsocketMessage = message_data['data']
|
|
382
|
-
uid = data['uid']
|
|
383
|
-
name = data['name']
|
|
384
|
-
b = data['data']
|
|
385
|
-
create_result = self.udp_forward_client.create_udp_socket(name, uid, data['ip_port'], name_to_speed_limiter.get(name))
|
|
386
|
-
if create_result:
|
|
387
|
-
self.udp_forward_client.send_by_uid(uid, b)
|
|
388
|
-
|
|
389
|
-
# Handle connection confirmed/failed (for C2C)
|
|
390
|
-
elif msg_type == MessageTypeConstant.CONNECT_CONFIRMED:
|
|
391
|
-
data: TcpOverWebsocketMessage = message_data['data']
|
|
392
|
-
uid = data['uid']
|
|
393
|
-
# C2C 连接确认,暂无特殊处理
|
|
394
|
-
LoggerFactory.get_logger().debug(f'C2C connection confirmed: {uid.hex()}')
|
|
395
|
-
|
|
396
|
-
elif msg_type == MessageTypeConstant.CONNECT_FAILED:
|
|
397
|
-
data: TcpOverWebsocketMessage = message_data['data']
|
|
398
|
-
uid = data['uid']
|
|
399
|
-
LoggerFactory.get_logger().info(f'C2C connection failed: {uid.hex()}')
|
|
400
|
-
# 关闭本地连接
|
|
401
|
-
conn = self.forward_client.uid_to_socket_connection.get(uid)
|
|
402
|
-
if conn:
|
|
403
|
-
self.forward_client.close_connection(conn.socket)
|
|
404
|
-
|
|
405
|
-
except Exception as e:
|
|
406
|
-
LoggerFactory.get_logger().error(f'Data connection message handling error: {e}')
|
|
407
|
-
LoggerFactory.get_logger().error(traceback.format_exc())
|
|
408
|
-
|
|
409
350
|
def _on_p2p_data_received(self, uid: bytes, data: bytes):
|
|
410
351
|
"""Callback when data comes from P2P Tunnel"""
|
|
411
352
|
self.forward_client.send_by_uid(uid, data)
|
|
@@ -471,10 +412,6 @@ class WebsocketClient:
|
|
|
471
412
|
self.forward_client.close()
|
|
472
413
|
self.udp_forward_client.close()
|
|
473
414
|
|
|
474
|
-
# Stop data connections (will be re-established after PUSH_CONFIG response)
|
|
475
|
-
if self.data_conn_manager:
|
|
476
|
-
self.data_conn_manager.stop_all()
|
|
477
|
-
|
|
478
415
|
# Restart tunnel manager
|
|
479
416
|
if self.tunnel_manager:
|
|
480
417
|
self.tunnel_manager.stop()
|
|
@@ -496,9 +433,7 @@ class WebsocketClient:
|
|
|
496
433
|
'config_list': push_client_data,
|
|
497
434
|
"client_name": client_name,
|
|
498
435
|
'version': SystemConstant.VERSION,
|
|
499
|
-
'p2p_supported': True
|
|
500
|
-
'multi_connection': self.multi_connection_enabled,
|
|
501
|
-
'num_data_channels': self.num_data_channels
|
|
436
|
+
'p2p_supported': True
|
|
502
437
|
}
|
|
503
438
|
message: MessageEntity = {
|
|
504
439
|
'type_': MessageTypeConstant.PUSH_CONFIG,
|
|
@@ -522,9 +457,6 @@ class WebsocketClient:
|
|
|
522
457
|
LoggerFactory.get_logger().info(f'WS Closed: {a}, {b}')
|
|
523
458
|
self.heart_beat_task.is_running = False
|
|
524
459
|
self.forward_client.close()
|
|
525
|
-
# Stop data connections
|
|
526
|
-
if self.data_conn_manager:
|
|
527
|
-
self.data_conn_manager.stop_all()
|
|
528
460
|
# Stop tunnel manager
|
|
529
461
|
if self.tunnel_manager:
|
|
530
462
|
self.tunnel_manager.stop()
|
|
@@ -21,7 +21,6 @@ from common.speed_limit import SpeedLimiter
|
|
|
21
21
|
from constant.message_type_constnat import MessageTypeConstant
|
|
22
22
|
from context.context_utils import ContextUtils
|
|
23
23
|
from entity.message.message_entity import MessageEntity
|
|
24
|
-
from server.session_manager import SessionManager
|
|
25
24
|
|
|
26
25
|
|
|
27
26
|
class PublicSocketServer:
|
|
@@ -151,6 +150,12 @@ class TcpForwardClient:
|
|
|
151
150
|
LoggerFactory.get_logger().error(f'Close error: {traceback.format_exc()}')
|
|
152
151
|
return
|
|
153
152
|
|
|
153
|
+
# --- 发送端限速:在发送前等待 ---
|
|
154
|
+
if data.speed_limiter and recv:
|
|
155
|
+
wait_time = data.speed_limiter.acquire(len(recv))
|
|
156
|
+
if wait_time > 0:
|
|
157
|
+
time.sleep(wait_time)
|
|
158
|
+
|
|
154
159
|
if LoggerFactory.get_logger().isEnabledFor(logging.DEBUG):
|
|
155
160
|
LoggerFactory.get_logger().debug(f'send to ws uid: {socket_connection.uid}, len: {len(recv)}')
|
|
156
161
|
send_message: MessageEntity = {
|
|
@@ -169,22 +174,9 @@ class TcpForwardClient:
|
|
|
169
174
|
except (OSError, ValueError, KeyError):
|
|
170
175
|
LoggerFactory.get_logger().error(f'close error: {traceback.format_exc()}')
|
|
171
176
|
try:
|
|
172
|
-
|
|
173
|
-
if data.speed_limiter and recv:
|
|
174
|
-
wait_time = data.speed_limiter.acquire(len(recv))
|
|
175
|
-
if wait_time > 0:
|
|
176
|
-
time.sleep(wait_time)
|
|
177
|
-
|
|
178
|
-
# 使用多连接:根据 UID 选择数据通道
|
|
179
|
-
control_handler = socket_connection.socket_server.websocket_handler
|
|
180
|
-
session = SessionManager.get_instance().get_session_by_handler(control_handler)
|
|
181
|
-
if session:
|
|
182
|
-
target_handler = session.get_data_handler(socket_connection.uid)
|
|
183
|
-
else:
|
|
184
|
-
target_handler = control_handler
|
|
185
|
-
is_compress = target_handler.compress_support
|
|
177
|
+
is_compress = socket_connection.socket_server.websocket_handler.compress_support
|
|
186
178
|
self.tornado_loop.add_callback(
|
|
187
|
-
partial(
|
|
179
|
+
partial(socket_connection.socket_server.websocket_handler.write_message, NatSerialization.dumps(send_message, ContextUtils.get_password(), is_compress)), True)
|
|
188
180
|
except Exception:
|
|
189
181
|
LoggerFactory.get_logger().error(traceback.format_exc())
|
|
190
182
|
|
|
@@ -17,7 +17,6 @@ from common.speed_limit import SpeedLimiter
|
|
|
17
17
|
from constant.message_type_constnat import MessageTypeConstant
|
|
18
18
|
from context.context_utils import ContextUtils
|
|
19
19
|
from entity.message.message_entity import MessageEntity
|
|
20
|
-
from server.session_manager import SessionManager
|
|
21
20
|
|
|
22
21
|
|
|
23
22
|
class UdpEndpoint:
|
|
@@ -204,7 +203,7 @@ class UdpForwardClient:
|
|
|
204
203
|
|
|
205
204
|
uid = server.add_endpoint(address)
|
|
206
205
|
|
|
207
|
-
# 发送端限速:在发送前等待
|
|
206
|
+
# --- 发送端限速:在发送前等待 ---
|
|
208
207
|
if server.speed_limiter and data:
|
|
209
208
|
wait_time = server.speed_limiter.acquire(len(data))
|
|
210
209
|
if wait_time > 0:
|
|
@@ -224,16 +223,10 @@ class UdpForwardClient:
|
|
|
224
223
|
if LoggerFactory.get_logger().isEnabledFor(logging.DEBUG):
|
|
225
224
|
LoggerFactory.get_logger().debug(f"Send UDP data to WebSocket, uid: {uid}, len: {len(data)}")
|
|
226
225
|
|
|
227
|
-
#
|
|
228
|
-
|
|
229
|
-
session = SessionManager.get_instance().get_session_by_handler(control_handler)
|
|
230
|
-
if session:
|
|
231
|
-
target_handler = session.get_data_handler(uid)
|
|
232
|
-
else:
|
|
233
|
-
target_handler = control_handler
|
|
234
|
-
is_compress = target_handler.compress_support
|
|
226
|
+
# Schedule WebSocket send operation in the async loop
|
|
227
|
+
is_compress = server.websocket_handler.compress_support
|
|
235
228
|
self.tornado_loop.add_callback(
|
|
236
|
-
partial(
|
|
229
|
+
partial(server.websocket_handler.write_message, NatSerialization.dumps(send_message, ContextUtils.get_password(), is_compress)), True)
|
|
237
230
|
|
|
238
231
|
async def register_udp_server(self, port: int, name: str, ip_port: str, websocket_handler, speed_limit_size: float):
|
|
239
232
|
"""Register a UDP server"""
|