一、p2p是什么?

p2p是点对点的缩写(peer-to-peer networking),其可以定义为:端对端的资源共享,每一端即可是服务端,也可以是客户端。既可以是资源的提供者,也可以是资源的共享者。传统C/S模型需要实现端和端的资源共享, 需要将资源上传到服务器,然后另外一端再从转服务器下载,如下图所示:

而P2P则不需要将资源上传到服务器,它是端对端传输,每一个端点既可以是服务器,也可以是客户端。采用p2p之后,端点之间的实时性最高,无需服务器中转,不占服务器宽带,通信更加安全。在视频直播,在线教育,视频监控,安防行业用的比较多。p2p架构如下图所示:

p2p模式支持同一个内网的端点之间直接互传数据,如上图的客户端B和客户端C,这种方式数据传输是最短最快的。

同时也支持不同内网的端点之间直接互传数据,如上图的客户端A与 客户端B、客户端A客户端C之间,这时数据首先会到达NAT1,然后再到NAT2,最后到达客户端B或客户端C。

二、NAT是什么

NAT全称是网络地址转换,它是一种把内部私有网络地址(IP地址)转换成公网网络IP地址的技术。比如我们电脑里面网卡地址是192.168.1.10,但是我们再百度搜索“我的IP”却显示210.12.214.53,这就是NAT的功能。NAT主要是部署在路由器或者交换机上。

三、为什么需要NAT

NAT是Network Address Translation的缩写,也就是网络地址转换的意思。现在大多数设备的网络采用的是ipv4网络,ipv4中ip的定义为x.x.x.x,其中每一位为0-255,所以全球的ip总数256^4,这个数量是不足够全球使用的,为了保证每个人都能有ip使用,NAT技术诞生了。

使用NAT将有助于减缓可用的IP地址空间的枯竭。比如你有一个路由器(家用的那种就可以)这个路由器本身连接了公网(被分配到了一个公网的IP地址)。路由器后面有接了N多个设备,每个设备都分配到了一个私有的地址(内网地址),这些地址可以通过这个路由器和外网交互。使用NAT还有能避免来自网络外部的攻击,隐藏并保护网络内部的计算机。

四、NAT有哪些类型

NAT有4个类型,它们分别是:NAT1、NAT2、NAT3、NAT4。从 NAT1 至 NAT4 限制越来越多。

1. 完全圆锥形NAT(Full Cone NAT)

完全圆锥型NAT把一个来自内部IP地址和端口的所有请求,始终映射到相同的外网IP地址和端口;同时,任意外部主机向该映射的外网IP地址和端口发送报文,都可以实现和内网主机进行通信,就像一个向外开口的圆锥形一样,故得名。

这种模式很宽松,限制小,只要内网主机的IP地址和端口与公网IP地址和端口建立映射关系,所有互联网上的主机都可以访问该NAT之后的内网主机。

2. 地址限制式圆锥形NAT(Address Restricted Cone NAT)

地址限制式圆锥形NAT同样把一个来自内部IP地址和端口的所有请求,始终映射到相同的外网IP地址和端口;与完全圆锥型NAT不同的是,当内网主机向某公网主机发送过报文后,只有该公网主机才能向内网主机发送报文,故得名。相比NAT1,NAT2 增加了地址限制,也就是IP受限,而端口不受限。

如下图所示,地址限制圆锥型NAT(Address Restricted Cone NAT)会将客户机地址{X:y}转换成公网地址{A:b}并绑定,只有来自主机{P}的包才能和主机{X:y}通信。

3. 端口限制式圆锥形NAT(Port Restricted Cone NAT)

端口限制式圆锥形NAT更加严格,在上述条件下,只有该公网主机该端口才能向内网主机发送报文,故得名。相比NAT2,NAT3 又增加了端口限制,也就是说IP、端口都受限。

端口限制圆锥型NAT(Port Restricted Cone NAT)会将客户机地址{X:y}转换成公网地址{A:b}并绑定,只有来自主机{P,q}的包才能和主机{X:y}通信。如下图所示:

4. 对称式NAT(Symmetric NAT)

对称式NAT把内网IP和端口到相同目的地址和端口的所有请求,都映射到同一个公网地址和端口;同一个内网主机,用相同的内网IP和端口向另外一个目的地址发送报文,则会用不同的映射(比如映射到不同的端口)。如下图所示:

对称型NAT(Symmetric NAT)会将客户机地址{X:y}转换成公网地址{A:b}并绑定为{X:y}|{A:b}<->{P:q}。对称型NAT只接受来自{P:q}的incoming packet,将它转给{X:y} ,每次客户机请求一个不同的公网地址和端口,NAT会新分配一个端口号{C,d} 。

和端口限制式NAT不同的是,端口限制式NAT是所有请求映射到相同的公网IP地址和端口,而对称式NAT是为不同的请求建立不同的映射。

它具有端口受限锥型的受限特性,内部地址每一次请求一个特定的外部地址,都可能会绑定到一个新的端口号。也就是请求不同的外部地址映射的端口号是可能不同的。

五.如何穿透NAT

目前主流的几种解决方式有ALG、STUN、TURN、ICE,我们分别来介绍它们的工作原理及工作流程

1、ALG

ALG是指能识别特定应用层协议(如SIP、H.323或MGCP协议)的防火墙。它不是简单地查看分组首部信息来解决数据分组是否可以通过,而是更深层地分析负载内容的数据,也就是应用层的数据。SIP和H.323协议都在负载中放了重要的控制信息。通过分析哪一个端口需要打开。防火墙动态的打开那些被应用的端口,而所有别的端口依然安全地保持关闭状态。ALG是支持VOIP应用最简单的一种方式,但该方案的缺点非常明显:每增加一种新的应用都将需要对 NAT/Firewall进行升级。在安全要求上还需要作一些折衷,因为ALG 不能识别加密后的报文内容,所以必须保证报文采用明文传送,这使得报文在公网中传送时有很大的安全隐患。SIP响应消息用于对请求消息进行响应,指示呼叫或注册的成功或失败状态。在请求与响应报文中需要进行ALG处理的地址字段类型主要有:Via、Record_Route、Contact、SDP。

ALG处理流程为如下三个步骤:

首先,ALG根据会话标识的协议类型对报文进行解码,若解码发现报文为不需要做ALG或解码发现为错误字段时退出,解码发现需进行字段转换时进一步处理;其次,ALG查找接口上的NAT配置,根据NAT配置转换报文中的IP地址、端口、call-id等信息并建立关联表,关联表记录了载荷地址的转换关系;最后,ALG调整报文载荷中的长度字段,如sip message header的content-length字段标识message body的长度,ALG对message body中的地址转换后,message body长度可能变化,content-length字段值需要置为变化后的值。

2、STUN

STUN的全称是Simple Traversal of UDP Through NAT,即UDP对NAT的简单穿越方式。是一种网络协议它允许位于NAT(或多重NAT)后的客户端找出自己的公网地址,查出自己位于哪种类型的NAT之后以及NAT为某一个本地端口所绑定的Internet端端口。这些信息被用来在两个同时处于NAT 路由器之后的主机之间建立UDP通信。该协议由RFC 3489定义。 应用程序(即STUN CLIENT)向NAT外的STUN SERVER通过UDP发送请求STUN 消息询问自身的转换后地址,

STUN SERVER收到请求消息,产生响应消息,响应消息中携带请求消息的源端口,即STUN CLIENT在NAT上对应的外部端口。响应消息通过NAT发送给STUN CLIENT,

STUN CLIENT通过响应消息体中的内容得知其在NAT上对应的外部地址,并且将其填入以后呼叫协议的UDP负载中,告知对端,同时还可以在终端注册时直接注册这个转换后的公有IP地址,这样就解决SIP穿越NAT的通信建立问题以及作为被叫时的问题。

本端的接收地址和端口号为NAT外的地址和端口号。由于通过STUN协议已在NAT上预先建立媒体流的NAT映射表项,故媒体流可顺利穿越NAT。

3、TURN

TURN的全称为Traversal Using RelayNAT,即通过Relay方式穿越NAT,TURN应用模型通过分配TURNServer的地址和端口作为客户端对外的接受地址和端口,即私网用户发出的报文都要经过TURNServer进行Relay转发。这种方式又称SPAN(Simple Protocol for Augmenting NATs)方式. TURN方式解决NAT问题的思路与STUN相似,也是基于私网接入用户通过某种机制预先得到其私有地址对应在公网的地址(STUN方式得到的地址为出口NAT上的地址,TURN方式得到地址为TURNServer上的地址),然后在报文负载中所描述的地址信息直接填写该公网地址的方式,实际应用原理也是一样的。这种方式除了具有STUN方式的优点外,还解决了STUN应用无法穿透对称NAT(Symmetric NAT)以及类似的Firewall设备的缺陷,即无论企业网/驻地网出口为哪种类型的NAT/FW,都可以实现NAT的穿透,同时TURN支持基于TCP的应用,如H323协议。此外TURN Server控制分配地址和端口,能分配RTP/RTCP地址对(RTCP端口号为RTP端口号加1)作为私网终端用户的接受地址,避免了STUN方式中出口NAT对RTP/RTCP地址端口号的任意分配,使得客户端无法收到对端发来的RTCP报文(对端发RTCP报文时,目的端口号缺省按RTP端口号加 1发送)。

4、ICE

交互式连通建立方式ICE(Interactive Connectivity Establishment)并非一种新的协议,它不需要对STUN、TURN或RSIP进行扩展就可适用于各种NAT。ICE是通过综合运用上面某几种协议,使之在最适合的情况下工作,以弥补单独使用其中任何一种所带来的固有缺陷。ICE跟STUN和TURN不一样,ICE不是一种协议,而是一个framework,它整合了STUN和TURN。使用ICE方式穿透NAT,必须映射ICE定义的参数到SIP消息格式中,同时对其SDP属性进行简单扩展—在SDP的Media块中定义一个新的属性“alt”来支持ICE。它包含一个候选IP地址和端口,SDP的接受端可以用该地址来替换m和c中的地址。Media块中可能会有多个alt属性,这时每个alt应该包括不重复的IP地址和端口。 对于SIP来说,ICE只需要定义一些SDP(Session Description Protocol)附加属性即可,对于别的多媒体信令协议也需要制定一些相应的机制来实现。其思想是:建立媒体流信道时,发出很多种选择,有本地端口,STUN端口,TURN端口,并给出这些端口的优先级,由被叫方自主选择端口,根据一定的算法和联通性测试,选出最好的端口来通信。

六.p2p打洞实测

下面通过两个程序来测试p2p打洞并传输实时视频流,一个作为播放端的p2pclient,另一个是提供rtsp视频服务的p2pdevice,p2pclient和p2pdevice分别运行于不同的内网机器上。打洞成功后,p2pclient直接从p2pdevice获取rtsp视频流,并由vlc播放。

rtsp协议本身支持用udp或tcp传输实际的视频流,由于我们的p2p底层是由udp实现的,因此为了简单,我们这里配置vlc播放器使用tcp模式: 运行VLC media player后 ,选择工具—偏好设置—输入/编解码器。在最下面的live555流传输中选择 RTP over RTSP (TCP), 如下图:

下面先看程序p2pclient的代码,以下示例代码在windows下用vc2017或以上编译并执行。

/*
本P2PSDK主要用于实时音视频传输
支持linux、安卓、IOS和windows平台
支持c语言,c++,c#,java,unity等环境下使用
rtmp或rtsp等由tcp或udp开发的应用均能接入,配置简单接口的详细说明参见另外的博客说明:
https://blog.csdn.net/xiehuanbin/article/details/128917721有问题请联系qq: 390090739
*/
#include "stdafx.h"
#include "p2papi.h"int g_nlocalport=0;
int DEVICE_ID   = 1000;
int g_nP2PSetUp = 0;int main(int argc, char* argv[])
{int frame_index = 0;int64_t start_time = 0;if (argc >= 2)DEVICE_ID = atoi(argv[1]);printf("my deviceid = %d\n", DEVICE_ID);p2p_engine_init("43.142.138.68",20000,"d:/p2p.log");//"192.168.3.166"int sessionID = p2p_get_free_session();CP2PHoleSink sink;p2p_set_sink(sessionID,&sink);//p2p_set_log_level(2);g_nlocalport = 49999;p2p_start_proxy(sessionID, g_nlocalport,1);int64_t p2puid = DEVICE_ID | 0x1000000000000000;p2p_connect_server(sessionID, p2puid, "");printf("local server port=%d\n", g_nlocalport);while (1){SleepEx(10,TRUE);}p2p_stop_proxy(sessionID);p2p_engine_destroy();return 0;
}

CP2PHoleSink是一个回调类,打洞状态发生变化时将回调此接口。

class CP2PHoleSink : public IP2PSessionSink
{
public:CP2PHoleSink(){}~CP2PHoleSink(){}virtual void onServerConnected(int session, int state){p2p_login_server(session, DEVICE_ID);}virtual void  onLoginServer(int session, int state){//13是要连接的设备IDif (g_nP2PSetUp==0)p2p_connect_peer(session,13);}// < 0 p2p打洞失败,后续将连接中转服务器    // 0 p2p打洞失败,服务器中转建立失败或中转连接断开    // 1 p2p打洞成功,使用外网IP,后续可以创建通道或者直接收发udp数据// 2 p2p打洞成功,使用内网IP,后续可以创建通道或者直接收发udp数据// 3 p2p打洞失败,服务器中转已经建立virtual void onHoleState(int session, int64_t uid, int state, int reserved){        g_nP2PSetUp = state;if (state < 0)printf("打洞失败,正在连接中转服务器...\n");else if (state == 0)printf("与中转服务器的连接断开...\n");else if (state == 1 || state == 2)printf("打洞成功.\n");else if (state == 3)printf("打洞失败,服务器中转连接已经建立.\n");elseprintf("打洞错误,state=%d.\n", state);if (state > 0){/*//通道100://默认为 P2P_OPTION_UDP_MODEL 模式,不需要调用p2p_create_channel,就可以直接发送数据p2p_send(session, uid, 100, "test data", 9);//通道101://通过调用p2p_create_channel,改为 P2P_OPTION_TCP_MODEL 模式p2p_create_channel(session, uid, 101);p2p_send(session, uid, 101, "testtcp", 7);*/}}virtual void onRecvServerData(int session, int64_t cmd, int subcmd, const unsigned char * pDataBuffer, int wDataSize){}virtual void onRecvP2PData(int session, int64_t uid, int channel, const unsigned char * pDataBuffer, int wDataSize){}virtual void onChannelState(int session, int64_t uid, int channel, int state){}virtual void onProxyStarted(int session, int port){}
};

接着再看程序p2pdevice的代码,以下示例代码在windows下用vc2017或以上编译并执行。

/*
本P2PSDK主要用于实时音视频传输
支持linux、安卓、IOS和windows平台
支持c语言,c++,c#,java,unity等环境下使用
rtmp或rtsp等由tcp或udp开发的应用均能接入,配置简单接口的详细说明参见另外的博客说明:
https://blog.csdn.net/xiehuanbin/article/details/128917721有问题请联系qq: 390090739
*/
#include "stdafx.h"
#include "p2papi.h"
#include "livemedia.h"int DEVICE_ID   = 13;
int g_nP2PSetUp = 0;int main(int argc, char* argv[])
{int frame_index = 0;int64_t start_time = 0;if (argc >= 2)DEVICE_ID = atoi(argv[1]);p2p_engine_init("43.142.138.68",20000,"d:/p2pserver.log");//"192.168.3.166"DWORD last_update_time = GetTickCount();DWORD last_framenum_update = last_update_time;DWORD nFrameNum = 0;int initVal = 0;printf("my deviceid = %d\n", DEVICE_ID );int sessionID = p2p_get_free_session();CP2PHoleSink sink;p2p_set_sink(sessionID,&sink);p2p_set_log_level(3);//设备授权ID和密钥:4294967390,49uozeazd9//此授权ID与p2p_login_server中的设备ID不同//8589934684,lg27kun886p2p_connect_server(sessionID, 4294967390, "49uozeazd9");int serviceport = start_rtsp_server(sessionID,0);p2p_set_device_port(sessionID, serviceport);while (initVal == 0){rtsp_server_update();SleepEx(10,TRUE);}p2p_stop_proxy(sessionID);stop_rtsp_server();p2p_engine_destroy();return 0;
}

CP2PHoleSink和p2pclient里面 定义一样,是一个回调类,打洞状态发生变化时将回调此接口。


class CP2PHoleSink : public IP2PSessionSink
{
public:CP2PHoleSink()    {    }~CP2PHoleSink()    {    }virtual void onServerConnected(int session, int state){p2p_login_server(session, DEVICE_ID);}virtual void  onLoginServer(int session, int state){}virtual void onProxyStarted(int session, int port){}// < 0 p2p打洞失败,后续将连接中转服务器    // 0 p2p打洞失败,服务器中转建立失败或中转连接断开    // 1 p2p打洞成功,使用外网IP,后续可以创建通道或者直接收发udp数据// 2 p2p打洞成功,使用内网IP,后续可以创建通道或者直接收发udp数据// 3 p2p打洞失败,服务器中转已经建立virtual void onHoleState(int session, int64_t uid, int state, int reserved){        g_nP2PSetUp=state;    if (state < 0)printf("打洞失败,正在连接中转服务器...\n");else if (state == 0)printf("与中转服务器的连接断开...\n");else if (state == 1 || state == 2)printf("打洞成功.\n");else if (state == 3)printf("打洞失败,服务器中转连接已经建立.\n");elseprintf("打洞错误,state=%d.\n", state );}virtual void onRecvServerData(int session, int64_t cmd, int subcmd, const unsigned char * pDataBuffer, int wDataSize){}virtual void onRecvP2PData(int session, int64_t uid, int channel, const unsigned char * pDataBuffer, int wDataSize){}virtual void onChannelState(int session, int64_t uid, int channel, int state){}
};

p2pdevice编译后运行截图:

p2pclient编译后运行截图:

打洞成功后,就可以使用vcl media player来播放rtsp视频了, 如有任何凝问,加我qq:390090739。

测试时必须先运行p2pdevice程序,再运行p2pclient程序,然后运行vcl media player,打开vcl后,选择 媒体—打开网络串流—网络,输入地址rtsp://127.0.0.1:49999/video/test.264,然后点“播放”按扭, 既可播放p2pdevice提供的视频流。

点击下面的链接,下载完整的示例代码,可以自己编译,测试p2p打洞效果。

完整测试代码下载

p2p打洞源码,p2p内网穿透源码,NAT内网穿透源码,NAT穿透源码相关推荐

  1. UDP 构建p2p打洞过程的实现原理(持续更新)

    UDP 构建p2p打洞过程的实现原理(持续更新) 发表于7个月前(2015-01-19 10:55)   阅读(433) | 评论(0) 8人收藏此文章, 我要收藏 赞0 8月22日珠海 OSC 源创 ...

  2. p2p打洞之nat分类

    最近学习了一些p2p的知识,这里做一些笔记.p2p首先要区分网络类型,不同的网络类型有不同的打洞的方式. 1.完全锥形 所谓的完全锥形其实表达得很形象,就是内网机器在局域网内打了一个洞,然后通过这个洞 ...

  3. 【房间墙上凿个洞,看你在干嘛~】安全攻防内网渗透-绕过防火墙和安全检测,搭建DNS隐蔽隧道

    作者:Eason_LYC 悲观者预言失败,十言九中. 乐观者创造奇迹,一次即可. 一个人的价值,在于他所拥有的.所以可以不学无术,但不能一无所有! 技术领域:WEB安全.网络攻防 关注WEB安全.网络 ...

  4. P2P打洞服务器与客户端

    本文从以下四个部分开始分析讲解,客户端与服务端的源码在文章末尾链接 一.P2P打洞的原理 二.P2P服务器的实现 三.P2P客户端的实现 四.数据包格式 一.P2P打洞原理 #####1.打洞解决了什 ...

  5. 【微电网优化】基于matlab粒子群算法求解微网经济调度和环境友好调度优化问题【含Matlab源码 2283期】

    ⛄一.获取代码方式 获取代码方式1: 完整代码已上传我的资源:[微电网优化]基于matlab粒子群算法求解微网经济调度和环境友好调度优化问题[含Matlab源码 2283期] 点击上面蓝色字体,直接付 ...

  6. php手机发卡,PHP最新金发卡企业级发卡平台整站源码(自适应手机端) 支付通道齐全 运营级自动发卡完整版源码 发卡网源码...

    [温馨提示]源码包解压密码:www.youhutong.com 资源描述 PHP最新金发卡企业级发卡平台整站源码(自适应手机端) 支付通道齐全 运营级自动发卡完整版源码 发卡网源码 安装教程: 环境p ...

  7. 做一个p2p打洞的C#程序

    做一个p2p打洞的C#程序 转载于:https://www.cnblogs.com/cuihongyu3503319/archive/2011/03/15/1985021.html

  8. 设计类超实用的导航网站,一网包含1000+个行业内热门资讯灵感源!

    一个新手设计师日常工作需要的设计素材网站到实用工具网站,按照习惯大家可能会将网址保 存下来以备用时之需.收藏这么多,在搜索的时候,我们还是会漫无目的. 一流设计导航|16map,把同类的网站都分类整理 ...

  9. P2P打洞原理(二十二)

    一.P2P打洞原理 1.打洞解决了什么问题? 我们平常使用的一般都为私有ip,但是私有ip之间是不能直接通信的,如果要进行通信只能通过公网上的服务器进行数据的转发,难道我们每次发送数据都要经过公网上的 ...

最新文章

  1. 词向量之BERT 结构
  2. 微软获 OpenAI 独家 GPT-3 模型授权,是潘多拉还是聚宝盆?
  3. Java语法基础50题训练(下)
  4. #6682. 梦中的数论(Min25筛)
  5. 怎么用计算机弹c哩c哩,计算器音乐c哩c哩乐谱 | 手游网游页游攻略大全
  6. 用户体验是非常难琢磨的东西
  7. 如何生成MD5哈希?
  8. 程序员的发展方向是什么?
  9. IPSEC 安全连接
  10. 01 前言/基础设施 - DevOps之路
  11. java 密码加密_Java如何实现密码加密
  12. sdf贴图方式之tri-planar贴法
  13. 双非渣本,他是如何逆袭拿到3W高薪
  14. Linux conda tensorflow-gpu安装及Not creating XLA devices, tf_xla_enable_xla_devices not set相关问题解决
  15. python的requests爬取Uniprot中蛋白序列和N-糖基化位点
  16. 第1关:ZooKeeper初体验
  17. 零基础入门推荐系统 - 新闻推荐实战-笔记四
  18. 使用 redis 连接指定端口的 redis 数据库
  19. Linux ARM平台开发系列讲解(GMSL摄像头篇)1.2 MAX9296 GMSL链路配置
  20. linux centos 查看内存使用情况

热门文章

  1. java语言 获取本机的ip地址
  2. 手动更新(rpi-update)树莓派固件
  3. html标签属性语音,HTML area 标签 media 属性
  4. delphi怎么获得文件服务器,delphi做web服务器
  5. 右键文件夹时资源管理器重启
  6. 【c语言】职工信息管理系统 包含读取写入txt文件,职工信息的增删改查
  7. java计算机毕业设计springboot+vue线上教学辅助系统
  8. linux 安装pyodbc 报错
  9. pyqt使用mysql提示 “Driver not loaded Driver not loaded”
  10. 解决ubuntu16.04 USB鼠标键盘使用卡顿