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

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

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

$R.Mu#M%_B%O x0

/N4|+hz,\(fDh&~0客户端B收到服务器S的打洞通知后,先连接S的【协助打洞】端口号(本地端口号可以用GetSocketName()函数取得,假设为X),启动线程尝试连接客户端A的公网IP和端口号,根据路由器不同,连接情况各异,如果运气好直接连接就成功了,即使连接失败,但打洞便完成了。同时还要启动线程在相同的端口(即与S的【协助打洞】端口号建立连接的本地端口号X)上侦听到来的连接,等待客户端A直接连接该端口号。pcMing工作室ua P"D_%`{,j

//
// 执行者:客户端A
// 服务器要求主动端(客户端A)直接连接被动端(客户端B)的外部IP和端口号
//
BOOL Handle_SrvReqDirectConnect ( t_SrvReqDirectConnectPkt *pSrvReqDirectConnectPkt )
{
	ASSERT ( pSrvReqDirectConnectPkt );
	printf ( "You can connect direct to ( IP:%s  PORT:%d  ID:%u )\n", pSrvReqDirectConnectPkt->szInvitedIP,
		pSrvReqDirectConnectPkt->nInvitedPort, pSrvReqDirectConnectPkt->dwInvitedID );

	// 直接与客户端B建立TCP连接,如果连接成功说明TCP打洞已经成功了。
	CSocket Sock;
	try
	{
		if ( !Sock.Socket () )
		{
			printf ( "Create socket failed : %s\n", hwFormatMessage(GetLastError()) );
			return FALSE;
		}
		UINT nOptValue = 1;
		if ( !Sock.SetSockOpt ( SO_REUSEADDR, &nOptValue , sizeof(UINT) ) )
		{
			printf ( "SetSockOpt socket failed : %s\n", hwFormatMessage(GetLastError()) );
			return FALSE;
		}
		if ( !Sock.Bind ( g_nHolePort ) )
		{
			printf ( "Bind socket failed : %s\n", hwFormatMessage(GetLastError()) );
			return FALSE;
		}
		for ( int ii=0; ii<100; ii++ )
		{
			if ( WaitForSingleObject ( g_hEvt_ConnectOK, 0 ) == WAIT_OBJECT_0 )
				break;
			DWORD dwArg = 1;
			if ( !Sock.IOCtl ( FIONBIO, &dwArg ) )
			{
				printf ( "IOCtl failed : %s\n", hwFormatMessage(GetLastError()) );
			}
			if ( !Sock.Connect ( pSrvReqDirectConnectPkt->szInvitedIP, pSrvReqDirectConnectPkt->nInvitedPort ) )
			{
				printf ( "Connect to [%s:%d] failed : %s\n", 
					pSrvReqDirectConnectPkt->szInvitedIP, 
					pSrvReqDirectConnectPkt->nInvitedPort, 
					hwFormatMessage(GetLastError()) );
					Sleep (100);
			}
			else break;
		}
		if ( WaitForSingleObject ( g_hEvt_ConnectOK, 0 ) != WAIT_OBJECT_0 )
		{
			if ( HANDLE_IS_VALID ( g_hEvt_ConnectOK ) ) SetEvent ( g_hEvt_ConnectOK );
			printf ( "Connect to [%s:%d] successfully !!!\n", 
			pSrvReqDirectConnectPkt->szInvitedIP, pSrvReqDirectConnectPkt->nInvitedPort );
			
			// 接收测试数据
			printf ( "Receiving data ...\n" );
			char szRecvBuffer[NET_BUFFER_SIZE] = {0};
			int nRecvBytes = 0;
			for ( int i=0; i<1000; i++ )
			{
				nRecvBytes = Sock.Receive ( szRecvBuffer, sizeof(szRecvBuffer) );
				if ( nRecvBytes > 0 )
				{
					printf ( "-->>> Received Data : %s\n", szRecvBuffer );
					memset ( szRecvBuffer, 0, sizeof(szRecvBuffer) );
					SLEEP_BREAK ( 1 );
				}
				else
				{
					SLEEP_BREAK ( 300 );
				}
			}
		}
	}
	catch ( CException e )
	{
		char szError[255] = {0};
		e.GetErrorMessage( szError, sizeof(szError) );
		printf ( "Exception occur, %s\n", szError );
		return FALSE;
	}

	return TRUE;
}
pcMing工作室@a cr+v ~1L

在客户端B打洞和侦听准备好以后,服务器S回复客户端A,客户端A便直接与客户端B的公网IP和端口进行连接,收发数据可以正常进行,为了测试是否真正地直接TCP连接,在数据收发过程中可以将服务器S强行终止,看是否数据收发还正常进行着。pcMing工作室A9U$U0k [`M

pcMing工作室"{5Y3[2u$}X

顶:123 踩:146
对本文中的事件或人物打分:
当前平均分:-0.09 (774次打分)
对本篇资讯内容的质量打分:
当前平均分:-0.19 (688次打分)
【已经有700人表态】
108票
感动
79票
路过
88票
高兴
75票
难过
85票
搞笑
85票
愤怒
94票
无聊
86票
同情
上一篇 下一篇
发表评论
换一张

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

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

网络资源