八月底,我在家工作。 后来,有人让我向办公室发送一个 VMWare 映像。 虚拟机的大小为 +17 GB,因此无法使用 Slack。 由于我不在办公室,使用 USB 驱动器也不是办法。
我有一些空闲时间,于是就想:"好吧,用它来编写一个小程序。UDT's 交会模式应该很简单"(这种模式基本上可以让你建立真正的 P2P 连接)... 顺便说一下,我本可以用更好的东西,比如 ,但是......嘿,我只是想做一些编码工作 :-)
ssr:
我是几天后才发现上面这个笑话的。 xkcd 的人描述了如何文件传输问题尚未解决.
ssr:
此外bar.io后来我还发现一些JavaScript WebRTC 代码与我试图通过命令行实现的功能完全相同.
我试图为 C# 找一个 WebRTC 实现,结果发现了来自 Frozen Mountain 的 IceLink ()。 我还没有尝试,但它看起来很棒。
ssr:
简单说明一下:我所说的 P2P 是指在两个不同的防火墙后连接两台电脑。
如果计算机在同一个本地网络上,或者可以直接从互联网访问,我就不会在这篇博文中将其视为 P2P(理论上可以是,但要让它们连接起来,传统的套接字操作就足够了)。
ssr:
其实应该是 "看,妈,没有服务器!"。
要在两台位于不同防火墙后的计算机之间建立 P2P 连接,首先需要获取并交换它们的公共 IP 和端口。
然后,每台计算机都会尝试连接到另一个 IP:端口,如果幸运的话,它们会穿越 NAT 并连接上(你知道,防火墙/NAT 允许你在使用公网 IP 的同时使用私网 IP)。
但你需要一个中央服务器来协调这一过程。 每台客户端计算机都要连接一台服务器,用来获取和交换它们的 IP 地址。
我们自己的管道服务器可以扮演这个角色(它已经在为我们自己的伪 P2P 操作充当中继服务器--所有流量都通过中央服务器,这是我想避免的真正的 P2P 操作)。
#@#But I wanted to write a small console application so simple that it didn't need to "trust" any intermediate server, so I didn't want to use a central server to do the initial negotiation (or signaling).#@#
我想要一个无服务器的 P2P 设置...
因此,我们的目标是让 p2pcopy 启动,显示公共 IP(这需要一个服务器),并询问对方的公共 IP。 然后,p2pcopy 的用户将通过 Slack 或任何其他消息系统交换他们的公共 IP:端口,然后开始数据传输...... 无需服务器。
ssr:
到目前为止,我一直认为 UDT 是理所当然的。 UDT 是在 UDP 基础上编写的一个巧妙协议,在高延迟/高带宽网络上的性能优于 TCP。
这意味着什么? 嗯,如果你的 ping 需要 100 毫秒,但你的带宽很高,那么 UDT 将绕着 TCP 跑一圈。
nonenone.
none
ssr:
UDT 编码交会模式none
none
如果两个对等体执行相同的代码,它们就会真正连接起来!
#@#Yes, they both "connect()" instead of a connect/accept pair, and it works, that's the magic of NAT traversal.#@#
#@#I tested this with 2 laptops, manually, one on the Codice'ssr: and the other just on my LAN (Wi-Fi) and... it worked! Although I had to run the two peers concurrently for it to work... which is something I initially sub estimated when I though "I will code it in 5 minutes".#@#
ssr:
运行 p2pcopy 的每个节点都必须获取其外部 IP 和端口,以便与另一个对等节点交换。 为了做到这一点,我使用了(并清理了)我在CodeProject:用 C# 实现 STUN 客户端。
ssr:
到目前为止,整个编码过程都非常迅速,只需几分钟就能启动和运行。
none
于是,我决定向办公室的一位同事求助。
我们都启动了自己的应用程序,交换了 IP,点击 ENTER,然后......什么都没有! 没成功 哎哟
然后,我们开始使用 Slack 手动同步,以便能够同时 HIT 运行连接代码(在同一张桌子上的两台笔记本电脑上按 ENTER 键时,这并不难)。 结果还是不行。
最糟糕的是,当你的同事开始取笑你时,你知道,"是啊,你这是多么伟大的发明啊!" :-D
none
如果是这样的话,我的 p2pcopy 就没什么用了
ssr:
与真实用户的首次测试完全失败。 我花了 10 分钟编写 p2pcopy 的代码",结果还是要多花一点时间。
#@#Initially I thought: "ok, it can't be done without a central server, that's all". But, well, I kept relaunching manually on my two laptops and it sort of worked most of the time. I was worried because of the low data transfer speed (1 to 1.5MB/s seemed to be the upper limit, with many transfer under 700KB/s).#@#
none
如果双方都查看时间,并等到给定的一秒到来呢? 那么它们实际上会在同一时间开始... 但如果它们的时钟不同步呢?
解决办法是从互联网时钟中获取准确时间。 谷歌提供了大量 C# 实现,我只用了一个来自 StackOverflow.
用户输入对方的对等 IP:端口对后,程序会检索互联网时间,并等待一分钟中的某一秒到来。 比如等待 0、10、20、30、40、50 秒后再开始同步连接。
它成功了! 我在局域网/VPN 设置上进行了测试,还测试了通过远程桌面访问 VPN 上的远程机器,结果正常!
#@#Later I ran tests with two different "real users" and while it wasn't perfect, most of the time you could enter the other peer and hit enter without having to "Slack-synchronize" and it worked fine.#@#
ssr:
一旦我成功了,我就创建了一个 GitHub repo 来共享代码,包括我开发它的全部历史、办理登机手续(不用说,我其实是用 Plastic 开发的,只是推送到了 GitHub :P)。
但我最担心的是数据传输速度。
我向办公室发送了著名的 17GB VmWare 映像,数据传输速度始终保持在 800KB/s 左右,只是偶尔会达到 1MB/s。
速度很慢。
然后,我开始重新阅读和研究 UDT 选项,但没有成功。 任何组合似乎都无法加快数据传输。
因此,我想我必须找到一种更好的方法来发送数据,我决定测试并行数据传输:如果我将文件分成两部分(或更多部分),并尝试通过并行发送片段来更好地利用可用带宽,会怎么样?
我在分支上做了一些测试主控多线程none
ssr:
然后,我编写了一个使用纯 TCP 发送/接收数据的选项,以便在局域网上进行测试。 这与 P2P 无关,只是为了测量和比较数据传输。
none
ssr:
然后,我在局域网上测试了真正的 UDT 会合(毕竟是 NAT 打洞),以比较普通 TCP 和 UDT 的结果:
tcp
#@#p2pcopy.exe sender --tcp --tcpremotepeer 192.168.1.39:7070 --file C:\Users\pablo\Downloads183u.tif Connected to 192.168.1.39:7070 -[###################################] 181.64 MB / 181.64 MB. 2.3 MB/s#@#
udt
#@#p2pcopy.exe sender --localport 4300 --file C:\Users\pablo\Downloads183u.tif Using local port: 4300 Your firewall is FullCone Tell this to your peer: 88.41.37.87:4300 Enter the ip:port of your peer: 192.168.1.39:21300 Your firewall is FullCone [18:35:52] - Waiting 8 sec to sync with other peer Your firewall is FullCone 0 - Trying to connect to 192.168.1.39:21300. Connected successfully to 192.168.1.39:21300 -[#################################] 181.64 MB / 181.64 MB. 1.75 MB/s#@#
UDT: 1.75MB/s 与技术合作计划(TCP:2.3MB/s(我必须打开端口进行 TCP 测试)。 有差别,但不是很大!
请注意,在 UDT 模式下(不带 --tcp 标志),我输入的是本地 IP 地址,而忽略了公共 IP 地址。
有趣的发现 – 您可以使用 NAT 打洞穿越局域网上的每台计算机的防火墙。 这一点非常有趣,因为在局域网上,你受到自己防火墙的保护,但在这里,NAT 打洞也能起作用。 因此,更好的基于 P2P 的 Plastic Tube 可以检测两个对等设备是否有相同的公网 IP(它们在同一个局域网上),而不是向公网 IP 打洞(这确实行不通),只需使用专用地址即可。
none
事实上,Wi-Fi 问题让我开始研究 PeerFinder,因为我分享了这里.
ssr:
在看到包括多线程数据传输在内的 perf 结果后,我认为 UDT 可能存在问题,而普通 TCP 可能会做得更好。
于是,我试着让 TCP 打孔机工作。
理论上说,你必须做到以下几点:
- 每个同行启动 2 个线程。
- 一个线程尝试连接另一个对等节点。
- 另一条线聆听。
- 两个线程都创建了套接字,但它们利用地址重用功能绑定到同一个本地端口。
后来我读到,其实根本不需要 "accept 部分",事实上,在我的所有测试中,我从未看到过 accept 起作用。
我开始在局域网上进行测试,结果成功了:除非禁用笔记本电脑上的防火墙,否则简单的 tcp 无法正常工作(连接/接受,无打孔,原始代码),而我的 tcp 打孔代码却能正常工作! 连接立即建立并发送了数据。
使用 --tcpholepunch 的示例会话如下:
p2pcopy\bin\Debug\p2pcopy.exe sender --tcpholepunch --localport 7070 --tcpremotepeer 192.168.3.35:7070 --file PeerFinder.exe Running tcp hole punch Trying to connect Connector correctly connected \[############################################################] 10.5 KB / 10.5 KB.
在这个测试中,每个对等程序都会事先设置其本地端口和远程 IP:端口,我从未执行过交换部分,因为我没有使用 STUN 来处理 TCP(事实上,代码只是针对 UDP)。
在最初的局域网成功后,我在 VPN 上进行了测试。 没有成功。 我看到其中一个对等体正在连接,但另一个总是失败。
后来,我用我的一位同事从他家连接进行了测试......结果也不行。 我又用办公室的另一个同事进行了测试,结果也一样。
我可能弄错了,但在本地网络之外,tcp 打孔不够可靠。
有趣的发现 – none.
ssr:
与 TCP 相比,UDP 打孔似乎更有可能奏效,这一点我已经亲眼目睹了。 可以找到一些很好的解释,其中提到 SYN 数据包和 TCP 是如何建立的等等。
但从根本上说,这意味着在互联网上,你最好坚持使用 UDP,这意味着你需要在上面建立一些可靠性层,比如 UDT。
nonenonenone
ssr:
ssr:
由于 P2P 是现代网络应用程序的优势,我开始阅读一些资料,了解它们是如何做到这一点的。
WebRTC 就是他们都在使用的技术。 它能在大多数现代浏览器上用 JavaScript 轻松实现视频会议和音频。
nonenone用于双向点对点任意数据传输。
我曾尝试用 C# 来实现 WebRTC,但没有找到。 使用 WebRTC,以真正的 P2P 方式实现 Tube 将非常简单,因为该库可以处理打孔、决定是否需要通过中继、解决两个节点是否在同一局域网上的问题等等。
我一直在搜索,但除了 XSockets(一种用于实时通信的商业产品)之外,我没有找到任何适用于 C# 的东西,但我不清楚它们是否能完成整个 RTC 的工作。
后来,我发现可以通过在表单上嵌入浏览器或其他东西来实现 WebRTC,并实现自动化,但这看起来像是矫枉过正。
ssr:
更多的阅读让我找到了 libjingle(同样,因为我觉得我以前已经读过关于它的文章)。 这是一个由 Google 开发的库,用于简化 P2P 应用程序的开发。
而且,WebRTC 似乎完全是在 libjingle 的基础上构建的(我的意思是,你可以找到这样的包含 #include"webrtc/p2p/base/pseudotcp.h";代码上.
它使用 ICE、TURN、STUN、XMPP-or-something 等协议以可靠的方式创建连接。 似乎 92% 的时间可以直接实现 P2P,只有 8% 的时间需要中继。 这似乎是他们用于 Google Hangouts 的协议(不确定他们是否还在使用)。
none
ssr:
我的问题是:这些人是如何实现 TCP 点对点连接的?
我以为是这样,因为我试过 从家里连接到办公室,速度达到 2MB/s,而 p2pcopy 只有 1.2MB/s 左右。 不知为什么,它们的速度更快,我想知道为什么。
我一直在阅读,但没有找到很好的答案,直到浏览代码时我发现了一些关于 pseudotcp 的内容。 起初我以为这只是一个选项,但后来我发现了这个:none
none可以通过防火墙发送类似 TCP 的数据包。 通常,通过 NAT 建立 UDP 连接比建立 TCP 连接更容易。 因此,我们提供了 PseudoTcpChannel,使 UDP 数据包具有类似 TCP 的功能。 建立连接时,每个 FileShareSession 对象都会创建自己的 PseudoTcpChannel 对象。 FileShareSession 对象在建立连接时会创建自己的 PseudoTcpChannel 对象,它通过调用传入其构造函数的会话对象上的 Session::CreateChannel 来创建 TransportChannel,从而提供外部数据连接。 它暴露了一个 StreamInterface,供内部组件读/写数据到远程计算机。 在文件共享应用程序中,PseudoTcpChannel 充当通道与 HttpServer 或 HttpClient 之间的中介,通过伪 TCP 层封装或拆封数据。 当 FileShareSession 的关联会话对象接收到带有 QN_SHARE_CHANNEL 成员的 XML 信息节时,FileShareSession 就会创建 PseudoTcpChannel。
那么,归根结底,他们是在像我一样进行 UDP 打洞,只不过他们把 pseudotcp 放在了 UDT 的上面,而不是 UDT。 所以,也许 pseudotcp 比 UDT 更有效率!
none可在此阅读.
none
我发现了一个关于 libjingle 的有趣主题,甚至还有一个可能的 C# 移植.
ssr:
我开始寻找有关 pseudotcp 的信息,结果发现了 libnice。 这是另一个 P2P 库,他们似乎从 Google 复制了 pseudotcp 代码,并在最近进行了改进。 我找到了一篇关于它的文章:
最后,在 libnice' 的伪 TCP 实现中添加了 FIN/ACK 支持。 该代码最初基于 Google 的 libjingle 伪 TCP,通过在 UDP 中封装类似 TCP 的数据包,在 UDP 上建立可靠的连接。 它实现了 TCP 的基本功能,但像关闭 FIN/ACK 握手这样的功能则留给了更高级别的协议。 这对 Google 来说没问题,但对我们的用例却不合适,因此我们增加了这方面的支持。 此外,我们还需要使用 GTlsConnection 在伪 TCP 连接上分层 TLS,这就需要实现半双工关闭支持,并修复 GTlsConnection 中一些令人讨厌的漏洞。
libnice 在 C# 中也没有封装或任何东西,不过我读到曾经有一个 GTK# 应用程序在 C# 中使用 DBus 来使用它。这里他们提到了 Chatter、Telephathy GUI 和 Telephathy 是另一种 P2P 技术。 我还在一个讨论区但没有找到任何代码。
libnice pseudotcp 代码采用 C 语言而非 C++,可在此处找到:
ssr:
您可以从 GitHub 获取源代码还有一个提供二进制版本以备不时之需 :-P
ssr:
我想试试这个想法,看看使用 pseudotcp 代替 UDT 如何提高数据传输速度。
ssr:
我承认我喜欢所有与网络编程相关的东西。 虽然我不是什么专家,但我确实乐在其中。
#@#My motivation with all this P2P thing, though, is to add real peer-to-peer connectivity to our Plastic Tube feature. Right now, as I mentioned, all traffic goes through a relay. We don't cache any data at all, but obviously it would be more effective both in cost and transfer speed if it used true P2P. Unlike p2pcopy, the Tube uses a server to negotiate the connection and to serve as "directory service" so you can easily reach remote repositories by email like this:#@#your_repo@tube:robin@batman.com这非常方便。
读来非常有趣,谢谢!
回复删除下面是几个可以与 c# 一起使用的 webrtc 栈
回复删除太好了,谢谢)
删除我注意到 Github 已经 3 个月没有更新了。 成功率为 50%,最大速度约为 1 MB/秒,发送大文件时会中途崩溃。
回复删除谢谢您的分享!
删除是的,我们的速度比 1MB/s 多一点,但也不是太多。 我不得不说,Reep.io 的传输速率并不高。
我的目标是在我们的主要产品 Plastic SCM 中使用这些代码进行 P2P 操作。
为了改进 perf,我们应该试试伪 tcp,看看效果如何。
关于 50%的成功率:嗯......是的,在路由器上开孔有时需要不止一次尝试。 您可以做的一件事是,在一次成功后的下一次数据传输中指定端口。
真正的改进方法是使用中央服务器作为会合点,但这样就失去了这一想法的部分美感。
关于崩溃:没有发现任何崩溃。 你是指发送大文件时连接中断吗? 如果能实现重试就太好了。
关于项目活动: I'm open to receive contributions? 加入重试怎么样?) 想做吗?
谢谢!
巴勃罗
有趣的阅读! 你有没有试过在对称 NAT 后面的任一或两个对等网络中使用你的代码? 考虑到整个端口地址转换问题,这似乎行不通。
回复删除既然您交换的是外部端口,它就应该能正常工作。 我经常用它与队友交换大文件,大多数时候似乎都能正常工作 :-)
删除