最近在玩udp p2p通讯, 遇到一个问题, 希望有经验的朋友指点一下, 感谢

hyblusea 2012-09-29 11:03:57
计算机A, B 分别位于NAT之后(俗称为内网), 计算机C在公网, 如下图:
A<----------->NatA<--------------->C<-------------------->NatB<------------->B

A,B向C发送消息后, C能够获取到A,B的映射IP和映射端口等信息, 因此C通过A和B映射IP、映射端口可以向A, B发送消息。
如果A需要将消息发送给B, 目前已经实现由C进行中转, 但这不符合P2P的思想,也增加了C的负载。于是希望通过C能够打通A与B的点对点连接.

参考网上一篇文章 http://andylin02.iteye.com/blog/444666, 里面有这样一句话:
"那么A要向B发送消息怎么办呢?需要C向B发送一个消息告诉B方A的地址让B向这个地址发送一个消息,对A进行一个穿透。这样A就可以向B发送消息了。"

经测试以后发送B直接使用A的映射地址和端口对其发送消息, A无法接收, 于是再搜索相关资料得知, A的映射地址和端口仅对于计算机C是有效的....

所以请教一下高手, 正确的做法是怎样的呢? 希望能分享一些经验 , 不甚感谢 .
...全文
280 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
ms_fans_007 2012-09-29
  • 打赏
  • 举报
回复
NAT并不是所有的路由都可以穿透的,和路由器的安全级别有关系的,

家用的打洞,比较简单。我发一份DEMO win32写的DEMO,应该可以实现,但是公司在局域网,没有外网测试。

原理是这样的。

服务器,等待接收 AB的连接,保存他们的地址信息。

AB各自发送一个UDP 0字节数据过去,让服务器获得AB的连接信息。

通过C把AB的连接信息 发送给BA。

AB得到了对应BA的信息后,进行数据发送。

代码如下,虽然是WIN32代码,但能看明白吧:

S

int _tmain(int argc, _TCHAR* argv[])
{

WSADATA wsaData;
WORD wVersionRequested = MAKEWORD(2, 2);
int nResult = WSAStartup(wVersionRequested, &wsaData);

SOCKET sk;
SOCKET skA;
SOCKET skB;

struct sockaddr_in local;
struct sockaddr_in fromA; //A的连接信息
struct sockaddr_in fromB; //B的连接信息

int fromlen =sizeof(fromA);
local.sin_family=AF_INET;
local.sin_port=htons(1888);

local.sin_addr.s_addr=INADDR_ANY;
sk=socket(AF_INET,SOCK_DGRAM,0);
bind(sk,(struct sockaddr*)&local,sizeof(local));

char b[1024] = { 0 };
//接收AB的连接信息
if (recvfrom(sk,b,sizeof(b),0,(struct sockaddr*)&fromA,&fromlen)!=SOCKET_ERROR)
if (recvfrom(sk,b,sizeof(b),0,(struct sockaddr*)&fromB,&fromlen)!=SOCKET_ERROR)
//将AB的连接信息 发送给BA
sendto(sk,(char*)&fromA, fromlen,0,(struct sockaddr*)&fromB,fromlen);
sendto(sk,(char*)&fromB, fromlen,0,(struct sockaddr*)&fromA,fromlen);
system("pause");
return 0;
}


C:

int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD(2, 2);
int nResult = WSAStartup(wVersionRequested, &wsaData);

struct sockaddr_in server;
struct sockaddr_in fromB;
int len =sizeof(server);
server.sin_family=AF_INET;
server.sin_port=htons(1888);
server.sin_addr.s_addr=inet_addr("127.0.0.1");
char b[1024] = { 0 };
SOCKET sk =socket(AF_INET,SOCK_DGRAM,0);
//发送信息给服务器
sendto(sk,b,0,0,(struct sockaddr*)&server,len);
//接收B的连接信息
recvfrom(sk,(char*)&fromB,sizeof(fromB),0,(struct sockaddr*)&server,&len);
char s[1024] ={0};
sprintf(s,"12345");
char r[1024] ={0};
//向B发送信息
sendto(sk,s,1024,0,(struct sockaddr*)&fromB,len);
//接收B的回包
recvfrom(sk,r,sizeof(r),0,(struct sockaddr*)&fromB,&len);
printf("%s", r);
system("pause");

return 0;
}
hyblusea 2012-09-29
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 的回复:]

A同时向B的公有IP:端口和私有IP:端口发送数据包,同时B同时向A的公有IP:端口和私有IP:端口发送数据包。
[/Quote]

你所说的前面部分思路与我目前的做法是一样的, 惟一不能理解的是为什么A要对B的私有地址:端口发送消息呢? 如果确定B属予另一个子网, 那么B的私有地址, 很可能与A所在的子网中的某私有地址重复, 所以向B的私有地址发送消息在这种场景下是不太合适的....不知道我是不是理解有误 , 或者可以再说详细一点吗?

另外, A->C, NAT A所记录的A的映射地址:端口, 仅对C有效吗?
hyblusea 2012-09-29
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 的回复:]
经测试以后发送B直接使用A的映射地址和端口对其发送消息, A无法接收, 于是再搜索相关资料得知, A的映射地址和端口仅对于计算机C是有效的....

对这句有点怀疑。。。

如果内外有外网的访问权限,A的映射地址和端口在B上也应该是有效的,只不过要绕一圈才能访问到。

B给A发消息,A收不到的原因得琢磨下,是否映射的对外端口是单向发送的。。。

瞎猜一下。。。
[/Quote]

A将消息发送到C, 那么A->C 的映射地址和端口信息保存在路由器(NAT A)里面, 因此只有C可以使用该映射地址和端口A才可以收到消息, 如果是其他地址使用该映射地址和端口路由器会丢弃该包...所以现在比较困拢
安乐风流 2012-09-29
  • 打赏
  • 举报
回复
由你的描述可知:你是想借助C与A、B现有活动会话使A、B能够直接通信,这就是P2P打洞:分为UDP打洞和TCP打洞,下面讲讲UDP打滑洞:
首先A与C有活动的UDP会话或连接,C能够看得到A的(公有IP:端口,私有IP:端口)对;同理,B也一样。
1.A发送打洞请求C连接到B,此时C会发送B的(公有IP:端口,私有IP:端口)对响应A的同时发送A的(公有IP:端口,私有IP:端口)对给B。
2.A同时向B的公有IP:端口和私有IP:端口发送数据包,同时B同时向A的公有IP:端口和私有IP:端口发送数据包。
3.当向A发向B的私有IP:端口的数据包穿过自己的NatA时NatA会作记录,这时B发向A数据包到达NatA后NatA就知道该分发给谁了,同理在B的NatB处也一样,这样就打通了A与B的直接联接,就像分别在NatA和NatB上打了一个洞。
具体过程和例子代码请参照《Windows网络与通信程序设计》第二版(作者:王艳平)的第10章。
Johnyin 2012-09-29
  • 打赏
  • 举报
回复

经测试以后发送B直接使用A的映射地址和端口对其发送消息, A无法接收, 于是再搜索相关资料得知, A的映射地址和端口仅对于计算机C是有效的....

对这句有点怀疑。。。

如果内外有外网的访问权限,A的映射地址和端口在B上也应该是有效的,只不过要绕一圈才能访问到。

B给A发消息,A收不到的原因得琢磨下,是否映射的对外端口是单向发送的。。。

瞎猜一下。。。

110,546

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧