关于p2p中,穿透nat的不稳定性问题
原来是贴在dfw那的一个帖子。分不够可以加。谢谢
网络上有很多关于穿透NAT的文章,很多是关于UDP打洞的。
我下载了一位前辈的实现这个原理代码,可是很奇怪的是:
相同的网络环境,有时候可以通,有时候又不通。客户端代码片断如下:
发送消息:
function SendMessageTo(pSendName: pchar; pInfo: Pointer; size: integer): BOOL;
begin
for i := 0 to MAXRETRY do
begin
RecvedACK := false;
remoteAddr.sin_addr.S_addr := htonl(UserIP);
remoteAddr.sin_family := AF_INET;
remoteAddr.sin_port := htons(UserPort);
MessageHead.ord := oP2PMESSAGE;
MessageHead.iStringLen := size;
strcopy(MessageHead.Sender, userName);
err := sendto(sock, MessageHead, SizeOf(MessageHead), 0, remoteAddr, SizeOf(remoteAddr));
sendto(sock, pInfo^, MessageHead.iStringLen, 0, remoteAddr, SizeOf(remoteAddr));
// 等待接收线程将此标记修改
for j := 0 to 10 do
begin
if (RecvedACK) then
begin
result := True;
exit;
end else
Sleep(300);
end; //for
// 没有接收到目标主机的回应,认为目标主机的端口映射没有
// 打开,那么发送请求信息给服务器,要服务器告诉目标主机
// 打开映射端口(UDP打洞)
serverAddr.sin_addr.S_addr := Inet_addr(svrIP);
serverAddr.sin_family := AF_INET;
serverAddr.sin_port := htons(SERVER_PORT);
transMessage.ord := oP2PTRANS;
strcopy(transMessage.msg.translatemessage.userName, pSendName);
sendto(sock, transMessage, SizeOf(transMessage), 0, serverAddr, SizeOf(serverAddr));
Sleep(500); // 等待对方先发送信息。
end; //for
end;
接收线程代码
while True do
begin
err := recvfrom(sock, recvbuf, SizeOf(recvbuf), 0, remoteAddr, addrLen);
if err <= 0 then continue;
pData := nil;
recvtype := -100;
case recvbuf.ord of
oP2PMESSAGE:
begin
getmem(pInfo, recvbuf.iStringLen);
err := recvfrom(sock, pInfo^, recvbuf.iStringLen, 0, remoteAddr, addrLen);
if err <= 0 then continue;
pData := pInfo;
recvtype := 1;
recvsize := recvbuf.iStringLen;
sendbuf.ord := oP2PMESSAGEACK;
sendto(sock, sendbuf, SizeOf(sendbuf), 0, remoteAddr, addrLen);
//freemem(pInfo);
//break;
end; //oP2PMESSAGE接收到P2P的消息
oP2PSOMEONEWANTTOCALLYOU://收到服务器要求向某个客户端打洞的消息
begin
remoteAddr.sin_addr.S_addr := htonl(recvbuf.iStringLen);
remoteAddr.sin_family := AF_INET;
remoteAddr.sin_port := htons(recvbuf.port);
sendbuf.ord := oP2PTRASH;
sendto(sock, sendbuf, SizeOf(sendbuf), 0, remoteAddr, SizeOf(remoteAddr));
//break;
end; //接收到打洞命令,向指定的IP地址打洞
oP2PMESSAGEACK:
begin
RecvedACK := True;
end; // 发送消息的应答
...........
..............
还有一个问题:有资料上说,为了保持NAT给应用程序分配的UDP端口不被回收,应该让应用程序定时地向NAT给自己分配的地址和端口发包。
例如:CA的LAN地址是192.168.0.3:1234 经过NAT后变成218.66.101.35:8666,那么CA应该定时向218.66.101.35:8666发送UDP包来保持这个
8666的端口。但是我按照这样做只好,发现CA并不能收到这个包
while true do
begin
err := sendto(sock, msg, SizeOf(msg), 0, remoteAddr, addr);//跟踪发现err>0
Sleep(3000);
end;
有没有什么工具可以知道NAT是否抛弃了那些UDP包
请大侠指教,这会是什么问题。我已经倾我所有的分数了
来自:Roseking, 时间:2006-2-28 9:15:32, ID:3366395
不太明白你的思路, 不过有一个您似乎弄错了
sendto 成功时返回发出的字节数, 失败返回-1
来自:jamjar, 时间:2006-2-28 9:18:05, ID:3366400
楼上的,我的意思就是我的sendto成功了,但是收不到这个消息
来自:Roseking, 时间:2006-2-28 9:19:44, ID:3366404
UDP本来就是不可靠的, sendto成功了,对方也可能没有收到这个数据包
来自:jamjar, 时间:2006-2-28 9:21:46, ID:3366405
我是一个循环来发,难道都收不到吗。
来自:jamjar, 时间:2006-2-28 9:30:54, ID:3366412
基本思路是这样的
1:客户端(A)直接向对方(B)的目的地址和端口发送包。
2:如果收到B的回答(RecvdASK=true),就算成功了。
3:如果没有收到B的回答(recvedASK=false),那么就给服务器发个请求包。
这个包里有B的信息。服务器给B发包,要求B给A发一个包(也就是打洞)。
按照原理,B的NAT上将打开一个通道,让A发送的包进来。
4:此时A发的包,B就能收到了
来自:41426277, 时间:2006-2-28 9:36:55, ID:3366422
jamjar, 真是太谢谢你的提示.
来自:jamjar, 时间:2006-2-28 9:46:22, ID:3366437
这个是服务端出来打洞的代码
oP2PTRANS:
begin
for i := 0 to clientList.Count - 1 do
begin
pUserListNode := clientList.Items[i];
if strcomp(pUserListNode^.UserName, Buf.msg.translatemessage.UserName) = 0 then break;
end; //for
remoteAddr.sin_family := AF_INET;
remoteAddr.sin_port := htons(pUserListNode^.port);
remoteAddr.sin_addr.s_addr := htonl(pUserListNode^.ip);
transMessage.ord := oP2PSOMEONEWANTTOCALLYOU;
transMessage.iStringLen := ntohl(SenderAddr.sin_addr.s_addr);
transMessage.port := ntohs(SenderAddr.sin_port);
sendto(sock, transMessage, sizeof(transMessage), 0, remoteAddr, addrLen);
//break;
end; //oP2PTRANS // 某个客户希望服务端向另外一个客户发送一个打洞消息
来自:jamjar, 时间:2006-2-28 10:06:51, ID:3366465
运行后,出现的情况是
如果A向B发送消息,
那么B可以收到服务器发来的要求B向A打洞的消息,但是无法收到A的消息
来自:netfun2000, 时间:2006-2-28 12:22:24, ID:3366692
帮你顶,关注中....^_^
来自:bbscom, 时间:2006-2-28 12:35:22, ID:3366711
打洞裡有漏洞
来自:jamjar, 时间:2006-2-28 15:31:46, ID:3366996
楼上说的是啥意思,麻烦说明白,心里急啊
问题点数:100、回复次数:7Top
1 楼nuaawenlin(飘人)回复于 2006-03-01 10:05:55 得分 30
从你的代码没有看出,B和A之间没有相互发送Session的消息
当接收到服务器的打洞消息后,A和B都要向各自的网关发送一个Session,告诉它允许对方的UDP数据进入内网Top
2 楼lybid2002(随风)回复于 2006-03-01 10:26:45 得分 0
to nuaawenlin(飘人):
代码只贴出了片断,A和B都要向各自的网关发送一个Session,告诉它允许对方的UDP数据进入内网,这个是有的。否则也不会有的时候发送成功了。
如果需要的话,把代码都贴出来。Top
3 楼myth_2002(myth)回复于 2006-03-01 10:38:53 得分 40
俺就是那个jamjar,还是我来说吧
假如A要向B发消息
remoteAddr.sin_addr.S_addr := htonl(UserIP);//这里就是B的外网地址
remoteAddr.sin_family := AF_INET;
remoteAddr.sin_port := htons(UserPort);//这里就是B的外网端口
MessageHead.ord := oP2PMESSAGE;
MessageHead.iStringLen := size;
strcopy(MessageHead.Sender, userName);
err := sendto(sock, MessageHead, SizeOf(MessageHead), 0, remoteAddr, SizeOf(remoteAddr));//向B的外网地址发消息。
sendto(sock, pInfo^, MessageHead.iStringLen, 0, remoteAddr, SizeOf(remoteAddr));
如果不成功,那么就请求服务器给B发消息
serverAddr.sin_addr.S_addr := Inet_addr(svrIP);
serverAddr.sin_family := AF_INET;
serverAddr.sin_port := htons(SERVER_PORT);
transMessage.ord := oP2PTRANS;
strcopy(transMessage.msg.translatemessage.userName, pSendName);
sendto(sock, transMessage, SizeOf(transMessage), 0, serverAddr, SizeOf(serverAddr));
Sleep(500); // 等待对方先发送信息。
B收到服务器发来的消息后:
oP2PSOMEONEWANTTOCALLYOU://收到服务器要求向某个客户端打洞的消息
begin
remoteAddr.sin_addr.S_addr := htonl(recvbuf.iStringLen);//这里是A的外网地址
remoteAddr.sin_family := AF_INET;
remoteAddr.sin_port := htons(recvbuf.port);//A的外网端口
sendbuf.ord := oP2PTRASH;
sendto(sock, sendbuf, SizeOf(sendbuf), 0, remoteAddr, SizeOf(remoteAddr));//向A的外网地址发消息
//break;
end; //接收到打洞命令,向指定的IP地址打洞
Top
4 楼alen_ghl(东方求*)回复于 2006-03-01 10:41:10 得分 10
是不是session的时间有效性导致的?Top
5 楼myth_2002(myth)回复于 2006-03-01 11:22:08 得分 10
请alen_ghl详细说一下,如何保持sessionTop
6 楼returnnull(南斗)回复于 2006-03-01 18:10:16 得分 10
session 大概只有数十秒的时效
所以要做心跳包保活Top
7 楼echomo123(无语)回复于 2006-03-15 14:18:42 得分 0
关注Top




