简谈udp打洞和tcp打洞

UDP打洞技术:

对于两个peer,A和B。

  • 1、若A和B位于同一个nat之后。如果nat支持回环转换,A和B之间打洞时使用彼此的外网地址是可以连通的。但是最好是优先尝试内网连接。

  • 2、若A和B位于不同的nat之后。若两个nat都是公网地址,则属于“典型”连接过程,连接相对简单可靠。

  • 3、若A和B位于多级nat之后。
    a)A和B有相同的出口nat。则使用内网地址连接,可能会存在明显错误(需要鉴别)。若顶级nat不支持回环转换,则连接会失败。
    b)A和B在不同的出口nat后。则过程类似情况2。

  • 4、若A和B只有一方在nat之后。利用反向连接的方法完成。(也可以采用tcp协议连接)。

  • 连接过程:
    依据stun协议鉴别出的nat类型,采取合适的连接过程。
    两个nat4之后的peer仍然无法直接完成连接,有需要时可采用TURN协议建立“连接”(通过中继完成的通信)。

  • 重要问题:
    1、udp会经常收到非预期的数据包,所以需要做过滤和鉴别。比如,双方连接时,需要从服务器拿到相同的令牌,数据包提交了这个令牌才认为是有效的。


TCP打洞技术:

  • tcp打洞也需要NAT设备支持才行。
    tcp的打洞流程和udp的基本一样,但tcp的api决定了tcp打洞的实现过程和udp不一样。
    tcp按cs方式工作,一个端口只能用来connect或listen,所以需要使用端口重用,才能利用本地nat的端口映射关系。(设置SO_REUSEADDR,在支持SO_REUSEPORT的系统上,要设置这两个参数。)

  • 连接过程:(以udp打洞的第2种情况为例(典型情况))
    nat后的两个peer,A和B,A和B都bind自己listen的端口,向对方发起连接(connect),即使用相同的端口同时连接和等待连接。因为A和B发出连接的顺序有时间差,假设A的syn包到达B的nat时,B的syn包还没有发出,那么B的nat映射还没有建立,会导致A的连接请求失败(连接失败或无法连接,如果nat返回RST或者icmp差错,api上可能表现为被RST;有些nat不返回信息直接丢弃syn包(反而更好)),(应用程序发现失败时,不能关闭socket,closesocket()可能会导致NAT删除端口映射;隔一段时间(1-2s)后未连接还要继续尝试);但后发B的syn包在到达A的nat时,由于A的nat已经建立的映射关系,B的syn包会通过A的nat,被nat转给A的listen端口,从而进去三次握手,完成tcp连接。

  • 从应用程序角度看,连接成功的过程可能有两种不同表现:(以上述假设过程为例)
    1、连接建立成功表现为A的connect返回成功。即A端以TCP的同时打开流程完成连接。
    2、A端通过listen的端口完成和B的握手,而connect尝试持续失败,应用程序通过accept获取到连接,最终放弃connect(这时可closesocket(conn_fd))。
    多数Linux和Windows的协议栈表现为第2种。

  • 但有一个问题是,建立连接的client端,其connect绑定的端口号就是主机listen的端口号,或许这个peer后续还会有更多的这种socket。虽然理论上说,socket是一个五元组,端口号是一个逻辑数字,传输层能够因为五元组的不同而区分开这些socket,但是是否存在实际上的异常,还有待更多观察。

  • 另外的问题:
    1、Windows XP SP2操作系统之前的主机,这些主机不能正确处理TCP同时开启,或者TCP套接字不支持SO_REUSEADDR的参数。需要让AB有序的发起连接才可能完成。
    上述tcp连接过程,仅对NAT1、2、3有效,对NAT4(对称型)无效。
    由于对称型nat通常采用规律的外部端口分配方法,对于nat4的打洞,可以采用端口预测的方式进行尝试。

猜你喜欢

转载自blog.csdn.net/weixin_43667308/article/details/86260469