打洞理论是这么说的:如果client A 想要向client B发送一个消息,那么不能直接发送,否则会被NAT拦截。必须通知服务器给client B 发送一个通知,通知client B 向client A的方向打洞,那么以后client A就可以直接跟client B联系了。网上找到的一个解释:
  1.   Client   A向Server发一个UDP包,Server收到后,将发送方的IP地址和UDP端口(NAT   A向Server发送UDP包的端口,而不是Client   A的4000端口)通知Client   B。之后Client   B就需要开始向这个IP地址和UDP端口发送UDP包了。当然,在下面第2步还未完成之前,Client   B向这个IP地址和UDP端口发送的所有UDP包都会被NAT   A丢弃。   
    
  2.   Client   A向NAT   B的地址和任意端口发一个包,   发送这个包没有其他目的,只是为了让NAT   A认为从NAT   B发送来的UDP包是安全的而已。我有一个疑问:就是为什么client A直接向client B发送消息会被NAT拦截,但是clinet B向cilent A发送的打洞消息就不会被client A 的NAT拦截呢???下面是我的想法:
难道是这个client B 给client A 的打洞消息也会被client A 的NAT拦截,但是它的目的已经达到了,就是即使被拦截了,也已经打出了一个洞,从此以后,client A 给client B的消息可以直接发送了。
但是我自己仔细想想不太可能,因为client B给client A 发送的打洞消息被拦截,那么就没有到达client A的这台内网主机,也就不可能在client A的NAT中有这个端口映射,没有端口映射,通过NAT后的clinet A直接发送给clinet B的消息就会加上一个client B 所不知道的外网IP+端口号,对于这个client B的NAT 肯定会拦截。难道NAT只认IP,不认端口;如果clinet B给client A 的外网IP发过打洞消息(即使这个消息被拦截),那么以后通过这个client A的外网发给 client A的消息都不会被拦截;但是,我在一个帖子上面看到,NAT是认IP+端口的,那个人换了一个socket ,因为不同socket绑定的不同的服务器端口,所以他用 socket A接收 client 的请求,用socket B去回复client 的时候,client 不能收到消息,也就是说socket B发出的消息不能达到位于NAT后面的client。而如果他用socket A 接收,用socket A回复的话,可以穿过NAT到达client 主机。
这个说明,NAT是认IP,又认端口的。另外,我自己按照1,2所说的写了自己的代码,但是我是server.client A ,client B,三者同时运行在自己的一台机器上面,我的机器是个内网主机,按照上面的说明,我的client A与clinetB 没法通讯。。知道内网主机不能做server,因为client不能连接到server,但是我的是可以连接上的(貌似是通过模拟的吧,我的server IP地址就是用的内网IP 192.168.0.X),还有就是知道 有些NAT不支持 “内网环回”(貌似这个说法),就是2个clinet A与clinet B不能位于同一个NAT下面,我不知道我的client A 与client B不能通讯的原因是什么:
1.是由于我的打洞做法错误
2.由于不支持 “内网环回”
如果是原因2,那么说明我的打洞做法是正确的,但是为什么这么打洞是正确 的,我已经说了我的理解是这样打洞是行不通的,那么请告诉我为什么这样打洞是正确的
自己想了很久,还是不知道怎么回事,请有经验的高手跟前辈们指教。本人一向很大方分数。散发100,如果答案满意,可以再加。

解决方案 »

  1.   

    LZ应该先了解一下NAT的原理,再尝试“打洞”。NAT大概分4种类型,最常见的工作原理大致是:当内网主机使用一个端口向外发送数据时,NAT在公网分配一个端口,与该主机端口向关联,此后该主机端口发出的数据都用该公网端口向外发送,该公网端口收到的数据都转发给该主机端口。
    “打洞”要借助公网Server有两点原因:一是要让NAT给主机分配端口建立关联;二是要获得主机的公网IP和端口号。这样才能与该主机进行通讯。
    如果NAT不支持“内网环回”,同一内网的两台主机就不能通过“打洞”方式通讯。
      

  2.   

    呵呵,上面说的我基本知道,我现在问的是怎么打洞。。
    我的针对的NAT就是所谓的Cone NAT
      

  3.   

    上面1、2的说法适用于对外网主机只认IP不认端口的NAT。对于既认IP又认端口的NAT,需要Server把A的IP和端口告诉B,再把B的IP和端口告诉A,然后双方“打洞”。
      

  4.   


    NAT中,规定收到的包的目的地址不在NAT地址列表中的话,就会被抛弃!现在打洞都要借助于一个中间服务器,把相互参数告知对方。
      

  5.   

    再问一个问题:
    1.怎样将内网主机作为一个公网主机一样做server?
    2.如果是在同一个局域网的话,可以直接用内网IP地址通讯,这样就可以绕过“内网环回”问题。现在我的问题是,如果client A与client B在同一个局域网内部,怎样获得他们的内网IP,因为我的server获得的是他们外网IP,只是他们的端口不同而已。
      

  6.   


    内网机器无法做公网的server吧,除非进行NAT影射。
      

  7.   

    1、设置NAT端口映射。
    2、Server根据外网IP可以判断两台Client是否在同一内网,Client可以自己取出内网IP和端口发包告诉Server。
      

  8.   

    里面有一个时序的问题
    不一定是哪一个包被nat拦截
      

  9.   

    网上找到的一个解释: 
      1.  Client  A向Server发一个UDP包,Server收到后,将发送方的IP地址和UDP端口(NAT  A向 Server发送UDP包的端口,而不是Client  A的4000端口)通知Client  B。之后Client  B就需要开始 向这个IP地址和UDP端口发送UDP包了。当然,在下面第2步还未完成之前,Client  B向这个IP地址和 UDP端口发送的所有UDP包都会被NAT  A丢弃。  
        
      2.  Client  A向NAT  B的地址和任意端口发一个包,  发送这个包没有其他目的,只是为了让NAT   A认为从NAT  B发送来的UDP包是安全的而已。 
    ------------------------------------------------------------------------------------
    我的成功做法如下:
    通信的参与者有三方,分别是P2PServer, PeerServer, PeerClient; 我的业务流程是这样的,PeerServer作为视频源而存在,PeerClient向其请求视频数据然后播放, 
    PeerServer启动或登录时向P2PServer发送数据包,P2PServer收到的addr则是PeerServer经nat后的ip和端口, 事实上这个数据包如果发送成功,则PeerSever的udp洞已经打开了,P2PServer和PeerServer的会话建立成功,只要每隔一段时间(保险一点可以取30秒)向P2PServer服务器发送心跳包,那么这个会话就不会结束!数据包含有改PeerSever的唯一标识ID, P2PServer可以维护一个PeerServer的信息列表(一下简称会话列表),可以用结构体数组来实现。
    当然,PeerServer也不能保证24小时在线,可能因为关机,重启,关闭软件或者包发送失败等, P2PServer并不总是能收到心跳包,所以P2PServer也要定时检验会话列表,如果发现某个会话的上次心跳时间距离现在已经超过两分钟,则视为其PeerServer掉线,将该会话信息删除这时候PeerClient只要知道PeerServer经NAT后的IP和Port就能直接和PeerServer进行通信, 怎么知道? 当然是向P2Pserver查询了!
      

  10.   

    穿透NAT有几种情形:(假设有两台计算机:x,y)1 x,y只有其中一个是私有IP(i.e. 有一台机器位于内网)。
    2 x,y两都是私有IP(i.e. 两台机器都位于内网)。
      

  11.   

    得到NAT类型就好办了,因为这个通信端口大部分是经常会变动的,但变动基本都是有规律的,NAT类型会告诉你这个规律
      

  12.   

    顺便问一句,如何查看路由器的NAT表?
      

  13.   

    3的情况估计连服务器都会比较麻烦了,一个UDP Socket只能同时发送和接收。如果服务端要提高效率,采用一个SOCKET专事接收,多个SOCKET一起发送的话,发送的端口也是不一样的,在客户端NAT看来,发送的SOCKET也是“IP正确,端口错误”的。
    不知道认远程发送端口的NAT占得比例多大。
      

  14.   

    “当内网主机使用一个端口向外发送数据时,NAT在公网分配一个端口,与该主机端口向关联,此后该主机端口发出的数据都用该公网端口向外发送,该公网端口收到的数据都转发给该主机端口。”
    内网主机使用的端口是不是要端口映射啊???