我想做个聊天程序~如果要实现不同NAT网络中的机器互相通讯,一定要通过服务器转发吗?
我觉得那样对服务器的开销太大,是否可以让他们直接通讯。如果可以,能给我说下大概原理吗?

解决方案 »

  1.   

    |
                              |
       +----------------------+----------------------+
       |                                             |
     NAT A (外网IP:202.187.45.3)                 NAT B (外网IP:187.34.1.56)
       |   (内网IP:192.168.0.1)                      | (内网IP:192.168.0.1)
       |                                             |
    Client A  (192.168.0.20:4000)             Client B (192.168.0.10:40000)    首先,Client A登录服务器,NAT A为这次的Session分配了一个端口60000,那么Server S收到的Client A的地址是202.187.45.3:60000,这就是Client A的外网地址了。同样,Client B登录Server S,NAT B给此次Session分配的端口是40000,那么Server S收到的B的地址是187.34.1.56:40000。
        此时,Client A与Client B都可以与Server S通信了。如果Client A此时想直接发送信息给Client B,那么他可以从Server S那儿获得B的公网地址187.34.1.56:40000,是不是Client A向这个地址发送信息Client B就能收到了呢?答案是不行,因为如果这样发送信息,NAT B会将这个信息丢弃(因为这样的信息是不请自来的,为了安全,大多数NAT都会执行丢弃动作)。现在我们需要的是在NAT B上打一个方向为202.187.45.3(即Client A的外网地址)的洞,那么Client A发送到187.34.1.56:40000的信息,Client B就能收到了。这个打洞命令由谁来发呢,呵呵,当然是Server S。
        总结一下这个过程:如果Client A想向Client B发送信息,那么Client A发送命令给Server S,请求Server S命令Client B向Client A方向打洞。呵呵,是不是很绕口,不过没关系,想一想就很清楚了,何况还有源代码呢(侯老师说过:在源代码面前没有秘密 8)),然后Client A就可以通过Client B的外网地址与Client B通信了。
        
        注意:以上过程只适合于Cone NAT的情况,如果是Symmetric NAT,那么当Client B向Client A打洞的端口已经重新分配了,Client B将无法知道这个端口(如果Symmetric NAT的端口是顺序分配的,那么我们或许可以猜测这个端口号,可是由于可能导致失败的因素太多,我们不推荐这种猜测端口的方法)。
        
        下面是一个模拟P2P聊天的过程的源代码,过程很简单,P2PServer运行在一个拥有公网IP的计算机上,P2PClient运行在两个不同的NAT后(注意,如果两个客户端运行在一个NAT后,本程序很可能不能运行正常,这取决于你的NAT是否支持loopback translation,详见http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt,当然,此问题可以通过双方先尝试连接对方的内网IP来解决,但是这个代码只是为了验证原理,并没有处理这些问题),后登录的计算机可以获得先登录计算机的用户名,后登录的计算机通过send username message的格式来发送消息。如果发送成功,说明你已取得了直接与对方连接的成功。
        程序现在支持三个命令:send , getu , exit
        
        send格式:send username message
        功能:发送信息给username
        
        getu格式:getu
        功能:获得当前服务器用户列表
        
        exit格式:exit
        功能:注销与服务器的连接(服务器不会自动监测客户是否吊线)
            
        代码很短,相信很容易懂,如果有什么问题,可以给我发邮件[email protected]  或者在CSDN上发送短消息。同时,欢迎转发此文,但希望保留作者版权8-)。
        
        最后感谢CSDN网友 PiggyXP 和 Seilfer的测试帮助
      

  2.   

    谢谢chendazhi(不务正业)我大概知道原理了~ 不过您发的文件是英文的,偶英文还不是很好,能否给我中文的资料呢~
    呵呵,先结帖