其实很早我就已经实现了使用TCP协议穿透NAT了,但是苦于一直没有时间,所以没有写出来,现在终于放假有一点空闲,于是写出来共享之。

一直以来,说起NAT穿透,很多人都会被告知使用UDP打孔这个技术,基本上没有人会告诉你如何使用TCP协议去穿透(甚至有的人会直接告诉你TCP协议是无法实现穿透的)。但是,众所周知的是,UDP是一个无连接的数据报协议,使用它就必须自己维护收发数据包的完整性,这常常会大大增加程序的复杂度,而且一些程序由于某些原因,必须使用TCP协议,这样就常常令一些开发TCP网络程序的人员“谈穿透色变”。那么,使用TCP协议是不是就不能实现穿透呢?答案当然是否定的:TCP协议不仅能实现NAT穿透,而且实现起来比UDP穿透甚至还简单一些。

要了解如何使用TCP穿透NAT,就要首先看看如何使用UDP穿透NAT。 
    我们假设在两个不同的局域网后面分别有2台客户机A和 B,AB所在的局域网都分别通过一个路由器接入互联网。互联网上有一台服务器S。 
    现在AB是无法直接和对方发送信息的,AB都不知道对方在互联网上真正的IP和端口, AB所在的局域网的路由器只允许内部向外主动发送的信息通过。对于B直接发送给A的路由器的消息,路由会认为其“不被信任”而直接丢弃。 
    要实现 AB直接的通讯,就必须进行以下3步:A首先连接互联网上的服务器S并发送一条消息(对于UDP这种无连接的协议其实直接初始会话发送消息即可),这样S就获取了A在互联网上的实际终端(发送消息的IP和端口号)。接着 B也进行同样的步骤,S就知道了AB在互联网上的终端(这就是“打洞”)。接着S分别告诉A和B对方客户端在互联网上的实际终端,也即S告诉A客户B的会话终端,S告诉B客户A的会话终端。这样,在AB都知道了对方的实际终端之后,就可以直接通过实际终端发送消息了(因为先前双方都向外发送过消息,路由上已经有允许数据进出的消息通道)。

用UDP来实现以上3步不存在什么理论上的问题,因为UDP是无连接的协议,它允许socket进行“多对一”的通讯(即几个具有不同IP和端口号的socket向一个接收socket发送消息)。但是使用TCP就出现了问题:在一般情况下,TCP socket不允许在已经建立连接的端口上再进行监听和使用该本地端口。换句话说,当AB连接上服务器S后,S将AB的实际终端告诉对方,下一步本该是AB利用对方的实际终端进行直连,但这时你会发现对方的实际终端已经被占用了(就是各自连接到服务器S的会话占用了终端),无法同时listen和 connect。于是很多人得出结论:TCP无法实现NAT穿透。 
    于是问题的关键变成了如何复用一个TCP连接的本地终端,这其实不是协议的问题,而是一个API的问题。幸运的是,所有主流操作系统都支持一个特定的TCP套接字选项——SO_REUSEADDR。这个选项允许将多个socket绑定到同一个本地终端。我们建立socket的时候只要加上这么一行:

setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &flag, len) ;   //C++就这么做
_Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, True)  '这是vb.net 更加简单

知道上面的知识就很好办了,下面我来说说TCP协议的穿透流程: 
    机器布局还是和上面使用UDP的一样。现在假设客户A想和客户B建立TCP连接。 
首先还是 AB分别和服务器S分别建立连接,S记录AB的互联网实际终端。然后S分别向AB发送对方的实际终端。接着,从A和B向S连接时使用的端口,AB都异步调用connect函数连接对方的实际终端(就是S告诉的终端),同时,AB双方都在同一个本地端口监听到来的连接(也可以先监听,再connect更好)。由于双方都向对方发送了connect请求(假设各自的SYN封包已经穿过了自己的NAT),因此在对方connect请求到达本地的监听端口时,路由器会认为这个请求是刚刚那个connect会话的一部分,是已经被许可的,本地监听端口就会用SYN-ACK响应,同意连接。这样,TCP穿透NAT的点对点连接就成功了。

使用TCP协议的NAT穿透技术(转)的更多相关文章

  1. 使用TCP协议的NAT穿透技术

    一直以来,说起NAT穿透,很多人都会被告知使用UDP打孔这个技术,基本上没有人会告诉你如何使用TCP协议去穿透(甚至有的人会直接告诉你TCP协议是无法实现穿透的).但是,众所周知的是,UDP是一个无连 ...

  2. 使用TCP协议的NAT穿透技术 (转载)

    其实很早我就已经实现了使用TCP协议穿透NAT了,但是苦于一直没有时间,所以没有写出来,现在终于放假有一点空闲,于是写出来共享之. 一直以来,说起NAT穿透,很多人都会被告知使用UDP打孔这个技术,基 ...

  3. 网络协议之NAT穿透

    NAT IPv4地址只有32位,最多只能提供大致42.9亿个唯一IP地址,当设备越来越多时,IP地址变得越来越稀缺,不能为每个设备都分配一个IP地址.于是,作为NAT规范就出现了.NAT(Networ ...

  4. p2p网络中的NAT穿透技术----常见NAT穿越解决方案

    转:http://blog.csdn.net/cllzw/article/details/46438257 常见NA丁穿越解决方案 NAT技术在缓解IPv4地址紧缺问题.构建防火墙.保证网络安全等方面 ...

  5. WebRTC下 的 NAT 穿透技术

    NAT的概念模型 NAT名字很准确,网络地址转换,就是替换IP报文头部的地址信息.NAT通常部署在一个组织的网络出口位置,通过将内部网络IP地址替换为出口的IP地址提供公网可达性和上层协议的连接能力. ...

  6. NAT穿透解决方案介绍

    最近公司要实现在各种网络环境下面的多屏互动(机顶盒.android phone.iphone及PC端)的需求:由于IP地址资源有限的原因,目前我们使用的各种终端设备都位于局域网后面也就是多台设备共享同 ...

  7. NAT穿透解决

    1.各种网络环境下的P2P通信解决方法: (1)如果通信双方在同一个局域网内,这种情况下可以不借助任何外力直接通过内网地址通信即可:   (2)如果通信双方都在有独立的公网地址,这种情况下当然可以不借 ...

  8. NAT穿透解决方案介绍(转)--Java ICE实现

    转:http://www.cnblogs.com/javaminer/p/3575282.html 最近公司要实现在各种网络环境下面的多屏互动(机顶盒.android phone.iphone及PC端 ...

  9. NAT穿透的方式

    目前主要的NAT类型有如下几种: 1)Full-cone NAT, also known as one-to-one NAT 一旦一个内网地址 (iAddr:iPort) 被映射到一个外部地址 (eA ...

随机推荐

  1. iOS 抓取 HTML ,CSS XPath 解析数据

    以前我们获取数据的方式都是使用 AFN 来 Get JSON 数据,比如 点我查看 JSON 数据.http://news-at.zhihu.com/api/4/news/latest 但例如下面的百 ...

  2. postmessage and sendmessage

    从msdn上看二者的解释: postmessage : Places (posts) a message in the message queue associated with the thread ...

  3. webpack window 使用sass来编译css样式

    1.执行安装: npm install sass-loader --save-dev (此处不行的话就换上npm install node-sass) 2.稍微修改一下config,删掉我们先前添加的 ...

  4. Android----输入模式设置

    InputType的参数: 用法:((EditText)findViewById(R.id.edit)).setInputType(InputType.*); int TYPE_CLASS_DATET ...

  5. 【struts2】预定义拦截器

    1)预定义拦截器 Struts2有默认的拦截器配置,也就是说,虽然我们没有主动去配置任何关于拦截器的东西,但是Struts2会使用默认引用的拦截器.由于Struts2的默认拦截器声明和引用都在这个St ...

  6. Python学习笔记009—函数

    1. 空函数 如果想定义一个什么事也不做的空函数,可以用pass语句: def nop(): pass pass语句什么都不做,那有什么用?实际上pass可以用来作为占位符,比如现在还没想好怎么写函数 ...

  7. MongoDB学习笔记(11) --- 聚合

    MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果.有点类似sql语句中的 count(*) aggregate() 方法 MongoDB中聚 ...

  8. php 将秒数转换为时间(年、天、小时、分、秒)

    $t=1637544; $d=Sec2Time($t); $d为  0年18天 22小时52分24秒 //将秒数转换为时间(年.天.小时.分.秒) function Sec2Time($time){ ...

  9. 如何学好C、C++语言

    如何学好C语言 有人在酷壳的留言版上询问下面的问题 keep_walker : 今天晚上我看到这篇文章. http://programmers.stackexchange.com/questions/ ...

  10. 供CImage类显示的半透明PNG文件处理方法

    原文链接: http://blog.sina.com.cn/s/blog_4070692f010003gy.html   前补:没想到这个帖子好像挺多人看哪……看来大家都被这个png郁闷的够呛.显示p ...