﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-野</title><link>http://www.cppblog.com/tianhongye/</link><description /><language>zh-cn</language><lastBuildDate>Sun, 26 Apr 2026 22:10:22 GMT</lastBuildDate><pubDate>Sun, 26 Apr 2026 22:10:22 GMT</pubDate><ttl>60</ttl><item><title>TURN</title><link>http://www.cppblog.com/tianhongye/archive/2015/01/15/209524.html</link><dc:creator>野</dc:creator><author>野</author><pubDate>Thu, 15 Jan 2015 04:11:00 GMT</pubDate><guid>http://www.cppblog.com/tianhongye/archive/2015/01/15/209524.html</guid><wfw:comment>http://www.cppblog.com/tianhongye/comments/209524.html</wfw:comment><comments>http://www.cppblog.com/tianhongye/archive/2015/01/15/209524.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tianhongye/comments/commentRss/209524.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tianhongye/services/trackbacks/209524.html</trackback:ping><description><![CDATA[<h4><b>2.1 </b><b>RFC5766/TURN</b></h4> <p>&nbsp;</p> <p>TURN，在RFC5766中定义，英文全称Traversal Using Relays around NAT（TURN）：Relay Extensions to Session Traversal Utilities for NAT（STUN），即使用中继穿透NAT：STUN的中继扩展。简单的说，TURN与STUN的共同点都是通过修改应用层中的私网地址达到NAT穿透的效果，异同点是TURN是通过两方通讯的“中间人”方式实现穿透。  <p>如果一个主机位于NAT的后面，在某些情况下它不能够与其他主机点对点直接连接。在这些情况下，它需要使用中间网点提供的中继连接服务。TURN协议就是用来允许主机控制中继的操作并且使用中继与对端交换数据。TURN与其他中继控制协议不同的是它能够允许一个客户端使用一个中继地址与多个对端连接。  <p>TURN协议被设计为ICE的一部分，用于NAT穿越，虽然如此，它也可以在没有ICE的地方单独使用。  <p>&nbsp;</p> <h5><b>2.1.1 </b><b>操作概述</b></h5> <p>&nbsp;</p> <p><img alt="STUN和TURN技术浅析2" src="http://www.h3c.com.cn/res/201206/01/20120601_1369601_image003_747038_97665_0.jpg"></p> <p>在一个典型组网中，一个TURN客户端连接在一个私有网络中，通过一个或多个NAT来连接到公网。在公网中有一个TURN服务器。在因特网的别处有一个或多个对端是这个TURN客户端希望通讯的。这些对端也有可能是在一个或多个NAT的后面。该客户端使用服务器作为一个中继来发送数据包 到这些对端去，并且从这些对端接收数据包。  <p>客户端通过一个IP地址和端口的组合来与服务器建立会话。客户端使用TURN命令在服务器上创建和操作一个ALLOCATION。一旦这个allocation创建好了，客户端能够在数据发往哪个对端的指示下发送应用数据到这个服务器，服务器将中继这些数据到合适的对端。客户端发送的应用数据包含在TURN消息中，服务器将数据提取出来，并以UDP数据包方式发送给对端。反向上，对端以UDP数据包方式发送应用数据到这个allocation提供的中继传输地址。因为TURN消息总是包含客户端与哪些对端通讯的指示，客户端能够使用单一的allocation来与多个对端通讯。  <p>&nbsp;</p> <h5><b>2.1.2 </b><b>术语</b></h5> <p>&nbsp;</p> <p>TURN client：遵循RFC5766的STUN客户端。  <p>TURN server：遵循RFC5766的STUN服务器。  <p>Peer：TURN客户端希望连接的主机。TURN服务器为TURN客户端和它的对端中继流量，但Peer并不与TURN服务器使用TURN协议进行交互，它接收从TURN服务器发送过来的数据，并向TURN服务器发送数据。  <p>Transport Address：IP地址与端口号的组合。  <p>Host Transport Address：客户端或对端的传输地址。  <p>Server-Reflexive Transport Address：NAT公网侧的传输地址，该地址由NAT分配，相当于一个特定的主机传输地址。  <p>Relayed Transport Address：TURN服务器上的传输地址，用于客户端和对端中继数据。  <p>TURN Server Transport Address：TURN服务器上的传输地址，用于客户端发送STUN消息给服务器。  <p>Peer Transport Address：服务器看到的对端的传输地址，当对端是在NAT后面，则是对端的服务器反射传输地址。  <p>Allocation：通过Allocate请求将中继传输地址提供给客户端，除了中继状态外，还有许可和超时定时器等。  <p>5-tuple：五元组，包括客户端IP地址和端口，服务器IP地址和端口和传输协议（包括UDP、TCP、TLS）的组合。  <p>Channel：通道号与对端传输地址的关联，一旦一个通道号与一个对端的传输地址绑定，客户端和服务器就能够利用带宽效应更大的通道数据消息来交换数据。  <p>Permission：一个对端允许使用它的IP地址和传输协议来发送数据到TURN服务器，服务器只为从对端发来的并且匹配一个已经存在的许可的流量中继到相应的客户端。  <p>Realm：服务器内用于描述服务器或内容的一个字符串，这个realm告诉客户端哪些用户名和密码的组合可用于认证请求。  <p>Nonce：服务器随机选择的一个字符串，包含在报文摘要中。为了防止中继攻击，服务器应该有规律的改变这个nonce。  <p>&nbsp;</p> <h5><b>2.1.3 </b><b>新的</b><b>STUN</b><b>方法</b></h5> <p>&nbsp;</p> <p>下面给出了新的STUN方法的编号：  <p>0x003&nbsp; Allocate  <p>0x004&nbsp; Refresh  <p>0x006&nbsp; Send  <p>0x007&nbsp; Data  <p>0x008&nbsp; CreatePermission  <p>0x009&nbsp; ChannelBind  <p>&nbsp;</p> <h5><b>2.1.4 </b><b>新的</b><b>STUN</b><b>属性</b></h5> <p>&nbsp;</p> <p>0x000c&nbsp; CHANNEL-NUMBER  <p>0x000D&nbsp; LIFETIME  <p>0x0010&nbsp; Reserved&nbsp; (was BANDWIDTH)  <p>0x0012&nbsp; XOR-PEER-ADDRESS  <p>0x0013&nbsp; DATA  <p>0x0016 XOR-RELAYED-ADDRESS  <p>0x0018&nbsp; EVEN-PORT  <p>0x0019&nbsp; REQUESTED-TRANSPORT  <p>0x001A&nbsp; DON’T-FRAGMENT  <p>0x0021&nbsp; Reserved&nbsp; (was TIMER-VAL)  <p>0x0022&nbsp; RESERVATION-TOKEN  <p>上面属性中的部分属性长度不是4字节的倍数，采用STUN的规则，使用1~3个padding字节来补齐。  <p><b>CHANNEL-NUMBER</b>  <p>CHANNEL-NUMBER属性包含通道的号码。属性长4字节，包含16比特的无符号整数和2字节的RFFU（Reserved For Future Use）字段，该字段必须设为0且在接收时被忽略。  <p>字节  <p>0&nbsp; 1&nbsp; 2&nbsp; 3  <p>Channel Number  <p>RFFU=0  <p><b>LIFETIME</b>  <p>LIFETIME属性表示服务器在没有收到refresh时维持一个allocation的持续时间。属性长4字节，包含一个32比特的无符号整数值，表示剩余多少秒终止  <p><b>XOR-PEER-ADDRESS</b>  <p>XOR-PEER-ADDRESS指定从TURN服务器看到的对端的地址和端口，例如如果对端是在一个NAT后面，则为对端的server-reflexive传输地址。  <p><b>DATA</b>  <p>DATA属性存在于所有的Send和Data indications消息中。属性的值是可变长度的，包括应用数据。如果属性的长度不上4字节的倍数，必须进行填充。  <p><b>XOR-RELAYED-ADDRESS</b>  <p>XOR-RELAYED-ADDRESS存在于所有的Allocate响应中。它指定了服务器分配给客户端的地址和端口。  <p>字节  <p>0&nbsp; 1&nbsp; 2&nbsp; 3  <p>00000000  <p>地址族  <p>端口号  <p>IP地址（32位或128位）  <p><b>EVEN-PORT</b>  <p>这个属性允许客户端请求在中继传输地址的端口为偶数，并且服务器可选的保留紧跟着的下一个端口号。属性的值长1字节，结构如下：  <p>字节  <p>0&nbsp; <p>R  <p>RFFU  <p>值包括一个1比特标志字段：  <p>R：如果为1，服务器被请求保留下一个更高的端口号（基于同一个IP地址）为随后的allocation。如果为0，则不请求保留。属性的其他7比特值必须设置为0，并且在接收时被忽略。因为属性不是4字节的倍数，必须进行填充。  <p><b>REQUESTED-TRANSPORT</b>  <p>客户端通过该属性为已分配的传输地址请求一个特定的传输协议。属性的值是4字节长度的。  <p>字节  <p>0&nbsp; 1&nbsp; 2&nbsp; 3  <p>Protocol  <p>RFFU  <p>协议字段指定了需求的协议。可以取自IPv4报头中的协议字段的值或IPv6报头的下一个报头字段的协议号。目前仅允许设置为17，即UDP。RFFU字段在传输时必须设置为0，并在接收时被忽略。保留用于未来使用。  <p><b>DON’T-FRAGMENT</b>  <p>客户端使用该属性来请求服务器设置IP报头中的DF（不要分片）位，当中继应用数据到对端时。该属性没有值，因此属性长度字段为0。  <p><b>RESERVATION-TOKEN</b>  <p>RESERVATION-TOKEN属性包含一个token来唯一的标识一个中继传输地址已经被服务器保留。服务器在一个成功响应中包含该属性来告诉客户端这个token，客户端在接下来的Allocate请求中包括该属性来请求服务器为这个allocation使用那个中继传输地址。属性值是8字节长。  <p>&nbsp;</p> <h5><b>2.1.5 </b><b>新的</b><b>STUN</b><b>错误响应号</b></h5> <p>&nbsp;</p> <p>403（Forbidden）：请求是有效的，但因管理或类似的规定而不能被执行。  <p>437（Allocation Mismatch）：服务器接收到请求，要求在适当的位置的allocation，但没有allocation存在，或者一个收到的请求不指定任何allocation，但是一个allocation存在。  <p>441（Wrong Credentials）：请求中的证书没有匹配那些用来创建allocation的证书  <p>442（不支持的传输协议）：Allocate请求要求服务器使用一个用于服务器和对端的传输协议但是该服务器不支持该传输协议。  <p>486（Allocation Quota Reached）：目前没有更多的allocations资源使用相同的用户名可被创建。  <p>508（Insufficient Capacity）：服务器不能够完成请求因为一些性能限制已经达到上限。在一个Allocate响应中，这可能因为服务器此时已经没有更多的中继传输地址资源了，没有更多的被请求的性能，或者相当于特定的保留的token不可用。  <h5><b>2.1.6 </b><b>协议交互过程详细举例</b></h5> <p>&nbsp;</p> <p><img alt="STUN和TURN技术浅析2" src="http://www.h3c.com.cn/res/201206/01/20120601_1369602_image004_747038_97665_0.jpg"></p> <p>客户端使用10.1.1.2:49271作为传输地址向服务器的传输地址发送Allocate请求。客户端随机选择一个96位的事务ID。该Allocate请求消息包括SOFTWARE属性来提供客户端的软件版本信息；包括LIFETIME属性，指明客户端希望该allocation具有1小时的生命期而非缺省的10分钟；包括REQUESTED-TRANSPORT属性来告诉服务器与对端之间采用UDP协议来传输；包括DONT-FRAGMENT属性因为客户端希望在随后的Send indications中使用DON’T-FRAGMENT属性。  <p>服务器需要任何请求必须是经过认证的，因此服务器拒绝了该最初的Allocation请求，并且回应了携带有错误响应号为401（未授权）的Allocate错误响应；该响应包括一个REALM属性，指明认证的域；还包括一个NONCE属性和一个SOFTWARE属性。  <p>客户端收到了错误响应号为401的Allocate错误响应，将重新尝试发送Allocate请求，此时将包括认证属性。客户端在新的请求中重新选择一个新的事务ID。客户端包括一个USERNAME属性，使用从服务器那收到的realm值来帮助它决定使用哪个值；请求还包括REALM和NONCE属性，这两个属性是从收到的错误响应中拷贝出来的。最后，客户端包括一个MESSAGE-INTEGRITY属性。  <p>服务器收到认证的Allocate请求后，检查每个属性是否正确；然后，产生一个allocation，并给客户端回应Allocate成功响应。服务器在该成功响应中携带一个LIFETIME属性，本例中服务器将客户端请求的1小时生命期减小为20分钟，这是因为这个特定的服务器可能不允许超过20分钟的生命期；该响应包括XOR-RELAYED-ADDRESS属性，值为该allocation的中继传输地址；该响应还包括XOR-MAPPED-ADDRESS属性，值为客户端的server-reflexive地址；该响应也包含一个SOFTWARE属性；最后，包括一个MESSAGE-INTEGRITY属性来证明该响应，确保它的完整性；  <p><img title="STUN和TURN技术浅析2" alt="STUN和TURN技术浅析2" src="http://www.h3c.com.cn/res/201206/01/20120601_1369603_image005_747038_97665_0.jpg" width="554" height="231">  <p>接着，客户端为了准备向对端A发送一些应用数据而创建一个permission。这里通过一个CreatePermission请求来做到。该请求携带XOR-PEER-ADDRESS属性包含有确定的请求的IP地址，这里为对端A的地址；需要注意的是，属性中地址的端口号被设置为0在CreatePermission请求中，并且客户端使用的是对端A的server-reflexive地址而不是它的主机地址（私网地址）；客户端在该请求中携带与之前的Allocate请求中一样的username、realm和nonce值，因此该请求被服务器认可。此时在该请求中，客户端没有携带SOFTWARE属性。  <p>服务器收到该CreatePermission请求，产生一个相应的许可，并以CreatePermission成功响应来回应。该响应中只包含了Transaction-ID和MESSAGE-INTEGRITY属性。  <p><img title="STUN和TURN技术浅析2" alt="STUN和TURN技术浅析2" src="http://www.h3c.com.cn/res/201206/01/20120601_1369604_image006_747038_97665_0.jpg" width="554" height="216">  <p>现在客户端使用Send indication来发送应用数据到对端A。对端的server-reflexive传输地址包含在XOR-PEER-ADDRESS属性中，应用数据包含在DATA属性中。客户端已经在应用层上执行了路径MTU发现功能，因此通过DON’T-FRAGMENT属性来告知服务器当通过UDP方式来向对端发送数据时应设置DF位。Indications不能使用长期证书机制来认证，所以该消息中没有MESSAGE-INTEGRITY属性。  <p>服务器收到Send indication后，提取出应用数据封装成UDP格式发给对端A；UDP报文的源传输地址为中继传输地址，并设置DF位。  <p>对端A回应它自己的包含有应用数据的UDP包给服务器。目的地址为服务器的中继传输地址。当服务器收到后，将生成Data indication消息给客户端，携带有XOR-PEER-ADDRESS属性。应用数据包含在DATA属性中。  <p><img title="STUN和TURN技术浅析2" alt="STUN和TURN技术浅析2" src="http://www.h3c.com.cn/res/201206/01/20120601_1369605_image007_747038_97665_0.jpg" width="553" height="219">  <p>客户端现在若要绑定一个通道到对端B，将指定一个空闲的通道号（本例中为0x4000）包含在CHANNEL-NUMBER属性中，对端B的传输地址包含在XOR-PEER-ADDRESS属性中。与以前一样，客户端再次利用上次请求中的username、realm和nonce。  <p>当服务器收到该请求后，服务器绑定这个对端的通道号，为对端B的IP地址安装一个permission，然后给客户端回应一个ChannelBind成功响应消息。  <p><img title="STUN和TURN技术浅析2" alt="STUN和TURN技术浅析2" src="http://www.h3c.com.cn/res/201206/01/20120601_1369606_image008_747038_97665_0.jpg" width="554" height="220">  <p>客户端现在发送一个ChannelData消息给服务器，携带有发送给对端B的数据。这个消息不是一个STUN消息，因此没有事务ID。它之有3个字段：通道号、数据、数据长度；服务器收到后，检查通道号后发现当前已经绑定了，就以UDP方式发送数据给对端B。  <p>接着，对端B发送UDP数据包回应给服务器的中继传输地址。服务器收到后，回应给客户端ChannelData消息，包含UDP数据包中的数据。服务器知道是给哪个客户端发送ChannelData消息，这是因为收到的UDP数据包中的目的地址（即服务器的中继传输地址），并且知道使用的是哪个通道号，这是因为通道已经与相应的传输地址绑定了。  <p><img title="STUN和TURN技术浅析2" alt="STUN和TURN技术浅析2" src="http://www.h3c.com.cn/res/201206/01/20120601_1369607_image009_747038_97665_0.jpg" width="554" height="376">  <p>有时候，20分钟的生命期已经到了，客户端需要刷新allocation。此时通过发送Refresh请求来进行。该请求包含最后一次使用的username、realm和nonce，还包含SOFTWARE属性。当服务器收到这个Refresh请求时，它注意到这个nonce值已经超期了，则给客户端回应一个错误响应号为438（过期Nonce）的Refresh错误响应，并提供一个新的nonce值。可护端将重试该请求，此时携带新的nonce值。若第二次尝试被接受，服务器将回应一个成功响应。需要注意的是，此时客户端在请求中没有携带LIFETIME属性，所以服务器刷新客户端的allocation时采用缺省的10分钟生命期。  <h3>3 总结</h3> <p>在现实Internet网络环境中，大多数计算机主机都位于防火墙或NAT之后，只有少部分主机能够直接接入Internet。很多时候，我们希望网络中的两台主机能够直接进行通信（即所谓的P2P通信），而不需要其它公共服务器的中转。由于主机可能位于防火墙或NAT之后，在进行P2P通信之前，我们需要进行检测以确认它们之间能否进行P2P通信以及如何通信。这种技术通常被称为NAT穿透（NAT Traversal）。  <p>RFC3489中定义的STUN，即简单地用UDP穿过NAT（STUN）是个轻量级的协议。它允许应用发现它们与公共互联网之间存在的NAT和防火墙及其他类型。它还为应用提供判断NAT给它们分配的公共网际协议（IP）地址。STUN可工作在许多现存NAT上，并且不需要它们做任何特别的行为。它允许广泛的各类的应用穿越现存的NAT设施。  <p>RFC5389中对STUN协议进行了修订，将其定位于为穿透NAT提供工具，即NAT会话穿透效用是一个用于其他解决NAT穿透问题协议的协议。它可以用于终端设备检查由NAT分配给终端的IP地址和端口号。同时，它也被用来检查两个终端之间的连接性，好比是一种维持NAT绑定表项的保活协议。STUN本身并不是一种完整的NAT穿透解决方案。它相当于是一种NAT穿透解决方案中的工具。这是与先前的版本相比最重要的改变。之前的RFC3489中定义的STUN是一个完整的穿透NAT解决方案。此外，最大的区别是支持TCP穿透。  <p>RFC5766中对STUN协议再次进行了扩展，即中继穿透NAT：STUN的扩展。TURN与STUN的共同点都是通过修改应用层中的私网地址达到NAT穿透的效用，异同点是TUN采用了两方通讯的“中间人”方式实现穿透，突破了原先STUN协议无法在两台主机不能够点对点直接连接下提供作用的限制。  <p>技术无止境，NAT穿透技术仍在不断更新中，这里只对STUN/TURN协议作了简单的介绍，具体细节请参考RFC3489/5389/5766。  <img src ="http://www.cppblog.com/tianhongye/aggbug/209524.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tianhongye/" target="_blank">野</a> 2015-01-15 12:11 <a href="http://www.cppblog.com/tianhongye/archive/2015/01/15/209524.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>