对stun/turn协议的认识与理解过程笔记

发布: 2014-05-18 12:46

stun协议的全称是 Session Traversal Utilities for NAT,NAT会话传输应用程序
turn协议的全称是 Traversal Using Relays around NAT使用中继穿透NAT.

这里提到了NAT,有必要先介绍下NAT相关的信息。
其实这两个协议是转为有了NAT才出现的协议,也就是为了解决NAT带来的问题的方案。

NAT又是什么呢?

NAT的全称是Network Address Translate,即网络地址翻译。
那么我们互联网络为什么需要NAT呢?
是由于在现在的IPV4设计初期,并没有想到能使用的这么广泛,
设计者以为IP从0.0.0.0-255.255.255.255这么多就足够用了。
实际上,随着互联网的超快速的发展,这些IP地址还真就不够用了,
于是,有人想到一个解决方案,采用分局域网的办法,有一台机器使用一个公网IP,
在局域网内的机器使用一段不同局域网可以重复的IP段,这样,能连接到互联网上的机器数量就能远大于IP地址数量,
而不需要每个机器都有一个公网IP地址。

这个IP地址不够用问题是解决了,局域网内的机器也能够访问互联网了,
而这个解决方案的核心就在于NAT设备,把局域网内的机器的内网IP,转换为NAT设备的公网IP地址,
再发送到互联网上,请求的响应回来之后,NAT设备再通过这个映射关系,知道把数据包返回给局域网内的哪台机器上。

现在,另一个问题来了,如果一局域网的机器想访问另一局域网的机器,
这就难了,如何定位这台机器呢?
这时就需要本文本开头提到的stun/turn两个协议了。

也许本文对STUN的名称定义并不全面。
STUN协议最初在RFC3489中定义,名称为Simple Traversal of UDP throughs NATs,即简单的用UDP穿透NAT
而在后续的RFC5389中,名称变成了本文开头提到的名字,Session Traversal Utilities for NATs。
这里有点意思的是,这两个名字的缩写相同,目的基本也相同。
两者的区别是,RFC3489是作为完整的NAT穿透解决方案而定义的,
FC5389重新把STUN协议定位成为NAT穿透提供工具,并且在后者中提到了关于TCP协议的NAT穿透定义。
从现实情况来看,RFC3489/STUN定位目标很高,却没能像预期中那样解决所有问题。

TURN协议定义的RFC5766中,这个协议也有点意思,它的定位是作为RFC5389的扩展,
一种采用中继方式实现NAT穿透的方法。

再往后发展,又定义了一个RFC6156,这个协议又是一个对RFC5766/TURN的扩展协议,
全名叫,Traversal Using Relays around NAT (TURN) Extension for
IPv6,即TURN协议的IPV6扩展。


接下来,通过一个实际例子对stun作更深入的体验。
我希望能做一个连接两个内网的节点的程序,被连接的这端作为服务存在,
但与其他公共服务不同的是这个服务运行在内网中,所以无法直接连接。

这其实是一个点对点的模式,所以首先想到的是用stun作打洞,让两个节点能够直接连接。
但在实际实现的过程中,通过检测发现两端都是对称型NAT,由stun提供的方式无法实现。
查找了一些开源的P2P项目,也都没有实现两端都是对称型NAT的打洞,而且在维基百科上则直接说明这种情况无法采用打洞方式实现P2P连接,必须通过中继实现。

看来第一步必须是考虑STUN中继的模式了。还好有个STUN服务器可用,并且支持的功能比较全面。
既然使用STUN中继,则相关的标准是定义的RFC5766中了,开始阅读标准定义并实现相应的功能代码。

这里着重说明的地方,有allocate操作,它要求不能重复allocate申请,没有释放请求协议,而在在一段时间内自动失效。
而如果希望一直使用,则需要定时使用refresh请求保持allocate的资源。

在成功的allocate之后,发送数据之前,需要有一个createPermission的请求,
这个请求目的是对通信的两端进行授权,否则中继不会转发收到的包到对端,而是丢弃。
这个授权与allocate类似,也没有释放请求协议,并且会在一段时间内自动失效。
如果希望一直使用,则需要定时再次使用相同的请求参数发送createPermission请求。
这一点很重要,在这过程没有注意到文档中的这一说明,导致程序多次测试都会在一段时间的正常连接后,
数据发送不了的情况,通过几次反复测试与阅读RFC文档才发现是这个标准没有满足的问题。

在RFC5766标准中定义的中继数据的发送有两种机制,一种是使用send和data 请求,另一种使用channelbind请求。
send/data机制中,send请求是把数据发送到STUN服务端口,并通过中继端口发送到对端。
data请求把数据直接发送到中继端口,并通过STUN服务端口发送到对端。
需要注意的是,在对称型NAT的情况下,通过中继端口向对端发送数据的话,
实际需要对端先打一个到中继端口的NAT洞,并且必须保持这个NAT打洞会话不能失效。
否则无法收到STUN中继端口发送过来的数据,因为数据包已经被对称型NAT服务器给丢弃了。
data请求到中继端口的NAT会话通道,发送端也有保持这个NAT会话通道不失效的职责。

通过对这第一种机制的说明,从中了解到,由于UDP协议的无状态不可靠特点,为了保持某些会话的可用,
在实现实现功能代码的时候,需要用到大量的定时操作,像两端都需要保持NAT会话打洞的有效,
一端需要保持allocate资源有效,保持permission有效。
这些都是需要特别注意的实现细节,如果少了其中一个,可能会发现即使开始的时候通信开始了,
但过不多久这个通信信道就失效了,而且不容易查到问题所在。


资料:
http://tools.ietf.org/html/rfc5766





原文: http://qtchina.tk/?q=node/808

Powered by zexport