以下将要用到一个叫做NAT的重要名词,先做点解释。

NAT是Net Address Translation(网络地址转换)的简称,就是说,局域网通常靠一个具有公网IP的代理网关服务器连到Internet共享上网。局域网内的机器并不具备公网IP地址,它只有内网地址,假设它要和Internet上的HTTP服务器通信,代理网关便会新建一个端口来和这个网内机器关联,并通过这个端口来和HTTP服务器交换数据。最终,网内机器->代理网关->HTTP服务器,在一个会话期间,各自的端口保持了映射关系,特别是代理网关和网内机器的端口映射,使得代理网关不会把接收到的数据向网内转发时,发错了机器。

局域网内的机器在网关处,就是靠NAT来映射端口并实现Internet连接,因此,NAT也直接被称为“端口映射”。端口映射之后,在一个会话期间保持,对于TCP连接是直到连接断开才销毁,而对于UDP,却存在一个不定的生存期,例如2秒。

如果两台机器A和B,分别处于两个局域网内,它们要通过Internet通信,这就是P2P(点到点)连接通信。

目前的Internet使用IPv4协议,采用32位IP地址,主要被用来进行C/S形式的通信,需要共享的资源集中放于Internet服务器上。IPv4对于P2P分布式资源共享的支持,极不友好。首先,32位IP地址已经不敷使用,公网IP地址日趋紧张,只能使用局域网共享公网IP的方式,局域网正是为了临时应对IP耗尽而出现的,长远的解决办法是研究IPv6。其次,分别处于两个局域网内的机器要通信,由于对方没有公网IP,直接呼叫对方是不可能的,必须借助第三方“中介”(机器或者软件)间接地连通,解决办法下列几种:

第一:实现局域网内的数据链路层协议,就是写一个类似于TCP/IP的协议,由它来代替Windows系统里的TCP/IP协议,由它直接基于网卡硬件获取数据。这是十分复杂的。

第二:用Internet上的公网服务器中转数据,但对于大数据量的中转,显然受到服务器和网络的负载极限的限制。

第三:依靠Internet上的公网服务器做“媒人”,将这两台分别处于不同局域网的机器相互介绍给对方,在它们建立连接之后,服务器即脱离关系。这种方式下,服务器把A的NAT端口映射关系告诉B,又把B的NAT端口映射关系告诉A,这样AB相互知道对方的端口映射关系之后,就能建立连接。因为A和B各自的端口映射关系是靠各自的代理网关动态建立的,动态建立的映射端口不得不告知对方。

第四:上面的第三种办法,也可以采用静态端口映射方式,这样就不需要中介服务器对A和B做介绍。在各方的代理网关上,可以在代理工具里将某个端口(如1350)和局域网内的某台机器(如内网IP为200.200.200.100,端口1360)做好静态映射,这样,代理网关会自动地将出入于1350端口的数据发往200.200.200.100的1360端口。当然,通信之前,必须对对方的端口映射关系做配置。有多少台网内机器要通信,就得映射多少个不同的端口,同时在另一个局域网内的机器就要做多少个配置。在局域网内搭建HTTP、FTP等服务器就是通过静态映射端口来实现的,这个端口一般不是HTTP、FTP的默认80和23,所以对这类站点的访问往往会在URL里加上端口号。

由此可见,上述前两种办法在简单应用中是不可取的,只有后两种可行。它们又各有缺点,第三种动态映射端口,需要增加中间服务器,第四种静态映射端口,在需要通信的各方机器很多的情况下,做手工端口映射和配置都是很繁琐的,并且一方添加一台机器,就需要在其余对方增加配置。

采用动态和静态相结合的办法是可以推想的,然而其可行性还必须经过测试。可以这样设计,为了让所有通信机器彼此知晓并定位。我们可以在局域网里,只对一台机器在代理网关处做静态端口映射,本局域网内的机器都向它登记。而两个局域网各自只做一项对对方的映射配置。两个局域网之间,没有静态映射端口的机器要通信,就靠有映射的机器来担当“介绍”。

就局域网和NAT的问题实际上还很多,比如各自的局域网的结构不同,局域网里可能又有子局域网,局域网可能是NAT代理结构,但也可能是HTTP代理,Sock4、Sock5代理等结构,NAT又分严格的和非严格NAT,严格NAT限制很多,更不便于P2P。不过,软件不能实现的地方,可以考虑改变硬件结构,例如将严格NAT变为非严格NAT。如果硬件改变不得,那么Internet整体上就有10%的系统不能实现P2P,除非等到正处于研发的IPv6协议出来。

P2P要解决的唯一技术难题是如何发现、定位和寻址对方,就是如何穿透NAT、HTTP、Sock等代理和如何穿透防火墙找到对方并建立起通信的问题。由于绝大多数局域网是NAT代理结构,所以前面对NAT论述比较详细,也是网上讨论最多的话题,相比之下,穿透Http、Sock代理就简单一些。此外,穿透NAT发现对等点的办法还有一些,例如多播,但由于现有Internet对多播并不友好,同时多播是无连接和不可靠的,其实现有难度。

许多软件都是按照上述一些技术实现了P2P通信,着名的有MSN、QQ和BitTorrent下载软件等。

实际上,围绕P2P通信,尤其是两个不同局域网间的P2P,已经有许多的P2P协议和开发包涌现。例如,Sun公司以Java写的开发包Jxta,微软在Windows XP平台上有P2P的β版开发包,Intel公布.Net平台上的P2P应用开发工具包,放到微软有关.Net平台上供用户免费下载。

但是利用它们来开发程序,非常繁琐,我们需要用简单的实现完成功能就可以了。

如果想研究得更深入仔细,请从Sun公司的网站和微软网站下载开发包,或者在Google里

搜索协议和开发包。

下面其实有两个实例,讲述连通的过程,包括简单伪代码。

我们不希望在IP层实现我们的P2P,而是希望在应用层,利用Windows提供的Socket建立P2P,至多下到用原始Raw Socket来写P2P。

首先看,我们对于公网有服务器做“中介(非中转)”的P2P怎么实现。

原理讲述:

例如AB两台机器分别处于两个不同的局域网后,由Server做中介,先看连接过程。

A首先连接服务器,采用UDP发包给Server,这个包包括了A的用户信息,类似于QQ的

QQ号、呢称等。Server方,可以用CSocket::GetPeerName()得到A的IP及端口,但得到的IP和端口应该是A的代理网关的公网PublicIP及其映射端口NatPort,该映射端口就是A的代理网关为A的本次UDP通信临时分配的Nat端口。可以断言,得到的端口一定不是A的内网IP和内网UDP端口。

服务器然后将A的公网IP、映射端口、用户信息等保存到(内存列表或者数据库),这样标志着A已经上线。服务器马上将其它在线的用户信息发回给A,包括其它用户的代理网关的公网IP及Nat端口。A同样将在线用户的这些信息保存并显示为列表,期待A用户做出选择。

对于B,同样有上述的上线过程。

当A用户做出选择,要和在线的B用户通信时,A首先发UDP包给B的公网IP及Nat端口,并立即发一个UDP包给服务器,让服务器去通知B,叫B给A也发一个UDP包。

换句话说,1、A发包给PublicB, 2、A发包给Server,3、Server发包给PublicB,4、B发包给PublicA。

上面的叙述用到了"Public"字样,它代表代理网关的公网IP及其映射端口。

由于A和B各自的网关都保存了各自的端口映射关系,发到网关的数据,网关会按照这个映射关系转发给A和B。

当A和B都分别收到对方发来的UDP包以后,连接宣告成功,服务器即可以脱离,AB即可以用UDP通信。

何以如此麻烦?

A在发UDP包给Server上线时,A的网关(A.Gate)就分配一个Nat端口(A.NatPort)给A,用于A和Server间的本次UDP会话,但A的网关明确标记,这个Nat端口,仅能用于A和Server之间的UDP通信,不能挪着它用。并且,这个临时分配的端口,只能保持一个很短的时效,也许是一两秒吧。这个时间内,如果A与Server没有任何通信,那么这个映射端口就宣告无效。下次,A和Server又要通信时,A的网关又会重新分配一个新的端口。这段表明三点:

1、A与Server的通信,需要A网关分配Nat端口来中转。

2、Nat端口只能用于A和Server间的通信。

3、Nat端口存在生存期,长时间A和Server无通信,该端口即宣告无效。

就是这些麻烦,使得我们的连接过程必须绕很多弯。

A和B的通信,就是借助事先AB分别与Server连接时,在各自的网关处建立的端口映射来通信。为避免上面的2、3点麻烦,A和B在初次连接时,必须几乎同时向对方发包。

如果A、B不同时发包给对方,它们各自的网关就会虑掉对方的包,因为该包不是Server发来的包,叫做不请自来的包。

并且,即便AB各自的网关不虑掉非Server发来的包,它们各自的Nat端口也有一个时效。那么A与Server,B与Server就不得不发心跳包,以维持各自的映射端口,保证其不失效。

上面的过程中,如果A和B建立连接失败,可以循环这个过程,直到一个有限的次数之后,仍不能连接则宣告失败。

大致就如此。以下是上述过程的一些伪代码,不太容易懂,不如看前面的论述。

下面将讨论,A和B同时发包的另外一个办法,以及关于静态端口映射、谁能做"中介"、

是否可以建立TCP通信等细节及其引申。

const TCHAR*  ServerIP   = _T("61.10.10.10");  //中介服务器,事先配置的

const UINT    ServerPort = 4500;               //中介服务器UDP端口,事先配置的

TCHAR   PubIP[256]; //对方机器的代理网关的公网IP

UINT    PubPort;    //对方机器的代理网关的公网映射端口

UINT    ClientPort = 4501;               //A和B的UDP端口

连接服务器用UDP,下面是客户方(AB的Socket)

class CClientSocket : public CSocket

{

public:

virtual void OnReceive( int nErrCode );

}

void CClientSocket::OnReceive( int nErrCode )

{

int       nFlag, *pFlag;  //UDP包标志,位于包头4个字节

TCHAR     PeerIP[128];    //UDP对方的IP

UINT      PeerPort;       //UDP对方的端口

char      RecBuf[64];     //假定包尺寸为64

//此处分析出对方IP和端口,即PeerIP和PeerPort

SOCKADDR  sa;

int       SockAddrLen = sizeof(sa);

GetPeerName( &sa, &SockAddrLen );

//从sa中取出PeerIP和PeerPort

Receive( RecBuf, 64 );

pFlag = (int*)RecBuf;

nFlag = *pFlag;

if( lstrcmp( PeerIP, ServerIP ) == 0 )  //如果是服务器返回的信息

{

switch( nFlag )

{

case  0:   //标识和服务器连接成功,RecBuf是服务器返回的其它在线用户

//的信息(包括对方的公网IP及端口),这里假定是B的信息

//从RecBuf取出B的代理网关的IP和端口放入PeerIP和PeerPort

lstrcpy( PubIP, PeerIP ); //PeerIP应该是对方的代理网关的公网IP

PubPort = PeerPort;       //同时PeerPort应该是对方的代理网关的公网映射端口

//接下来,给B的代理公网IP发一个UDP包,填充RecBuf,并使标志为0

SendTo( RecBuf, 64, PubPort, PubIP );

//马上叫服务器通知B,要B给A发一个UDP包,填充RecBuf,并使标志为1

SendTo( RecBuf, 64, ServerPort, ServerIP );

break;

case  1: //标识是来自服务器的通知,叫我(B)发一个UDP给A

//RecBuf里有A的代理公网IP和端口,取出来,放入PeerIP和PeerPort

lstrcpy( PubIP, PeerIP ); //PeerIP应该是对方的代理网关的公网IP

PubPort = PeerPort;       //同时PeerPort应该是对方的代理网关的公网映射端口

//给A发一个UDP包,填充RecBuf,并使标志为1

SendTo( RecBuf, 64, PubPort, PubIP );

break;

.

.

.

default:

break;

}

}

else //其它对等客户返回的信息

{

switch( nFlag )

{

case  0: //标识直接收到A发的UDP包

break;

case  1:  //标识是B发回的UDP包,但,是靠服务器通知B发的

//至此,可以判断AB互相是否连接成功,就是判断A发给B的UDP包B收到,而B发

//给A的UDP包A也收到,那么连接就是成功的。否则重复上面的过程。

break;

.

.

.

default:

break;

}

}

}

客户方先连接服务器,服务器收到后,返回所有在线的对等点用户信息

CClientSocket  cs;

char sBuf[64];  //sBuf的包标识为0,你可以填充其它任何信息,比如用户信息

cs.Create( ClientPort, SOCK_DGRAM );

cs.SendTo( sBuf, 64, ServerPort, ServerIP );

class CServerSocket : public CSocket

{

public:

virtual void OnReceive( int nErrCode );

}

void CServerSocket::OnReceive( int nErrCode )

{

int       nFlag, *pFlag;  //UDP包标志,位于包头4个字节

TCHAR     PeerIP[128];    //UDP对方的IP

UINT      PeerPort;       //UDP对方的端口

char      RecBuf[64];     //假定包尺寸为64

//此处分析出对方IP和端口,即PeerIP和PeerPort

SOCKADDR  sa;

int       SockAddrLen = sizeof(sa);

GetPeerName( &sa, &SockAddrLen );

//从sa中取出PeerIP和PeerPort

Receive( RecBuf, 64 );

pFlag = (int*)RecBuf;

nFlag = *pFlag;

switch( nFlag )

{

case  0:  //标识有客户连接

//给该客户返回所有在线用户

break;

case  1:  //来自A的通知,要我通知B,叫B发一个UDP包给A的公网IP

//从RecBuf中取出B的IP和端口放入PeerIP和PeerPort,填充RecBuf,标志为1

SendTo( RecBuf, 64, PeerPort, PeerIP );

break;

}

}

来源:微点阅读  https://www.weidianyuedu.com

关于不同局域网内经Internet的P2P通信技术相关推荐

  1. 不同局域网内经Internet的P2P通信技术总结

    以下将要用到一个叫做NAT的重要名词,先做点解释.  NAT是Net Address Translation(网络地址转换)的简称,就是说,局域网通常靠一个具有公网IP的代理网关服务器连到Intern ...

  2. 局域网内配置服务器方法--p2p技术、端口映射、网络域名

    最近公司要求在内网搭建服务器提供给外网使用,最主要就是在出差时能过方便的提供数据查看服务.局域网里面的PC机的IP地址只是用来内部地址区分的(内网IP),一般都为192.168.1.XX,当要和外网建 ...

  3. 使用ccproxy共享局域网内主机Internet

    这几天没办法回到宿舍上网,无奈宿舍的网络使用的是固定IP绑定MAC,上网是需要两个绑定外还得有拨号步骤.出了本来如果机子是32位的操作系统就可以在舍友不在的时候使用他的账号,又无奈的是自己的机器是64 ...

  4. 基于 P2P 技术的 Android 局域网内设备通信实践

    Android 局域网内的多设备通信方式有多种,其中常见的方式有: 基于 TCP/UDP 的 Socket 通信 基于 Bluetooth 的近场通信 基于 Wifi 的 Wi-Fi Direct 连 ...

  5. 单网卡部署WEB+Mail+FTP+ISA服务器之四:局域网内部署FTP和winwebmail服务器

    一.目的:构建简单FTP和MAIL服务器为下一步发布到互连网做准备.另外如果你觉得Exchange很麻烦,就用winwebmail吧,绝对的简单方便,安全性也不错. 二.网络环境: 三.局域网发布FT ...

  6. 如何在局域网内查找病毒主机

    如何在局域网内查找病毒主机 鄙视那些发arp,p2p管制类软件的朋友!!!!!! 一个净化的网络空间不光给别人带来便利,也会方便到自己!!! 试想以下当你发布了这些arp,p2p管制软件以后,这些软件 ...

  7. 怎么建立计算机共享文件夹,如何在局域网内建立共享文件夹

    在使用Windows 10工作时会遇到形形色色的问题,比如需在局域网内建立共享文件夹.那么如何操作呢?下面小编与你分享具体步骤和方法. 工具/材料 Windows 10操作系统 操作方法 01启动Wi ...

  8. 如何设置计算机网共享文件夹,局域网内如何设置共享文件夹呢

    在同一个局域网怎么设置共享文件夹呢,学习啦小编为你解除以后,希望对你有帮助哦! 局域网内设置共享文件夹方法如下: 1.ab两台电脑设置为局域网. 桌面右键网上邻居点属性,右键本地连接点属性,双击Int ...

  9. 局域网内计算机无法互相访问,轻松几招解决局域网不能互相访问故障

    经常有网友反映在Win2000和WinXP中浏览网上邻居时很慢,特别是网络中有Win9x和WinMe工作站时尤为明显,有时打开网上邻居甚至需要10多分钟,还经常报错,该如何解决呢?笔者以前也曾遇到过类 ...

  10. 局域网内建网站教程,如何零成本搭建自己的网站?

    无线局域网作为传统有线局域网络的补充和扩展,获得了家庭网络用户.中小型办公室用户.广大企业用户及电信运营商的青睐,得到了快速的应用. 如何实现局域网服务器搭建外网访问的网站? 在建站之前首先要知道需要 ...

最新文章

  1. RabbitMQ学习总结(7)——Spring整合RabbitMQ实例
  2. 如何在7分钟内黑掉40家网站?
  3. pythonif有多个条件怎么办_Python中if有多个条件处理方法
  4. redis 系列26 Cluster高可用 (1)
  5. 将list对象转换为QuerySet对象
  6. 在vue中methods互相调用的方法
  7. php mailer altbody,PHPMailer发送邮件中文乱码的解决办法
  8. php中mysqli 处理查询结果集的几个方法
  9. 计算机字处理软件word文档,2012计算机字处理软件 Word(answer)
  10. javascript基础知识(13) Date
  11. SQlite数据库的C编程接口(六) 返回值和错误码(Result Codes and Error Codes) ——《Using SQlite》读书笔记
  12. 还真碰到不会cd命令的同事
  13. python插入排序
  14. html中标记pre的作用是什么,html中pre和code标签
  15. dota2 服务器尚未更新到最新版本,dota2更新不动_steam dota2更新不动
  16. Unity中扫描二维码将电脑照片保存在手机中
  17. 常用jquery方法 总结
  18. C语言课设-单位车辆调度管理
  19. 11月赠书活动获奖读者
  20. Android使用WebView将网页打包成APP

热门文章

  1. Python 转义字符与原字符
  2. 微信抖音的服务器,抖音微信登陆未获得权限怎么办
  3. 用74HC573进行LED数码管驱动的编程
  4. CentOs7 docker部署face_recognition
  5. vue接入腾讯防水墙代码
  6. PROXMOX VE备份还原
  7. java 判断邮箱_java使用正则表达式判断邮箱格式是否正确的方法
  8. 腾讯+android+hotfix,发布到安卓平台报这个错误,xLua exception : xlua.access, no field __Hotfix0_Update...
  9. 实验一计算机网络基础知识,计算机网络实验基础知识 集线器的使用
  10. 深入浅出MFC:Windows程序的生与死