最近研究起了P2P网络,p2p网络其它很早就有了,但是用到的地方不多,以前最多用来p2p种子下载音乐视频这类的应用,对它的原理也一知半解,以p2p下载视频为例,大概原理:服务器里并不保存视频资源,只是保存哪些用户客户端里有此视频,相当于索引,用户A下载视频a,从服务器查询到对应的用户端B有此视频,然后让用户A和用户B建立连接,这样A就是直接从B下载了,减轻了服务器压力,而且A还可以同时从多个有此资源的客户端B1,B2,B3...同时下载不同分段,这样就更加加快下载速度,而且当下载完分段a1后,还可以和其它客户端C建立连接,客户C可以从客户A下载此a1分段。(大致原理是这样,但要实现那有那么简单,汗)

而最近的区块链,最底层的网络技术也用到了p2p网络来进行结点之间的数据同步。这个p2p网络其实有很大用途,当今主流架构是CS模型,也就是客户端直接和服务器直接通讯,客户端和客户端不进行通信,就算你看到的很像客户端与客户端通信的也不是,如微信/QQ,A发送消息到B ,表面上是A直接与B 通信聊天,其实中间经过了服务器,A 发送给服务器,服务器中转发给B ,只不过是长连接。但是这样会有问题,服务器其实是保存了你的聊天记录(微信不知道有没有保存,QQ是肯定有的),如果你的聊天内容很隐私,或者其它原因,不想让服务器保存,那么就可以用到P2P通信,A直接与B通信,服务器没有任何备份,通信内容只在A,B本地保存。这里不考虑网络攻击,中间人拦截等黑客技术,虽然它们也能拿到通信内容,但不在本场景考虑,这就是区块链和p2p的愿景:去服务器(本文不介绍区块链)

P2P通信基本原理可以参考这篇文章:P2P通信基本原理与实现

还有那个例子也挺好的:P2P-Over-MiddleBoxes-Demo 我在局域网测试可以,没有在公网测试

好的,看完上篇文章,对p2p网络应该有大概理解和实现,简单理一下个人理解:因为公网ip不够,不可能每台设备都分配个公网ip,所以出现 网络地址转换器(NAT),可以理解为路由器或防火墙,把ip分为公网ip和内网ip(192.168.0.0~ 192.168.255.255)也就是局域网内的ip,这里可以看下自己电脑ip,应该都是192.168.xx.xx,然后因为基本NAT不常见,主要考虑两种NAT,锥形NAT(Cone NAT)和对称型NAT(Symmetric NAT),虽然锥形NAT分好几种,但是不影响实现,统称锥形NAT

                           Server S1                             18.181.0.31:1235                     |||^  Session 1 (A-S1)  ^      |      |  18.181.0.31:1235  |      |     v 155.99.25.11:62000 v      |  |Cone NAT155.99.25.11|^  Session 1 (A-S1)  ^      |  |  18.181.0.31:1235  |      | v  192.168.0.1:1234  v      | |Client A192.168.0.1:1234

当内网192.168.0.1:1234设备请求18.181.0.31:1235服务器时,cone NAT会建立一个映射关系,把请求包转成外网ip:155.99.25.11:6200-->18.181.0.31:1235,当服务器回复时,NAT把结果包又转成内网地址,然后转发给对应客户端端口,服务器只知道客户端的公网ip 155.99.25.11:6200。

然后又介绍了p2p实现的几种方式:

1、中继:也就是服务器当转发器,A无法与B直接通信,只好通过中转服务器 :A-->Server-->B,B-->Server-->A,这也是最简单,或者这根本不能称为p2p,因为就是传统通信方式,最直接,最稳定,但节点多对服务器有压力

2、逆向链接:这个不用管,因为客户端很少有公网Ip

3、p2p打洞:打洞的目的就是让客户端A与B直接通信,打洞过程最常见为 "端点在不同的NAT",也就是A和B在两个不同网络下的局域网

端点在不同的NAT(原文解释)

假设客户端A和客户端B的地址都是内网地址,且在不同的NAT后面. A、B上运行的P2P应用程序和服务器S都使用了UDP端口1234,A和B分别初始化了 与Server的UDP通信,地址映射如图所示:

                            Server S18.181.0.31:1234||+----------------------+----------------------+|                                             |NAT A                                         NAT B
155.99.25.11:62000                            138.76.29.7:31000|                                             ||                                             |Client A                                      Client B10.0.0.1:1234                                 10.1.1.3:1234

现在假设客户端A打算与客户端B直接建立一个UDP通信会话. 如果A直接给B的公网地址138.76.29.7:31000发送UDP数据,NAT B将很可能会无视进入的 数据(除非是Full Cone NAT),因为源地址和端口与S不匹配,而最初只与S建立过会话. B往A直接发信息也类似.

假设A开始给B的公网地址发送UDP数据的同时,给服务器S发送一个中继请求,要求B开始给A的公网地址发送UDP信息. A往B的输出信息会导致NAT A打开 一个A的内网地址与与B的外网地址之间的新通讯会话,B往A亦然. 一旦新的UDP会话在两个方向都打开之后,客户端A和客户端B就能直接通讯, 而无须再通过引导服务器S了.

打洞的方式分UDP,TCP打洞,因为一些防火墙可能会过滤掉UDP请求,所以TCP打洞成功率更高,UDP效率高,连接少,所以优先UDP打洞,不行再用TCP。但是打洞也不是万能的,上面介绍过两种NAT,如果是Cone NAT就可以通过打洞来实现p2p连接,但如果是对称NAT,那么打洞也不行,只能用中继服务器当转发。

介绍完p2p基本原理与实现,然后去找想关的项目实现,发现介绍理论很多,但真正能用的项目代码很好,本来想用P2P-Over-MiddleBoxes 集成到android中,但是没成功。。编译不行。。

后来发现WebRTC项目,是实现网页之间实时、无插件的音频、视频和数据通信,在各大浏览器已支持,替换了传统要加载插件式等方式,谷歌开源,也挺成熟,项目介绍 :https://webrtc.github.io/samples/(需翻墙)

中文介绍:https://blog.csdn.net/caoshangpa/article/details/53306992

这里有个学习例子:ProjectRTC ,AndroidRTC ,clone 这两个,在服务器上运行ProjectRTC,需安装node js,

  • cd ProjectRTC/
  • npm install
  • node app.js

在浏览器访问ip:3000

然后start,选择摄像头,然后得到分享链接,在其它电脑上打开链接,选择view,就可以看见刚才摄像头的画面了,此时把服务器关闭,依然可以观看,说明p2p连接建立,不再需要服务器,当然这是同在一局域网下测试。如果想用android和web视频通话,把刚才创建的链接,最后面为Id,复制,

AndroidRTC clone ,在string.xml 中修改

<string name="host">server ip</string>

host 为运行projectRTC 的服务器ip,端口不用变,默认3000,修改

RtcActivity.oncreate 
 final Intent intent = getIntent();final String action = intent.getAction();callerId="7zamYe1fznlVAUZgAAC3";
/*if (Intent.ACTION_VIEW.equals(action)) {final List<String> segments = intent.getData().getPathSegments();callerId = segments.get(0);}*/

对callerId赋值为刚才的id,运行之后没问题就可以双向视频聊天了。

名词理解:结点=客户端=client=Peer

再说一下WebRTC 中,共需要有三个服务,三个服务可以运行在一台服务器上

1、信令服务(Signaling Server):这里是ProjectRTC 项目,当结点打开时,从信令服务器获取唯一id,对应android

client.on("id", messageHandler.onId);

表明已上线,要获取所有在线结点,可通过http://ip:3000/streams.json 接口获取。返回如下

name为流名,可自定,id 为节点id,节点和节点之间通过id进行连接,

2、stun 服务:p2p原理中打洞服务,先udp,再tcp

3、turn 服务:p2p原理中的中继服务器,备用服务

ICE : 整合stun+turn ,也就是当stun 打洞不行后,用turn 服务器进行通信的一整套方案

以推流为例,整个建立连接过程如下:

推流端                        信令服务器                    接收端
clientA                      Signaling                    clientB请求建立连接<----------------------------<------init--------<----------
收到请求,创建视频流|                 offer 同意推流V---->----------------------->----------------------------->|回复已收到offer          |<----------------------------<------answer--------<--------Vcandidate 交换候选ip:port (若干次)---->----------------------->----------------------------->candidate 交换候选ip:port(若干次)<----------------------------<---------------------<--------ICE|------------ ICE 通过ip:port 尝试建立连接  ------------|连接建立,B收到A的视频流

其中B为主动请求连接A的客户端,经过init,answer,offer 后,这三步很像Tcp 的三次握手,可参考理解。然后 candidate步骤为交换双方所有可连接ip:port ,这其中有内网ip,tcp,udp 各种连接方式,因为有多个候选,所以candidate 重复执行多次。ice就是寻找最合适的连接进行连接,上面说了信令服务可以用ProjectRTC ,而stun+turn 这两个服务可以用 coturn ,conturn 集成了stun+turn,所以搭建好这一个就行了,搭建方法参考文章,基于coturn的webrtc iceserver搭建 和其它文章。

当搭建成功后可以通过 http://ip:3478/ 测试,如下

则为成功,(为什么要使用turn,上面已经说了,当NAT为对称NAT时,打洞是不行的,只能使用turn中继服务,而stun 服务公开,开放的也有很多可以直接用,而turn推荐还是用自己的,毕竟数据流会经过服务器,而coturn 集成了两者,经测试,当两设备是同一局域网,或者在不同局域网,都可以通过stun进行打洞连接,而当一设备使用4G网络时,只能使用turn中继),还可以上 Trickle-Ice 测试你的turn 服务

填入stun或turn server,账户,密码(stun 不需要账户密码,turn 需要),格式为stun:ip:port 和turn:ip:port?transport=tcp或udp,然后点击gather candidates 按钮,可以通过增删server观察得到的ip数目变化,如果增加turn server 后,数目有增加,可表示turn server 启动成功。或者通过Component Type ,如果是 relay ,表示这为中继候选。

好了,上面说完WebRTC ,其实webRTC是一个很大的项目,值得研究的点也很多,主要分为视频的录制,编解码,网络传输几大块,这里给个中文的webrtc学习网站 :http://webrtc.org.cn/ 大部分资料都是基于js的,因为这里我只想用它的p2p传输,只研究了相关部分。

回到AndroidRTC项目,这个demo 代码没多少,为更好理解大概原理,可以看看,主要是 WebRtcClient.java 类。因为node我不懂,不关心服务器实现,只能看客户端了。在AndroidRTC 中,视频和音频是以流的方式传输,而且被封装进C层,不太清楚实现,我最终目的并不是传视频,但是我知道,视频流都能传,那其它数据肯定也能传,果然,webrtc中还有一个DataChannel,就是专门用来传输其它数据的,传输对象为bytebuffer,也就是byte[] 。

dataChannel 传输数据

channel.send(new DataChannel.Buffer(ByteBuffer.wrap("message".getBytes()), true));

接收数据:

 channel.registerObserver(new DataChannel.Observer() {@Overridepublic void onStateChange() {Log.d(TAG, "registerObserver onStateChange" + channel.state());mListener.onStatusChanged("DataChannel " + channel.state());}@Overridepublic void onMessage(DataChannel.Buffer buffer) {Log.d(TAG, "registerObserver onMessage" + buffer + " data=" + buffer.data + " isBinary=" + buffer.binary);int capacity = buffer.data.capacity();byte[] bytes = new byte[capacity];//接收到的byte[]数据buffer.data.get(bytes);}});

这段建立连接后通过dataChannel发送/接收数据的主要代码,,但是这个dataChannel还有个坑,ByteBuffer.wrap(byte[])  参数byte[] ,如果一段文字的还好,可以正常传输,但是这个是有大小限制的,经试验,单次包最大 66528 字节,~=64kb 也就是如果你传输一张图片的话,是会传输失败并断开连接,1百多k的图片会自动分包,通过打印分包后的大小,最大被分成65528个字节的包。

如果你不想跑这个视频通话,或者也只想传输其它数据,那么可以跑我上传的 AndroidP2pTest 这个项目,也是 ProjectRTC + AndroidP2pTest 的,主要删除了音视频流的传输,增加了文字,图片,文件传输。并且解决了上层传输数据大小超过64k的问题,内部做好了分包,详情见AndroidP2pTest。可以在此基础上修改为你想要的功能,项目运行见github。

发送数据直接用

client.sendData(dataChannel, data,1);

其中type为1,在接收的时候可对type区分,可自定义

接收数据处理

client.setIMessageReceiver(new IMessageReceiver() {@Overridepublic void onReceiverStart() {appText("开始接收");}@Overridepublic void onReceiverProcess(float process) {appText("接收中" + process);}@Overridepublic void onReceiverSuccess(byte[] data, int type) {appText("接收完成" + data.length);if (type == 1) {//textappText("收到 " + new String(data));} else if (type == 2) {//bitmapBitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);appText("收到  bitmap" + bitmap.getWidth() + " *" + bitmap.getHeight() + " =" + bitmap.getByteCount());Utils.safeShowBitmapDialog(RtcActivity.this, bitmap);} else if (type == 3) {//文件//写入文件。。。Log.d(TAG, "file size=" + data.length);appText("收到 文件 fileSize"+data.length);}}});

其中的信令服务,stun,turn 都是跑在一台服务器上,后期可能会关闭,毕竟只是用来开发,商业可千万别用这个,里面也列了一些免费的stun,turn服务,都可拿来玩。经测试,无论是同一局域网,还是不同局域网,还是4g,都能通信,其中4g为turn。

实现p2p后可以拿来做什么?

基于这之上,可以用来做个p2p聊天程序,视频通话……总之能想到的用户对用户应该都能实现,因为现在做android物联网方面的,我后期准备基于这个做一个远程控制类的程序,可以让一个实时显示另一台屏幕内容,并控制设备。

因为这个,学到的东西还挺多的,特此记录。

Android P2P 通信方案探索相关推荐

  1. android局域网通信方案,Android基于TCP的局域网聊天通信

    一.准备工作 开发环境 jdk1.8 Eclipse Luna Service Release 1 (4.4.1) 运行环境: 华为荣耀6(Android4.4).华为p9(Android7.0) 实 ...

  2. Android热修复升级探索——代码修复冷启动方案

    前言 前面一篇文档, 我们提到热部署修复方案有诸多特点(有关热部署修复方案实现, Android热修复升级探索--追寻极致的代码热替换).其根本原理是基于native层方法的替换, 所以当类结构变化时 ...

  3. 基于AOA协议的android USB通信

    摘 要:AOA协议是Google公司推出的用于实现Android设备与外围设备之间USB通信的协议.该协议拓展了Android设备USB接口的功能,为基于Android系统的智能设备应用于数据采集和设 ...

  4. CH340与Android串口通信

    CH340与Android串口通信 为何要将CH340的ATD+Eclipse上的安卓工程移植到AndroidStudio 移植的具体步骤 CH340串口通信驱动函数 通信过程中重难点 还存在的问题 ...

  5. 穿越NAT的p2p通信方法研究

    穿越NAT的p2p通信方法研究 日期:2008-12-08 来源:P2P网  作者:未知 字体:大 中 小 <script src="http://www.ppcn.net/ads/b ...

  6. android IPC通信(下)-AIDL

    android IPC通信(上)-sharedUserId&&Messenger android IPC通信(中)-ContentProvider&&Socket 这篇 ...

  7. 内网穿透实现P2P通信

    P2P 通信最大的障碍就是 NAT(网络地址转换),NAT 使得局域网内的设备可以与公网进行通讯,但是不同 NAT 下的设备之间通讯将会变得很困难.UDP 打洞就是用来使得设备间绕过 NAT 进行通讯 ...

  8. weex更新方案探索(一)

    created by zhenggl 在项目开发过程中发现: 由于网络的波动不稳定,有时weex在线js加载显示会比较慢甚至加载超时: 而资源在安装包内的本地js的方式,每次有B端页面的改动都需要重新 ...

  9. 让流媒体服务SRS支持P2P通信

    #  srs支持p2p通信简介 流媒体服务srs是国内开发且开源的一款功能强大,性能强劲的优秀的流媒体服务器,目前正被越来越广泛的使用.srs一般被用户部署在公网上的云主机,这样方便用户进行推拉流等各 ...

最新文章

  1. 撑起12306网站,全靠这个世界第一的缓存框架!
  2. php使用fputcsv进行大数据的导出
  3. IPM: Six right dimensions limitation
  4. 无法连接iphone软件更新服务器_上海腾科教育今日分享——提示“无法连接到服务器”的解决办法...
  5. STM32工作笔记0093---DAC数模转换实验-M3
  6. 运用计算机怎么实现自动化,如何用计算机串口实现自动化控制
  7. 信用评分卡 (part 7 of 7)
  8. matlab 快速傅里叶变换函数(fft)编写
  9. 使用sklearn加载波士顿房价数据集
  10. HbuilderX如何创建一个新的Vue工程
  11. Barsetto百胜图TripressoES意式便携咖啡机测评,咖啡随行玩味无穷
  12. SAP中货物移动库位权限管理测试
  13. 微信小程序开发一个简单的摇骰子游戏
  14. Java 中的判空操作
  15. AtCoder Beginner Contest 168 C~D题解
  16. 处理Elasticsearch集群yellow和red状态
  17. java 交易金额转换分,java金额元与分转换工具种
  18. QT 使用数据流方式QDataSteam读写结构体数据
  19. python除法编程_跟老齐学Python之啰嗦的除法
  20. 基于Web的校园互助平台设计与实现

热门文章

  1. MyBatis -- resultType 和 resultMap
  2. 服务器的ip端口加密协议混淆,Obfsproxy - 混淆/加密端口数据
  3. 直连的不同网段的两台主机如何通信
  4. fiddler抓app包获取不到HTTPS请求的2个解决方案
  5. python中的线性数据结构
  6. linux设备端口编写,在Linux上开发HDMI端口
  7. 终端 删除php文件内容,Mac_mac命令行终端可以卸载吗?mac命令行终端卸载软件教程,  mac os系统卸载软件方式有 - phpStudy...
  8. PyQt5基本控件详解之QTextEdit(五)
  9. c语言课程设计查找分数与删除元素,c语言课程设计学生成绩管理系统
  10. 在WIN7正常使用老版本工商银行华虹U盾驱动解决办法