dpmp 0.1.1__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.
- dpmp-0.1.1/LICENSE +201 -0
- dpmp-0.1.1/PKG-INFO +415 -0
- dpmp-0.1.1/README.md +390 -0
- dpmp-0.1.1/dpmp/__init__.py +27 -0
- dpmp-0.1.1/dpmp/config.py +74 -0
- dpmp-0.1.1/dpmp/defaults.py +62 -0
- dpmp-0.1.1/dpmp/link/__init__.py +27 -0
- dpmp-0.1.1/dpmp/link/discovery.py +376 -0
- dpmp-0.1.1/dpmp/link/manager.py +887 -0
- dpmp-0.1.1/dpmp/link/natprobe.py +78 -0
- dpmp-0.1.1/dpmp/link/path.py +180 -0
- dpmp-0.1.1/dpmp/link/puncher.py +253 -0
- dpmp-0.1.1/dpmp/link/rtp.py +521 -0
- dpmp-0.1.1/dpmp/link/signaling.py +641 -0
- dpmp-0.1.1/dpmp/protocol/__init__.py +7 -0
- dpmp-0.1.1/dpmp/protocol/addr.py +37 -0
- dpmp-0.1.1/dpmp/protocol/codec.py +89 -0
- dpmp-0.1.1/dpmp/protocol/constants.py +140 -0
- dpmp-0.1.1/dpmp/stream/__init__.py +13 -0
- dpmp-0.1.1/dpmp/stream/channel.py +62 -0
- dpmp-0.1.1/dpmp/util/__init__.py +2 -0
- dpmp-0.1.1/dpmp/util/net.py +115 -0
- dpmp-0.1.1/dpmp.egg-info/PKG-INFO +415 -0
- dpmp-0.1.1/dpmp.egg-info/SOURCES.txt +26 -0
- dpmp-0.1.1/dpmp.egg-info/dependency_links.txt +1 -0
- dpmp-0.1.1/dpmp.egg-info/top_level.txt +1 -0
- dpmp-0.1.1/pyproject.toml +36 -0
- dpmp-0.1.1/setup.cfg +4 -0
dpmp-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
dpmp-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dpmp
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: DPMP - Dual-Punch Multi-Path Protocol: P2P connectivity & transport library
|
|
5
|
+
Author: DPMP
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/yourname/dpmp
|
|
8
|
+
Keywords: p2p,nat-traversal,hole-punching,multi-path,udp,tcp,peer-to-peer,nat
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Internet
|
|
20
|
+
Classifier: Topic :: System :: Networking
|
|
21
|
+
Requires-Python: >=3.7
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# DPMP · Dual-Punch Multi-Path Protocol
|
|
27
|
+
|
|
28
|
+
**双打洞多路径协议** —— 一个 P2P 连接与传输底层库。
|
|
29
|
+
|
|
30
|
+
DPMP 只做一件事:**把字节可靠地从 A 送到 B**。
|
|
31
|
+
你提供「服务器 + 房间号」,它返回「一个可收发 `bytes` 的通道」;
|
|
32
|
+
至于这些字节是文件、视频帧还是游戏消息 —— 由你的上层定义。
|
|
33
|
+
|
|
34
|
+
适用场景:文件互传、远程控制、投屏、游戏房间等一切需要 P2P 的软件底座。
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 核心能力(两项独创)
|
|
39
|
+
|
|
40
|
+
1. **双打洞协同(Dual-Punch)**
|
|
41
|
+
先通过 UDP 打洞建立可靠控制通道(UDP-RTP),再用该通道作为**控制面**
|
|
42
|
+
协调 TCP 打洞(轮次对齐、候选并行、就绪信号、失败通知)。
|
|
43
|
+
控制面与数据面分离,显著提升 TCP 打洞成功率。
|
|
44
|
+
|
|
45
|
+
2. **梯度冗余多路径(Multi-Path)**
|
|
46
|
+
同一对端维护多条路径,按 **hot / warm_safe / warm_loose** 三级角色分级调度:
|
|
47
|
+
热备承载数据、保守暖备待命、宽松暖备兜底。热备断线自动接力、换路续传。
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 分层架构
|
|
52
|
+
|
|
53
|
+
| 层 | 模块 | 职责 |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| 协议 | `dpmp.protocol` | 线格式常量与编解码(两端唯一事实来源,不可配) |
|
|
56
|
+
| 连接 | `dpmp.link` | 发现 / 信令 / 双打洞 / UDP-RTP / 路径调度 / 连接管理 |
|
|
57
|
+
| 传输 | `dpmp.stream` | 统一字节流通道(屏蔽 TCP / UDP-RTP 差异) |
|
|
58
|
+
| 配置 | `dpmp.config` | 调优参数(全可配) |
|
|
59
|
+
| 默认 | `dpmp.defaults` | 默认信令服务器(便利入口,可覆盖) |
|
|
60
|
+
| 工具 | `dpmp.util` | 本机 IP / 子网 / 广播地址 / 设备标识 |
|
|
61
|
+
|
|
62
|
+
依赖:**纯标准库,零第三方**。
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 安装
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pip install -e .
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 快速开始
|
|
75
|
+
|
|
76
|
+
### 1. 公网模式(异地互传)
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from dpmp.link.signaling import SignalingClient
|
|
80
|
+
from dpmp.link.manager import LinkManager
|
|
81
|
+
|
|
82
|
+
# ① 信令客户端(不传服务器则用内置默认;见「默认服务器」一节)
|
|
83
|
+
sig = SignalingClient(
|
|
84
|
+
servers=["your.server.com:3336", "backup.com:3336"], # 多服务器故障转移
|
|
85
|
+
room="my_room", # 房间号,两端填同一个
|
|
86
|
+
name="my_pc", # 显示名
|
|
87
|
+
tcp_port=9998, # 本机打洞 / 长连接端口
|
|
88
|
+
lan_ips=[], # 本机局域网 IP(可空)
|
|
89
|
+
device_id="dev-001", # 稳定设备标识(去重用)
|
|
90
|
+
punch_local_port=9998, # 打洞本地端口
|
|
91
|
+
udp_hole_port=9996, # UDP 打洞端口
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# ② 连接管理(打洞 + 多路径 + 保活)
|
|
95
|
+
lm = LinkManager(sig, local_tcp_port=9998)
|
|
96
|
+
|
|
97
|
+
# ③ 把信令事件接到连接管理(必须)
|
|
98
|
+
sig.on_joined = lm.on_joined
|
|
99
|
+
sig.on_member_join = lm.on_member_join
|
|
100
|
+
sig.on_member_leave = lm.on_member_leave
|
|
101
|
+
sig.on_punch_go = lm.on_punch_go
|
|
102
|
+
sig.on_udp_hole_ready = lm.on_udp_hole_ready
|
|
103
|
+
sig.on_mapping_ready = lm.mark_mapping_ready
|
|
104
|
+
|
|
105
|
+
# ④ 连接就绪回调 —— 在这里跑你自己的协议
|
|
106
|
+
def on_ready(peer_id, sock, member):
|
|
107
|
+
sock.sendall(b"hello") # 发
|
|
108
|
+
data = sock.recv(4096) # 收
|
|
109
|
+
lm.on_socket_ready = on_ready
|
|
110
|
+
|
|
111
|
+
# ⑤ 启动
|
|
112
|
+
lm.start()
|
|
113
|
+
sig.start() # 依次尝试 servers,第一个成功即停
|
|
114
|
+
|
|
115
|
+
# ⑥ 网络切换时重建(宿主检测到后调用,见「网络切换重建」一节)
|
|
116
|
+
# 示例:Android 在 NetworkCallback 里、桌面在 IP 轮询里调用
|
|
117
|
+
def on_network_changed():
|
|
118
|
+
lm.on_network_changed() # 关闭旧连接 + 信令重绑 + 重新打洞(复用身份)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### 2. 局域网模式(**无需服务器**)
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
from dpmp.link.discovery import Discovery
|
|
125
|
+
|
|
126
|
+
d = Discovery(
|
|
127
|
+
device_id="dev-001", hostname="my-pc",
|
|
128
|
+
on_new_node=lambda ip, msg: print("发现设备", ip),
|
|
129
|
+
)
|
|
130
|
+
d.start()
|
|
131
|
+
d.broadcast_search() # 广播搜索
|
|
132
|
+
d.scan_subnet("192.168.1.0/24") # 扫描子网
|
|
133
|
+
print(d.get_nodes()) # {ip: {hostname, device_id, mac, ...}}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 3. 主动发送(按路径角色选路)
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
ch = lm.get_send_channel(peer_id) # 返回 (sock, io_lock, is_udp, role) 或 None
|
|
140
|
+
if ch:
|
|
141
|
+
sock, lock, is_udp, role = ch # role: hot / warm_safe / warm_loose
|
|
142
|
+
with lock:
|
|
143
|
+
sock.sendall(b"data")
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 4. 单独使用打洞
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
from dpmp.link.puncher import HolePuncher
|
|
150
|
+
|
|
151
|
+
p = HolePuncher(local_tcp_port=9998)
|
|
152
|
+
p.set_udp_hint(peer_id, ip, udp_port) # 双打洞:用 UDP 端口预测 TCP 候选
|
|
153
|
+
result = p.punch(peer, at_ms) # 并行打洞,返回 PunchResult(含 sock)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 5. 网络切换重建(接口反转)
|
|
157
|
+
|
|
158
|
+
DPMP 平台无关,**由宿主检测网络变化**,检测到后调用一行:
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
# 宿主:检测到 Wi-Fi/蜂窝切换、IP 变化时调用
|
|
162
|
+
lm.on_network_changed()
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
DPMP 内部自动完成:关闭旧连接 → 信令重开并重新加入(复用旧 id,无感重建)
|
|
166
|
+
→ 对所有成员重新打洞。带 10 秒冷却。
|
|
167
|
+
|
|
168
|
+
宿主检测示例(平台相关):
|
|
169
|
+
```python
|
|
170
|
+
# Android:ConnectivityManager.NetworkCallback.onAvailable 里
|
|
171
|
+
# 桌面:定时轮询本机 IP 集合,发现变化时
|
|
172
|
+
# 只要最终调用 lm.on_network_changed() 即可
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## 服务器地址
|
|
178
|
+
|
|
179
|
+
### 三种写法
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
# 写法一:IP + 端口
|
|
183
|
+
SignalingClient(server_ip="1.2.3.4", server_port=3336, ...)
|
|
184
|
+
|
|
185
|
+
# 写法二:多服务器列表(第一个失败自动切下一个)
|
|
186
|
+
SignalingClient(servers=[
|
|
187
|
+
"primary.example.com:3336",
|
|
188
|
+
"backup1.example.com:3336",
|
|
189
|
+
("backup2.example.com", 3336, 3337, 3338), # ip, port, tcp_port, nat_port
|
|
190
|
+
], auto_failover=True, ...)
|
|
191
|
+
|
|
192
|
+
# 写法三:字典
|
|
193
|
+
SignalingClient(servers=[{"ip": "1.2.3.4", "port": 3336}], ...)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### 默认服务器(便利入口,非依赖)
|
|
197
|
+
|
|
198
|
+
不传任何服务器时,回退到内置默认服务器,方便开箱即用:
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
from dpmp import DEFAULT_SERVER, DEFAULT_SERVER_EXPIRES, check_default_server_expiry
|
|
202
|
+
|
|
203
|
+
print(DEFAULT_SERVER) # "42.194.133.132"
|
|
204
|
+
print(DEFAULT_SERVER_EXPIRES) # "2026-11-01"
|
|
205
|
+
print(check_default_server_expiry())
|
|
206
|
+
# {'status': 'ok', 'days_left': 38, 'expires': '2026-11-01'}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
默认服务器**只是便利**,不是依赖:
|
|
210
|
+
|
|
211
|
+
- 使用默认服务器时,`start()` 会自动检查到期并在临近/过期时提示;
|
|
212
|
+
- 传了自己的 `server_ip` / `servers` 后,默认服务器**完全被绕过**;
|
|
213
|
+
- **局域网模式根本不使用服务器**。
|
|
214
|
+
|
|
215
|
+
> ⚠️ 默认服务器到期后,请改用自建服务器或备用服务器。
|
|
216
|
+
> 修改默认值见 `dpmp/defaults.py`。
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## 配置
|
|
221
|
+
|
|
222
|
+
调优参数通过 `Config` 按实例配置(协议常量不可配):
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
from dpmp import Config
|
|
226
|
+
|
|
227
|
+
cfg = Config(
|
|
228
|
+
rtp_window_init=64, # UDP-RTP 自适应窗口初始值
|
|
229
|
+
rtp_window_min=1, # 自适应窗口下限
|
|
230
|
+
rtp_window_max=256, # 自适应窗口上限(AIMD 动态调节)
|
|
231
|
+
rtp_keepalive_interval=10, # 保活间隔
|
|
232
|
+
punch_retry=5, # 打洞重试次数
|
|
233
|
+
punch_connect_timeout=4.0, # 打洞连接超时
|
|
234
|
+
punch_concurrency=8, # 打洞并发任务数
|
|
235
|
+
punch_candidate_concurrency=2,# 单次打洞中【并发 connect 的候选数】上限
|
|
236
|
+
heartbeat_interval=20, # 信令心跳间隔
|
|
237
|
+
proto_floor={"tcp": 30, "udp": 15}, # 保活硬性下限
|
|
238
|
+
rebuild_cooldown_sec=30.0, # 重建冷却
|
|
239
|
+
)
|
|
240
|
+
sig = SignalingClient(..., config=cfg)
|
|
241
|
+
lm = LinkManager(sig, 9998, config=cfg)
|
|
242
|
+
|
|
243
|
+
# 或改全局默认
|
|
244
|
+
import dpmp
|
|
245
|
+
dpmp.DEFAULT_CONFIG.rtp_window = 200
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**分层原则**:
|
|
249
|
+
- **协议常量不可配**(改了与对端不兼容)
|
|
250
|
+
- **调优参数、地址端口全部可配**
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## 自建服务器
|
|
255
|
+
|
|
256
|
+
信令服务器**独立开源**(见项目 `服务器/信令服务器.py`),
|
|
257
|
+
只牵线、不传数据、带宽近乎为零。可自行部署:
|
|
258
|
+
|
|
259
|
+
- Linux:`install_linux.sh`(systemd)
|
|
260
|
+
- Windows:`install_windows.bat`(计划任务)
|
|
261
|
+
- 配置:`config.json`(端口、房间上限、心跳超时等)
|
|
262
|
+
|
|
263
|
+
服务器地址通过 `server_ip` / `servers` 传给 `SignalingClient`,
|
|
264
|
+
客户端**不写死任何服务器**。
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## API 参考
|
|
269
|
+
|
|
270
|
+
### 顶层(`import dpmp`)
|
|
271
|
+
|
|
272
|
+
| 名称 | 说明 |
|
|
273
|
+
|---|---|
|
|
274
|
+
| `Config` / `DEFAULT_CONFIG` | 调优参数 |
|
|
275
|
+
| `DEFAULT_SERVER` / `DEFAULT_SERVER_EXPIRES` | 默认服务器与到期日 |
|
|
276
|
+
| `default_servers()` | 默认服务器候选列表 |
|
|
277
|
+
| `check_default_server_expiry()` | 检查默认服务器到期状态 |
|
|
278
|
+
| `__version__` / `__protocol__` | 版本 / 协议标识 |
|
|
279
|
+
|
|
280
|
+
### 连接层(`dpmp.link`)
|
|
281
|
+
|
|
282
|
+
| 名称 | 说明 |
|
|
283
|
+
|---|---|
|
|
284
|
+
| `Discovery` | 局域网 UDP 发现(广播 / 扫描 / 心跳) |
|
|
285
|
+
| `SignalingClient` | 公网信令客户端(房间 / 校时 / 映射观测 / NAT 探测) |
|
|
286
|
+
| `HolePuncher` / `PunchResult` | 双打洞核心 / 打洞结果 |
|
|
287
|
+
| `UdpReliableSocket` | UDP 可靠通道(UDP-RTP) |
|
|
288
|
+
| `PeerPathScheduler` / `Path` / `KeepaliveScheduler` | 梯度冗余多路径调度 |
|
|
289
|
+
| `LinkManager` / `Conn` | 连接管理 / 单条连接 |
|
|
290
|
+
|
|
291
|
+
### 传输层(`dpmp.stream`)
|
|
292
|
+
|
|
293
|
+
| 名称 | 说明 |
|
|
294
|
+
|---|---|
|
|
295
|
+
| `Channel` | 统一字节流通道(`sendall` / `recv` / `recv_exact` / `close`) |
|
|
296
|
+
| `wrap_channel(sock, io_lock, role)` | 包装底层 socket |
|
|
297
|
+
| `is_udp_rtp(sock)` | 判断是否 UDP-RTP 通道 |
|
|
298
|
+
|
|
299
|
+
### 工具层(`dpmp.util.net`)
|
|
300
|
+
|
|
301
|
+
| 名称 | 说明 |
|
|
302
|
+
|---|---|
|
|
303
|
+
| `get_all_local_ips(ipv6=False)` | 本机所有 IP |
|
|
304
|
+
| `get_all_subnets()` | 本机所有子网 CIDR |
|
|
305
|
+
| `get_broadcast_addrs()` | 广播地址 |
|
|
306
|
+
| `get_subnet_for_ip(ip)` | 由 IP 推断子网 |
|
|
307
|
+
| `get_mac_address()` | 本机 MAC |
|
|
308
|
+
| `get_or_create_device_id(path)` | 持久化设备 UUID |
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## 关键回调与方法
|
|
313
|
+
|
|
314
|
+
### LinkManager 回调
|
|
315
|
+
|
|
316
|
+
| 回调 | 触发时机 |
|
|
317
|
+
|---|---|
|
|
318
|
+
| `on_socket_ready(peer_id, sock, member)` | TCP 通道建立(**最常用**) |
|
|
319
|
+
| `on_udp_ready(peer_id, rtp)` | UDP-RTP 通道建立 |
|
|
320
|
+
| `on_state_changed()` | 成员状态变化(刷新 UI) |
|
|
321
|
+
|
|
322
|
+
### LinkManager 方法
|
|
323
|
+
|
|
324
|
+
| 方法 | 用途 |
|
|
325
|
+
|---|---|
|
|
326
|
+
| `get_send_channel(peer_id, exclude_socks=None)` | 按角色选路,返回可发送通道 |
|
|
327
|
+
| `get_socket(peer_id)` | 取 TCP socket |
|
|
328
|
+
| `get_udp_socket(peer_id)` | 取 UDP-RTP 通道 |
|
|
329
|
+
| `get_members()` | 成员列表(含状态、路径角色) |
|
|
330
|
+
| `get_path_roles(peer_id)` | 各路径角色 |
|
|
331
|
+
| `mark_active(peer_id)` | 标记活跃(重置空闲降频) |
|
|
332
|
+
| `on_network_changed()` | **网络切换时由宿主调用**,重建所有连接(见下节) |
|
|
333
|
+
| `start()` / `stop()` | 启动 / 停止 |
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## 网络切换重建(接口反转)
|
|
338
|
+
|
|
339
|
+
网络切换(Wi-Fi ↔ 蜂窝、IP 变化)后,旧网络上的所有 socket 与 NAT 映射全部失效。
|
|
340
|
+
DPMP 是**平台无关**的库,无法自己感知网络变化,因此采用「接口反转」:
|
|
341
|
+
|
|
342
|
+
- **宿主应用负责【检测】**(平台相关:Android NetworkCallback、桌面 IP 轮询等)
|
|
343
|
+
- **DPMP 负责【重建】**(统一逻辑,所有用户受益)
|
|
344
|
+
|
|
345
|
+
宿主检测到网络变化后,只需一行调用:
|
|
346
|
+
|
|
347
|
+
```python
|
|
348
|
+
lm.on_network_changed()
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
DPMP 内部编排完整重建:
|
|
352
|
+
|
|
353
|
+
1. 关闭所有现存连接(旧 NAT 映射已失效)
|
|
354
|
+
2. 让信令客户端**重开 socket 并重新加入**(`SignalingClient.rebind()`)
|
|
355
|
+
3. 对所有成员**重新发起打洞**
|
|
356
|
+
|
|
357
|
+
**无感重建**:重建时
|
|
358
|
+
- 不发 BYE(服务器保留成员条目,对端不掉线)
|
|
359
|
+
- 携带旧 `my_id`(`reuse_id`)——服务器支持则**复用身份**,对端看到的是
|
|
360
|
+
「同一成员回归」;旧服务器忽略该字段,退化为生成新 id(等价重新 join)
|
|
361
|
+
|
|
362
|
+
带 10 秒冷却,避免网络抖动频繁重建。
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## 端口说明
|
|
367
|
+
|
|
368
|
+
| 端口 | 协议 | 用途 |
|
|
369
|
+
|---|---|---|
|
|
370
|
+
| 3336 | UDP | 信令服务器(加入房间 / 心跳 / 打洞协调) |
|
|
371
|
+
| 3337 | TCP | 信令服务器 TCP 映射观测 |
|
|
372
|
+
| 3338 | UDP | 信令服务器 NAT 类型探测 |
|
|
373
|
+
| 9996 | UDP | 房间模式 UDP 打洞 |
|
|
374
|
+
| 9998 | TCP | 房间模式打洞 / 长连接 |
|
|
375
|
+
| 9997 | UDP | 局域网主动扫描 |
|
|
376
|
+
| 9998 | UDP | 局域网设备发现(广播 / 心跳) |
|
|
377
|
+
| 9999 | TCP | 局域网文件传输 |
|
|
378
|
+
|
|
379
|
+
所有端口均可通过参数或 `Config` 覆盖。
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
## 协议文档
|
|
384
|
+
|
|
385
|
+
- `DPMP_PROTOCOL.md` —— 完整协议规范(两端实现唯一事实来源)
|
|
386
|
+
- `test_vectors.json` —— 协议测试向量(防 Python / Kotlin 两端漂移)
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## 版本历史
|
|
391
|
+
|
|
392
|
+
### 0.1.1(底层能力补强)
|
|
393
|
+
|
|
394
|
+
- **新增**:UDP-RTP 自适应窗口(AIMD)——发送端按 ACK 加性增、按超时乘性减,
|
|
395
|
+
在 1~256 之间动态调节;高延迟链路上吞吐上限提升数倍。
|
|
396
|
+
- **新增**:打洞候选并发限制(`punch_candidate_concurrency`,默认 2)——
|
|
397
|
+
降低同一本地端口上 SO_REUSEPORT 的入站 SYN 匹配冲突。
|
|
398
|
+
- **新增**:网络切换重建(接口反转)——`SignalingClient.rebind()` +
|
|
399
|
+
`LinkManager.on_network_changed()`;宿主检测网络变化,DPMP 统一重建。
|
|
400
|
+
- **新增**:身份复用(`reuse_id`)——重建时复用旧 my_id,实现无感重建。
|
|
401
|
+
- **修复**:收到对端 PUNCH_FAIL 时未关闭半开连接——该 socket 可能被
|
|
402
|
+
`get_send_channel` 选为发送通道,把数据发进单向死连接。现在无条件关闭并
|
|
403
|
+
触发路径轮转。
|
|
404
|
+
|
|
405
|
+
### 0.1.0(初版)
|
|
406
|
+
|
|
407
|
+
- 双打洞协同、UDP-RTP 可靠通道、梯度冗余多路径、局域网发现、公网信令。
|
|
408
|
+
|
|
409
|
+
---
|
|
410
|
+
|
|
411
|
+
## 许可证
|
|
412
|
+
|
|
413
|
+
本项目采用 **Apache License 2.0** 开源许可,详见 `LICENSE` 文件。
|
|
414
|
+
|
|
415
|
+
你可以自由使用、修改、分发本项目(含商业用途),需保留版权声明与许可声明。
|