你的位置:pcMing工作室 >> 资讯 >> 编程开发 >> C++编程 >> 详细内容 在线投稿

TCP实现P2P通信、TCP穿越NAT的方法、TCP打洞

排行榜 收藏 打印 发给朋友 举报 来源: 互联网   发布者:未知
热度2467票  浏览1013次 【共0条评论】【我要评论 时间:2010年4月08日 20:18
pcMing工作室 P9Fl5v)}z9ECn6o

pcMing工作室ei)A)VB6\a

这里需要介绍一下NAT的类型:pcMing工作室Y ^"s!R0x pm.N C(`-R
NAT设备的类型对于TCP穿越NAT,有着十分重要的影响,根据端口映射方式,NAT可分为如下4类,前3种NAT类型可统称为cone类型。
U_ lio,w%g,Hs a0(1)全克隆( Full Cone) : NAT把所有来自相同内部IP地址和端口的请求映射到相同的外部IP地址和端口。任何一个外部主机均可通过该映射发送IP包到该内部主机。
pV#FZY NE3G0(2)限制性克隆(Restricted Cone) : NAT把所有来自相同内部IP地址和端口的请求映射到相同的外部IP地址和端口。但是,只有当内部主机先给IP地址为X的外部主机发送IP包,该外部主机才能向该内部主机发送IP包。
0d&]&Z:q XF)zi0(3)端口限制性克隆( Port Restricted Cone) :端口限制性克隆与限制性克隆类似,只是多了端口号的限制,即只有内部主机先向IP地址为X,端口号为P的外部主机发送1个IP包,该外部主机才能够把源端口号为P的IP包发送给该内部主机。
K'F7h;` mR c0(4)对称式NAT ( Symmetric NAT) :这种类型的NAT与上述3种类型的不同,在于当同一内部主机使用相同的端口与不同地址的外部主机进行通信时, NAT对该内部主机的映射会有所不同。对称式NAT不保证所有会话中的私有地址和公开IP之间绑定的一致性。相反,它为每个新的会话分配一个新的端口号。pcMing工作室/n1D%m ZR|

K9`$uZ"o0我们先假设一下:有一个服务器S在公网上有一个IP,两个私网分别由NAT-A和NAT-B连接到公网,NAT-A后面有一台客户端A,NAT-B后面有一台客户端B,现在,我们需要借助S将A和B建立直接的TCP连接,即由B向A打一个洞,让A可以沿这个洞直接连接到B主机,就好像NAT-B不存在一样。
P:Vy0Ni:N]0实现过程如下(请参照源代码):pcMing工作室4o"k,E)E6tx
1、 S启动两个网络侦听,一个叫【主连接】侦听,一个叫【协助打洞】的侦听。
1^,rrM5{T fS)w02、 A和B分别与S的【主连接】保持联系。pcMing工作室D v0mFmvM
3、 当A需要和B建立直接的TCP连接时,首先连接S的【协助打洞】端口,并发送协助连接申请。同时在该端口号上启动侦听。注意由于要在相同的网络终端上绑定到不同的套接字上,所以必须为这些套接字设置 SO_REUSEADDR 属性(即允许重用),否则侦听会失败。pcMing工作室 ?0A1@'v,Ym/d s~ J;D @
4、 S的【协助打洞】连接收到A的申请后通过【主连接】通知B,并将A经过NAT-A转换后的公网IP地址和端口等信息告诉B。
YB8Iy2c05、 B收到S的连接通知后首先与S的【协助打洞】端口连接,随便发送一些数据后立即断开,这样做的目的是让S能知道B经过NAT-B转换后的公网IP和端口号。pcMing工作室5un9Cc;cxg ~ G#L
6、 B尝试与A的经过NAT-A转换后的公网IP地址和端口进行connect,根据不同的路由器会有不同的结果,有些路由器在这个操作就能建立连接(例如我用的TPLink R402),大多数路由器对于不请自到的SYN请求包直接丢弃而导致connect失败,但NAT-A会纪录此次连接的源地址和端口号,为接下来真正的连接做好了准备,这就是所谓的打洞,即B向A打了一个洞,下次A就能直接连接到B刚才使用的端口号了。
J.B8d {}gWI|f z-c07、 客户端B打洞的同时在相同的端口上启动侦听。B在一切准备就绪以后通过与S的【主连接】回复消息“我已经准备好”,S在收到以后将B经过NAT-B转换后的公网IP和端口号告诉给A。
I]!^I#n08、 A收到S回复的B的公网IP和端口号等信息以后,开始连接到B公网IP和端口号,由于在步骤6中B曾经尝试连接过A的公网IP地址和端口,NAT-A纪录了此次连接的信息,所以当A主动连接B时,NAT-B会认为是合法的SYN数据,并允许通过,从而直接的TCP连接建立起来了。

O0V [y9J9xY(P`0

-t(c/x xBU:x{0整个实现过程靠文字恐怕很难讲清楚,再加上我的语言表达能力很差(高考语文才考75分,总分150分,惭愧),所以只好用代码来说明问题了。
$WLIKg%BA/W0

]s:yR3JOti`L0
// 服务器地址和端口号定义
#define SRV_TCP_MAIN_PORT		4000	// 服务器主连接的端口号
#define SRV_TCP_HOLE_PORT		8000	// 服务器响应客户端打洞申请的端口号

| l^}b0这两个端口是固定的,服务器S启动时就开始侦听这两个端口了。pcMing工作室F"U1BXz2Fz~

//
// 将新客户端登录信息发送给所有已登录的客户端,但不发送给自己
//
BOOL SendNewUserLoginNotifyToAll ( LPCTSTR lpszClientIP, UINT nClientPort, DWORD dwID )
{
	ASSERT ( lpszClientIP && nClientPort > 0 );
	g_CSFor_PtrAry_SockClient.Lock();
	for ( int i=0; i<g_PtrAry_SockClient.GetSize(); i++ )
	{
		CSockClient *pSockClient = (CSockClient*)g_PtrAry_SockClient.GetAt(i);
		if ( pSockClient && pSockClient->m_bMainConn && pSockClient->m_dwID > 0 && pSockClient->m_dwID != dwID )
		{
			if ( !pSockClient->SendNewUserLoginNotify ( lpszClientIP, nClientPort, dwID ) )
			{
				g_CSFor_PtrAry_SockClient.Unlock();
				return FALSE;
			}
		}
	}

	g_CSFor_PtrAry_SockClient.Unlock ();
	return TRUE;
}
pcMing工作室_IFaG/y

顶:125 踩:148
对本文中的事件或人物打分:
当前平均分:-0.08 (786次打分)
对本篇资讯内容的质量打分:
当前平均分:-0.18 (698次打分)
【已经有710人表态】
109票
感动
80票
路过
89票
高兴
78票
难过
87票
搞笑
85票
愤怒
95票
无聊
87票
同情
上一篇 下一篇
发表评论
换一张

网友评论仅供网友表达个人看法,并不表明本网同意其观点或证实其描述。

查看全部回复【已有0位网友发表了看法】

网络资源