首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > .NET > C# >

Tcp p2p 点对点 穿透 战败!该如何处理

2012-02-17 
Tcp p2p 点对点 穿透 战败!前几天在做一个文件点对点传输的项目,涉及到NAT穿透,从上个星期到今天一直在调

Tcp p2p 点对点 穿透 战败!
前几天在做一个文件点对点传输的项目,涉及到NAT穿透,从上个星期到今天一直在调试和测试,最后到今天,看来已经战败!

以下是两篇是我作为技术依据的文章:

http://blog.csdn.net/ssihc0/archive/2008/10/10/3053395.aspx
http://hi.baidu.com/wangzhe1945/blog/item/3e72fffe47fc2f365d60080c.html
http://hi.baidu.com/wangzhe1945/blog/item/5ccd3fa4e3ee67f09152ee38.html

总的技术原理归纳如下:
首先还是 AB分别和服务器S分别建立连接,S记录AB的互联网实际终端。然后S分别向AB发送对方的实际终端。接着,从A和B向S连接时使用的端口,AB都异步调用connect函数连接对方的实际终端(就是S告诉的终端),同时,AB双方都在同一个本地端口监听到来的连接(也可以先监听,再connect更好)。由于双方都向对方发送了connect请求(假设各自的SYN封包已经穿过了自己的NAT),因此在对方connect请求到达本地的监听端口时,路由器会认为这个请求是刚刚那个connect会话的一部分,是已经被许可的,本地监听端口就会用SYN-ACK响应,同意连接。这样,TCP穿透NAT的点对点连接就成功了。 

自己写的代码穿透失败。下载了别人p2p TCP 穿透的代码 http://download.csdn.net/source/700961 ,发现如果2个机器在同一个局域网(同一内网)内是可以传输文件,但是不同的陆游器(不同内网)下,穿透不成功。


我不是高手,p2p TCP 穿透 战败。

[解决办法]
不要泄气,继续坚持
[解决办法]
找原因,呵呵
[解决办法]

探讨
不要泄气,继续坚持

[解决办法]
没搞过,帮顶,继续努力
[解决办法]
A B都开启一个服务端口侦听,才能内网对内网的PtoP
A B开启的端口是多少是S服务知道的
要想联接,问S对方端口就行了。
[解决办法]
加油
[解决办法]
TCP 我也没搞定。。。。琢磨很久了。。。
[解决办法]
关键还是底层的原理了
从这方面找找突破口看看
[解决办法]
tcp的好像比较难。udp实现起来比较简单,要有一台在公网的机器做服务器,它用来当有内网的机器存在时帮助建立连接。
[解决办法]
进来学习!
[解决办法]
up
[解决办法]
学习下``
[解决办法]
up
[解决办法]
lz加油,期待解决后放码啊
[解决办法]
加油!!!!!
[解决办法]
S放的是A和B的NAT服务地址,S只知道A和B的公网地址,他不管A和B的私有地址,NAT负责把公网地址转为A和B的内部私有地址

A和B 的私有地址是不能通过路由的。
[解决办法]
RFC
http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt
[解决办法]
坚持就是一种胜利
[解决办法]

[解决办法]
tcp的比较难。udp实现起来比较简单
[解决办法]
有时间再看
[解决办法]
继续努力。
[解决办法]
努力了,失败也是成功

[解决办法]
留名,等高手解答再来看...
[解决办法]


加油!!
[解决办法]
关注下
[解决办法]
学习
[解决办法]
lz加油啊,我这方面帮不了你了
[解决办法]
up

[解决办法]
第一次来论坛,请多多关照
[解决办法]
学习,请多多关照
[解决办法]
也请关照我。。。
跟楼主一起学习
[解决办法]
我也要努力搞这个,很有意思啊
[解决办法]
坚持
[解决办法]

探讨
第一次来论坛,请多多关照

[解决办法]
up
[解决办法]
UP
[解决办法]
并非所的路由器都支持NAT穿透啊!
[解决办法]
参考实现在同一 LAN 内可以通信很简单,只要在连接服务器时同时告知自己的内网 IP/Port 就可以了。服务器比较得知两个客户端的外网 IP 相同则应在同一 LAN 内,因此告知对方的内网 EP,自然可以直接连接。
跨 LAN 不能连接说明打洞失败。
TCP 打洞可能要求 raw socket ,权限可能有问题(在 XP SP2/SP3 下据说有权限问题,禁止了 raw socket,只有驱动可以使用)
[解决办法]
学习
[解决办法]
UDP的容易实现,TCP的不知道。
[解决办法]
不要泄气,继续坚持
[解决办法]
最近正在学习这个tcp穿透NAT的东东。
坚持就是胜利,LZ
[解决办法]
你一定可以的!慢慢想解决的办法。
[解决办法]
学习中
[解决办法]
......
[解决办法]
进来学习`LZ不要泄气`
[解决办法]
这一块也不怎么会,学习
[解决办法]
支持楼主,加油
[解决办法]
楼主加油 呵呵..
[解决办法]
顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶
[解决办法]
b bb
[解决办法]
add oil and study
[解决办法]
不要泄气,继续坚持
[解决办法]
为什么失败,说说我的理解吧,可能不对
这里是nat地解释
http://en.wikipedia.org/wiki/Network_address_translation
里面有个drawbacks,就是nat的局限性

client1 client1 server client2 server client2
[192.168.1.1] ---- [202.102.122.3] ----- [130.21.42.4]----[10.1.1.15]


\/
\ /
[68.102.31.7]
server

当client1(192.168.1.1)发起请求,server(68.102.31.7)知道client1的地址和端口,但这个是client映射到client1 server(202.102.122.3)的地址和端口
同样client2发起请求,server知道的是client2 server的地址和端口

当client1(192.168.1.1)向client2(10.1.1.15)发起p2p连接请求的话,其实是向client2 server(130.21.42.4)发起连接请求,nat的局限性就说过了外网一般不能发起连接请求(除非作特殊处理,比如端口映射,双向nat等)
因为client server2并不知道外网的想连接内网的那台机器,这也是nat具有一定防火墙功能,可以有效的阻止外网请求


[解决办法]

[解决办法]
我这有一些资料,准备考完试,也就是3天后做一个P2P视频直播的小软件
还希望多多指导.Email: wptad$tom.com
给你一个TCP的例子,希望对你有所帮助.
Clt如下:

C/C++ code
#include "stdafx.h" #include "TcpHoleClt.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif HANDLE m_hEvtEndModule = NULL; HANDLE g_hThread_Main = NULL; CSocket *g_pSock_Main = NULL; HANDLE g_hThread_MakeHole = NULL; CSocket *g_pSock_MakeHole = NULL; HANDLE g_hThread_Listen = NULL; CSocket *g_pSock_Listen = NULL; char *g_pServerAddess = "callgle.xicp.net"; // 服务器地址 // 我自己的客户端信息 t_WelcomePkt g_WelcomePkt; UINT g_nHolePort = 0; HANDLE g_hEvt_MakeHoleFinished = NULL; // 打洞操作已经完成,可以让主动端(客户端A)来连接了 HANDLE g_hEvt_ListenFinished = NULL; // 侦听任务已启动 HANDLE g_hEvt_ConnectOK = NULL; // 连接建立了,这个事件来通知其他线程停止连接尝试 // // 执行者:客户端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, &amt;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, &amt;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; } 


[解决办法]
// 
// 执行者:客户端A、客户端B 
// 侦听线程函数。 
// 打洞开始后,客户端还同时启动一个侦听,接收来自端口 g_nHolePort 的连接请求 
// 
DWORD WINAPI ThreadProc_Listen( 
LPVOID lpParameter // thread data 


ASSERT ( HANDLE_IS_VALID(g_hEvt_ListenFinished) &amt;&amt; HANDLE_IS_VALID(g_hEvt_MakeHoleFinished) ); 
printf ( "Client.>u will listen at port >u\n", g_WelcomePkt.dwID, g_nHolePort ); 

BOOL bRet = FALSE; 
CSocket Sock; 
// 创建Socket,侦听来自端口 g_nHolePort 的连接请求 
try 

if ( !Sock.Socket () ) 

printf ( "Create socket failed : >s\n", hwFormatMessage(GetLastError()) ); 
goto finished; 

UINT nOptValue = 1; 
if ( !Sock.SetSockOpt ( SO_REUSEADDR, &amt;nOptValue , sizeof(UINT) ) ) 

printf ( "SetSockOpt socket failed : >s\n", hwFormatMessage(GetLastError()) ); 
goto finished; 

if ( !Sock.Bind ( g_nHolePort ) ) 

printf ( "Bind socket failed : >s\n", hwFormatMessage(GetLastError()) ); 
goto finished; 

if ( !Sock.Listen () ) 

printf ( "Listen failed : >s\n", hwFormatMessage(GetLastError()) ); 
goto finished; 

printf ( "Start TCP server listen port : >u\n", g_nHolePort ); 
g_pSock_Listen = &amt;Sock; 
if ( HANDLE_IS_VALID(g_hEvt_ListenFinished) ) 
SetEvent ( g_hEvt_ListenFinished ); 

CSocket sockAct; 
if ( Sock.Accept ( sockAct ) ) 

CString csSocketAddress; 
UINT nPort = 0; 
if ( !sockAct.GetPeerName ( csSocketAddress, nPort ) ) 

printf ( "GetPeerName failed : >s\n", hwFormatMessage(GetLastError()) ); 

else 

if ( HANDLE_IS_VALID ( g_hEvt_ConnectOK ) ) SetEvent ( g_hEvt_ConnectOK ); 
printf ( "Client.>u accept >s:>u\n", g_WelcomePkt.dwID, csSocketAddress, nPort ); 
// 发送测试数据 
printf ( "Sending data ...\n" ); 
char szBuf[1024] = {0}; 
for ( int i=0; i<10; i++ ) 

int nLen = _snprintf ( szBuf, sizeof(szBuf), "Line.>04d - Test Data", i ); 
if ( sockAct.Send ( szBuf, nLen ) != nLen ) 

printf ( "Send data failed : >s\n", hwFormatMessage(GetLastError()) ); 
break; 

else 

printf ( "Sent Data : >s -->>>\n", szBuf ); 
SLEEP_BREAK ( 300 ); 





catch ( CException e ) 

char szError[255] = {0}; 
e.GetErrorMessage( szError, sizeof(szError) ); 
printf ( "Exception occur, >s\n", szError ); 
goto finished; 


bRet = TRUE; 

finished: 
printf ( "ThreadProc_Listen end\n" ); 
return bRet; 


// 
// 执行者:客户端A 
// 有新客户端B登录了,我(客户端A)连接服务器端口 SRV_TCP_HOLE_PORT ,申请与客户端B建立直接的TCP连接 
// 
BOOL Handle_NewUserLogin ( CSocket &amt;MainSock, t_NewUserLoginPkt *pNewUserLoginPkt ) 

printf ( "New user ( >s:>u:>u ) login server\n", pNewUserLoginPkt->szClientIP, 
pNewUserLoginPkt->nClientPort, pNewUserLoginPkt->dwID ); 

BOOL bRet = FALSE; 
DWORD dwThreadID = 0; 
t_ReqConnClientPkt ReqConnClientPkt; 
CSocket Sock; 
CString csSocketAddress; 


char szRecvBuffer[NET_BUFFER_SIZE] = {0}; 
int nRecvBytes = 0; 
// 创建打洞Socket,连接服务器协助打洞的端口号 SRV_TCP_HOLE_PORT 
try 

if ( !Sock.Socket () ) 

printf ( "Create socket failed : >s\n", hwFormatMessage(GetLastError()) ); 
goto finished; 

UINT nOptValue = 1; 
if ( !Sock.SetSockOpt ( SO_REUSEADDR, &amt;nOptValue , sizeof(UINT) ) ) 

printf ( "SetSockOpt socket failed : >s\n", hwFormatMessage(GetLastError()) ); 
goto finished; 

if ( !Sock.Bind ( 0 ) ) 

printf ( "Bind socket failed : >s\n", hwFormatMessage(GetLastError()) ); 
goto finished; 

if ( !Sock.Connect ( g_pServerAddess, SRV_TCP_HOLE_PORT ) ) 

printf ( "Connect to [>s:>d] failed : >s\n", g_pServerAddess, SRV_TCP_HOLE_PORT, hwFormatMessage(GetLastError()) ); 
goto finished; 


catch ( CException e ) 

char szError[255] = {0}; 
e.GetErrorMessage( szError, sizeof(szError) ); 
printf ( "Exception occur, >s\n", szError ); 
goto finished; 

g_pSock_MakeHole = &amt;Sock; 
ASSERT ( g_nHolePort == 0 ); 
VERIFY ( Sock.GetSockName ( csSocketAddress, g_nHolePort ) ); 

// 创建一个线程来侦听端口 g_nHolePort 的连接请求 
dwThreadID = 0; 
g_hThread_Listen = ::CreateThread ( NULL, 0, ::ThreadProc_Listen, LPVOID(NULL), 0, &amt;dwThreadID ); 
if (!HANDLE_IS_VALID(g_hThread_Listen) ) return FALSE; 
Sleep ( 3000 ); 

// 我(客户端A)向服务器协助打洞的端口号 SRV_TCP_HOLE_PORT 发送申请,希望与新登录的客户端B建立连接 
// 服务器会将我的打洞用的外部IP和端口号告诉客户端B 
ASSERT ( g_WelcomePkt.dwID > 0 ); 
ReqConnClientPkt.dwInviterID = g_WelcomePkt.dwID; 
ReqConnClientPkt.dwInvitedID = pNewUserLoginPkt->dwID; 
if ( Sock.Send ( &amt;ReqConnClientPkt, sizeof(t_ReqConnClientPkt) ) != sizeof(t_ReqConnClientPkt) ) 
goto finished; 

// 等待服务器回应,将客户端B的外部IP地址和端口号告诉我(客户端A) 
nRecvBytes = Sock.Receive ( szRecvBuffer, sizeof(szRecvBuffer) ); 
if ( nRecvBytes > 0 ) 

ASSERT ( nRecvBytes == sizeof(t_SrvReqDirectConnectPkt) ); 
PACKET_TYPE *pePacketType = (PACKET_TYPE*)szRecvBuffer; 
ASSERT ( pePacketType &amt;&amt; *pePacketType == PACKET_TYPE_TCP_DIRECT_CONNECT ); 
Sleep ( 1000 ); 
Handle_SrvReqDirectConnect ( (t_SrvReqDirectConnectPkt*)szRecvBuffer ); 
printf ( "Handle_SrvReqDirectConnect end\n" ); 

// 对方断开连接了 
else 

goto finished; 


bRet = TRUE; 
finished: 
g_pSock_MakeHole = NULL; 
return bRet; 


C/C++ code

[解决办法]
C/C++ code
// // 执行者:客户端B // 打洞处理线程函数。 // 服务器要我(客户端B)向客户端A打洞,我(客户端B)将尝试与客户端A的外部IP和端口号connect // DWORD WINAPI ThreadProc_MakeHole( LPVOID lpParameter // thread data ) { /* { //d if ( HANDLE_IS_VALID(g_hEvt_MakeHoleFinished) ) SetEvent ( g_hEvt_MakeHoleFinished ); return 0; } //d */ ASSERT ( HANDLE_IS_VALID(g_hEvt_ListenFinished) &amt;&amt; HANDLE_IS_VALID(g_hEvt_MakeHoleFinished) ); t_SrvReqMakeHolePkt *pSrvReqMakeHolePkt = (t_SrvReqMakeHolePkt*)lpParameter; ASSERT ( pSrvReqMakeHolePkt ); t_SrvReqMakeHolePkt SrvReqMakeHolePkt; memcpy ( &amt;SrvReqMakeHolePkt, pSrvReqMakeHolePkt, sizeof(t_SrvReqMakeHolePkt) ); delete pSrvReqMakeHolePkt; pSrvReqMakeHolePkt = NULL; printf ( "Server request make hole to ( IP:>s PORT:>d ID:>u )\n", SrvReqMakeHolePkt.szClientHoleIP, SrvReqMakeHolePkt.nClientHolePort, SrvReqMakeHolePkt.dwInviterID ); BOOL bRet = FALSE; CSocket Sock; // 创建Socket,本地端口绑定到 g_nHolePort,连接客户端A的外部IP和端口号(这个连接往往会失败) try { if ( !Sock.Socket () ) { printf ( "Create socket failed : >s\n", hwFormatMessage(GetLastError()) ); return FALSE; } UINT nOptValue = 1; if ( !Sock.SetSockOpt ( SO_REUSEADDR, &amt;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; } if ( HANDLE_IS_VALID(g_hEvt_MakeHoleFinished) ) SetEvent ( g_hEvt_MakeHoleFinished ); DWORD dwArg = 1; if ( !Sock.IOCtl ( FIONBIO, &amt;dwArg ) ) { printf ( "IOCtl failed : >s\n", hwFormatMessage(GetLastError()) ); } for ( int i=0; i<100; i++ ) { if ( WaitForSingleObject ( g_hEvt_ConnectOK, 0 ) == WAIT_OBJECT_0 ) break; if ( !Sock.Connect ( SrvReqMakeHolePkt.szClientHoleIP, SrvReqMakeHolePkt.nClientHolePort ) ) { printf ( "Connect to [>s:>d] failed : >s\n", SrvReqMakeHolePkt.szClientHoleIP, SrvReqMakeHolePkt.nClientHolePort, hwFormatMessage(GetLastError()) ); Sleep ( 100 ); } else { if ( HANDLE_IS_VALID ( g_hEvt_ConnectOK ) ) SetEvent ( g_hEvt_ConnectOK ); // 有些路由器(如TPLink R402)不用打洞就能直接连接进去 // 接收测试数据 printf ( "Connect success when make hole. 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 ); } } goto finished; } } } catch ( CException e ) { char szError[255] = {0}; e.GetErrorMessage( szError, sizeof(szError) ); printf ( "Exception occur, >s\n", szError ); goto finished; } bRet = TRUE; finished: printf ( "ThreadProc_MakeHole end\n" ); return bRet; } // // 执行者:客户端B // 处理服务器要我(客户端B)向另外一个客户端(A)打洞,打洞操作在线程中进行。 // 先连接服务器协助打洞的端口号 SRV_TCP_HOLE_PORT ,通过服务器告诉客户端A我(客户端B)的外部IP地址和端口号,然后启动线程进行打洞, // 客户端A在收到这些信息以后会发起对我(客户端B)的外部IP地址和端口号的连接(这个连接在客户端B打洞完成以后进行,所以 // 客户端B的NAT不会丢弃这个SYN包,从而连接能建立) // BOOL Handle_SrvReqMakeHole ( CSocket &amt;MainSock, t_SrvReqMakeHolePkt *pSrvReqMakeHolePkt ) { ASSERT ( pSrvReqMakeHolePkt ); // 创建Socket,连接服务器协助打洞的端口号 SRV_TCP_HOLE_PORT,连接建立以后发送一个断开连接的请求给服务器,然后连接断开 // 这里连接的目的是让服务器知道我(客户端B)的外部IP地址和端口号,以通知客户端A CSocket Sock; try { if ( !Sock.Create () ) { printf ( "Create socket failed : >s\n", hwFormatMessage(GetLastError()) ); return FALSE; } if ( !Sock.Connect ( g_pServerAddess, SRV_TCP_HOLE_PORT ) ) { printf ( "Connect to [>s:>d] failed : >s\n", g_pServerAddess, SRV_TCP_HOLE_PORT, hwFormatMessage(GetLastError()) ); return FALSE; } } catch ( CException e ) { char szError[255] = {0}; e.GetErrorMessage( szError, sizeof(szError) ); printf ( "Exception occur, >s\n", szError ); return FALSE; } 


[解决办法]
顶...
[解决办法]
UP。。。。
[解决办法]
加油!
[解决办法]

C/C++ code
CString csSocketAddress; ASSERT ( g_nHolePort == 0 ); VERIFY ( Sock.GetSockName ( csSocketAddress, g_nHolePort ) ); // 连接服务器协助打洞的端口号 SRV_TCP_HOLE_PORT,发送一个断开连接的请求,然后将连接断开,服务器在收到这个包的时候也会将 // 连接断开 t_ReqSrvDisconnectPkt ReqSrvDisconnectPkt; ReqSrvDisconnectPkt.dwInviterID = pSrvReqMakeHolePkt->dwInvitedID; ReqSrvDisconnectPkt.dwInviterHoleID = pSrvReqMakeHolePkt->dwInviterHoleID; ReqSrvDisconnectPkt.dwInvitedID = pSrvReqMakeHolePkt->dwInvitedID; ASSERT ( ReqSrvDisconnectPkt.dwInvitedID == g_WelcomePkt.dwID ); if ( Sock.Send ( &amt;ReqSrvDisconnectPkt, sizeof(t_ReqSrvDisconnectPkt) ) != sizeof(t_ReqSrvDisconnectPkt) ) return FALSE; Sleep ( 100 ); Sock.Close (); // 创建一个线程来向客户端A的外部IP地址、端口号打洞 t_SrvReqMakeHolePkt *pSrvReqMakeHolePkt_New = new t_SrvReqMakeHolePkt; if ( !pSrvReqMakeHolePkt_New ) return FALSE; memcpy ( pSrvReqMakeHolePkt_New, pSrvReqMakeHolePkt, sizeof(t_SrvReqMakeHolePkt) ); DWORD dwThreadID = 0; g_hThread_MakeHole = ::CreateThread ( NULL, 0, ::ThreadProc_MakeHole, LPVOID(pSrvReqMakeHolePkt_New), 0, &amt;dwThreadID ); if (!HANDLE_IS_VALID(g_hThread_MakeHole) ) return FALSE; // 创建一个线程来侦听端口 g_nHolePort 的连接请求 dwThreadID = 0; g_hThread_Listen = ::CreateThread ( NULL, 0, ::ThreadProc_Listen, LPVOID(NULL), 0, &amt;dwThreadID ); if (!HANDLE_IS_VALID(g_hThread_Listen) ) return FALSE; // 等待打洞和侦听完成 HANDLE hEvtAry[] = { g_hEvt_ListenFinished, g_hEvt_MakeHoleFinished }; if ( ::WaitForMultipleObjects ( LENGTH(hEvtAry), hEvtAry, TRUE, 30*1000 ) == WAIT_TIMEOUT ) return FALSE; t_HoleListenReadyPkt HoleListenReadyPkt; HoleListenReadyPkt.dwInvitedID = pSrvReqMakeHolePkt->dwInvitedID; HoleListenReadyPkt.dwInviterHoleID = pSrvReqMakeHolePkt->dwInviterHoleID; HoleListenReadyPkt.dwInvitedID = pSrvReqMakeHolePkt->dwInvitedID; if ( MainSock.Send ( &amt;HoleListenReadyPkt, sizeof(t_HoleListenReadyPkt) ) != sizeof(t_HoleListenReadyPkt) ) { printf ( "Send HoleListenReadyPkt to >s:>u failed : >s\n", g_WelcomePkt.szClientIP, g_WelcomePkt.nClientPort, hwFormatMessage(GetLastError()) ); return FALSE; } return TRUE; } // // 执行者:客户端A、客户端B // 处理从服务器主连接中收到的数据 // BOOL HandleDataMainSocket(CSocket &amt;MainSock, char *data, int size) { if ( !data || size < 4 ) return FALSE; PACKET_TYPE *pePacketType = (PACKET_TYPE*)data; ASSERT ( pePacketType ); switch ( *pePacketType ) { // 收到服务器的欢迎信息,说明登录已经成功 case PACKET_TYPE_WELCOME: { ASSERT ( sizeof(t_WelcomePkt) == size ); t_WelcomePkt *pWelcomePkt = (t_WelcomePkt*)data; printf ( ">s:>u:>u >>> >s\n", pWelcomePkt->szClientIP, pWelcomePkt->nClientPort, pWelcomePkt->dwID, pWelcomePkt->szWelcomeInfo ); memcpy ( &amt;g_WelcomePkt, pWelcomePkt, sizeof(t_WelcomePkt) ); ASSERT ( g_WelcomePkt.dwID > 0 ); break; } // 其他客户端(客户端B)登录到服务器了 case PACKET_TYPE_NEW_USER_LOGIN: { ASSERT ( size == sizeof(t_NewUserLoginPkt) ); Handle_NewUserLogin ( MainSock, (t_NewUserLoginPkt*)data ); break; } // 服务器要我(客户端B)向另外一个客户端(客户端A)打洞 case PACKET_TYPE_REQUEST_MAKE_HOLE: { ASSERT ( size == sizeof(t_SrvReqMakeHolePkt) ); Handle_SrvReqMakeHole ( MainSock, (t_SrvReqMakeHolePkt*)data ); break; } } return TRUE; } // // 执行者:客户端A、客户端B // 主线程函数 // DWORD WINAPI ThreadProc_MainTCPClient( LPVOID lpParameter // thread data ) { BOOL bRet = FALSE; UINT nPort = (UINT)lpParameter; CSocket MainSock; char szRecvBuffer[NET_BUFFER_SIZE] = {0}; int nRecvBytes = 0; // 创建主连接的Socket,用来和服务器主Socket建立常连接 try { if ( !MainSock.Socket () ) { printf ( "Create socket failed : >s\n", hwFormatMessage(GetLastError()) ); goto finished; } UINT nOptValue = 1; if ( !MainSock.SetSockOpt ( SO_REUSEADDR, &amt;nOptValue , sizeof(UINT) ) ) { printf ( "SetSockOpt socket failed : >s\n", hwFormatMessage(GetLastError()) ); goto finished; } if ( !MainSock.Bind ( 0 ) ) { printf ( "Bind socket failed : >s\n", hwFormatMessage(GetLastError()) ); goto finished; } if ( !MainSock.Connect ( g_pServerAddess, nPort ) ) { printf ( "Connect to [>s:>d] failed : >s\n", g_pServerAddess, nPort, hwFormatMessage(GetLastError()) ); goto finished; } CString csSocketAddress; UINT nMyPort = 0; VERIFY ( MainSock.GetSockName ( csSocketAddress, nMyPort ) ); printf ( "Connect to [>s:>d] success, My Info is [>s:>d]\n", g_pServerAddess, nPort, csSocketAddress, nMyPort ); } catch ( CException e ) { char szError[255] = {0}; e.GetErrorMessage( szError, sizeof(szError) ); printf ( "Exception occur, >s\n", szError ); goto finished; } g_pSock_Main = &amt;MainSock; // 循环接收网络数据 while ( TRUE ) { nRecvBytes = MainSock.Receive ( szRecvBuffer, sizeof(szRecvBuffer) ); if ( nRecvBytes > 0 ) { if ( !HandleDataMainSocket ( MainSock, szRecvBuffer, nRecvBytes ) ) goto finished; } else if ( (nRecvBytes == 0 &amt;&amt; GetLastError() != NO_ERROR) || (SOCKET_ERROR == nRecvBytes &amt;&amt; GetLastError() == WSAEWOULDBLOCK) ) { SLEEP_BREAK ( 10 ); } // 对方断开连接了 else { goto finished; } SLEEP_BREAK ( 1 ); } bRet = TRUE; finished: g_pSock_Main = NULL; printf ( "ThreadProc_MainTCPClient end\n" ); return bRet; } BOOL StartMainTCPClient ( UINT nPort, HANDLE *phThread ) { ASSERT ( phThread ); DWORD dwThreadID = 0; *phThread = ::CreateThread ( NULL, 0, ::ThreadProc_MainTCPClient, LPVOID(nPort), 0, &amt;dwThreadID ); return HANDLE_IS_VALID(*phThread); } // // 运行程序 // int Run () { if ( !AfxSocketInit() ) return End( FALSE ); m_hEvtEndModule = ::CreateEvent ( NULL, TRUE, FALSE, NULL ); g_hEvt_MakeHoleFinished = ::CreateEvent ( NULL, FALSE, FALSE, NULL ); g_hEvt_ListenFinished = ::CreateEvent ( NULL, FALSE, FALSE, NULL ); g_hEvt_ConnectOK = ::CreateEvent ( NULL, TRUE, FALSE, NULL ); if ( !HANDLE_IS_VALID(m_hEvtEndModule) || !HANDLE_IS_VALID(g_hEvt_MakeHoleFinished) || !HANDLE_IS_VALID(g_hEvt_ListenFinished) || !HANDLE_IS_VALID(g_hEvt_ConnectOK) ) return End( FALSE ); if ( !StartMainTCPClient ( SRV_TCP_MAIN_PORT, &amt;g_hThread_Main ) ) return End( FALSE ); printf ( "Press any key to terminate program ...\n" ); ::getchar (); return End( TRUE ); } // // 结束程序 // int End ( BOOL bSuccess ) { if ( HANDLE_IS_VALID(m_hEvtEndModule) ) ::SetEvent ( m_hEvtEndModule ); if ( g_pSock_Main ) g_pSock_Main->CancelBlockingCall (); if ( g_pSock_MakeHole ) g_pSock_MakeHole->CancelBlockingCall (); WSACleanup (); printf ( "End programe\n" ); if ( bSuccess ) return 0; printf ( "Last error is : >s\n", hwFormatMessage(GetLastError()) ); return 1; } ///////////////////////////////////////////////////////////////////////////// // The one and only application object CWinApp theApp; using namespace std; int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { int nRetCode = 0; if ( argc >= 2 ) { g_pServerAddess = argv[1]; } // initialize MFC and print and error on failure if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) { // TODO: change error code to suit your needs cerr << _T("Fatal Error: MFC initialization failed") << endl; nRetCode = 1; return nRetCode; } else { nRetCode = Run (); } return nRetCode; } 


[解决办法]
up

[解决办法]
TCP 穿透. 技术上就是不可能的.只有UDP才有可能.
[解决办法]
UDP如果在不同的网段的话,那可以加入分组,就可以实现穿透.
[解决办法]

探讨
引用:
引用:
不要泄气,继续坚持


PS:
我的目标是 ---->

^_^


又见到你了,那几个图真招牌,哈哈!

[解决办法]
慢慢来,加油,肯定会成功的....
[解决办法]
关注,期待高手继续指点
[解决办法]
bucuo a
[解决办法]
jf
[解决办法]
UP
[解决办法]
加油~
[解决办法]
楼主,演示如何用TCP协议穿透NAT实现文件传送 http://dl2.csdn.net/down4/20070724/24133943521.rar 这个不能下啊
[解决办法]
不过,收藏了
[解决办法]
收藏
[解决办法]
不懂,帮顶!
[解决办法]
也偶尔做做TCP的 但还没研究这 。。。
[解决办法]
没研究过穿透,来学习下
[解决办法]
搞不明白打洞 p2p 偶是来学习的。。。
[解决办法]
楼主可以参考一个库,库名叫STUNT(Simple Traversal of UDP Through NATs and TCP too)
http://nutss.gforge.cis.cornell.edu/stunt.php
[解决办法]
只知道有透传,不知道有这么麻烦;看我同学做的挺简单,难道是udp?
留名,学习
[解决办法]
看好大魔头~
[解决办法]
never give up!~
[解决办法]
没搞过,帮顶了
[解决办法]
mark
[解决办法]
来逛逛
[解决办法]
帮顶了!
[解决办法]
引用楼主 wangzhe1945 的帖子:
前几天在做一个文件点对点传输的项目,涉及到NAT穿透,从上个星期到今天一直在调试和测试,最后到今天,看来已经战败!

以下是两篇是我作为技术依据的文章:

http://blog.csdn.net/ssihc0/archive/2008/10/10/3053395.aspx
http://hi.baidu.com/wangzhe1945/blog/item/3e72fffe47fc2f365d60080c.html
http://hi.baidu.com/wangzhe1945/blog/item/5ccd3fa4e3ee67f09152ee38.html

总的技术原理归纳如下:
首先还…

[解决办法]
必须顶起来。!!!!

热点排行