大家好,如果您还对turn服务器不太了解,没有关系,今天就由本站为大家分享turn服务器的知识,包括数据*过程的问题都会给大家分析到,还望可以解决大家的问题,下面我们就开始吧!

一、turn协议***数据*过程

在一个典型组网中,一个TURN客户端连接在一个私有网络中,通过一个或多个NAT来连接到公网。在公网中有一个TURN服务器。在因特网的别处有一个或多个对端是这个TURN客户端希望通讯的。这些对端也有可能是在一个或多个NAT的后面。该客户端使用服务器作为一个中继来发送数据包到这些对端去,并且从这些对端接收数据包。

客户端通过一个IP和端口的组合来与服务器建立会话。客户端使用TURN命令在服务器上创建和操作一个ALLOCATION。一旦这个allocation创建好了,客户端能够在数据发往哪个对端的指示下发送应用数据到这个服务器,服务器将中继这些数据到合适的对端。

客户端发送的应用数据包含在TURN消息中,服务器将数据提取出来,并以UDP数据包方式发送给对端。反向上,对端以UDP数据包方式发送应用数据到这个allocation提供的中继传输。

turn服务器 数据*过程

因为TURN消息总是包含客户端与哪些对端通讯的指示,客户端能够使用单一的allocation来与多个对端通讯。

turn术语

TURN client:遵循RFC5766的STUN客户端。

TURN server:遵循RFC5766的STUN服务器。

Peer:TURN客户端希望连接的主机。TURN服务器为TURN客户端和它的对端中继流量,但Peer并不与TURN服务器使用TURN协议进行交互,它接收从TURN服务器发送过来的数据,并向TURN服务器发送数据。

Transport Address:IP与端口号的组合。

Host Transport Address:客户端或对端的传输。

Server-Reflexive Transport Address:NAT公网侧的传输,该由NAT分配,相当于一个特定的主机传输。

Relayed Transport Address:TURN服务器上的传输,用于客户端和对端中继数据。

TURN Server Transport Address:TURN服务器上的传输,用于客户端发送STUN消息给服务器。

Peer Transport Address:服务器看到的对端的传输,当对端是在NAT后面,则是对端的服务器反射传输。

Allocation:通过Allocate请求将中继传输提供给客户端,除了中继状态外,还有许可和超时定时器等。

5-tuple:五元组,包括客户端IP和端口,服务器IP和端口和传输协议(包括UDP、TCP、TLS)的组合。

Channel:通道号与对端传输的关联,一旦一个通道号与一个对端的传输绑定,客户端和服务器就能够利用带宽效应更大的通道数据消息来*数据。

Permission:一个对端允许使用它的IP和传输协议来发送数据到TURN服务器,服务器只为从对端发来的并且匹配一个已经存在的许可的流量中继到相应的客户端。

Realm:服务器内用于描述服务器或内容的一个字符串,这个realm告诉客户端哪些用户名和密码的组合可用于认证请求。

Nonce:服务器随机选择的一个字符串,包含在报文摘要中。为了防止中继攻击,服务器应该有规律的改变这个nonce。

协议交互过程详细举例

以图2为例进行讲解,每个消息中,多个属*包含在消息中并显示它们的值。为了方便阅读,值以人们可读的格式来显示。

接着,客户端为了准备向对端A发送一些应用数据而创建一个permission。这里通过一个CreatePermission请求来做到。该请求携带XOR-PEER-ADDRESS属*包含有确定的请求的IP,这里为对端A的;需要注意的是,属*中的端口号被设置为0在CreatePermission请求中,并且客户端使用的是对端A的server-reflexive而不是它的主机(私网);客户端在该请求中携带与之前的Allocate请求中一样的username、realm和nonce值,因此该请求被服务器认可。此时在该请求中,客户端没有携带SOFTWARE属*。

服务器收到该CreatePermission请求,产生一个相应的许可,并以CreatePermission成功响应来回应。该响应中只包含了Transaction-ID和MESSAGE-INTEGRITY属*。

现在客户端使用Send indication来发送应用数据到对端A。对端的server-reflexive传输包含在XOR-PEER-ADDRESS属*中,应用数据包含在DATA属*中。客户端已经在应用层上执行了路径MTU发现功能,因此通过DON’T-FRAGMENT属*来告知服务器当通过UDP方式来向对端发送数据时应设置DF位。Indications不能使用长期证书机制来认证,所以该消息中没有MESSAGE-INTEGRITY属*。

服务器收到Send indication后,提取出应用数据封装成UDP格式发给对端A;UDP报文的源传输为中继传输,并设置DF位。

对端A回应它自己的包含有应用数据的UDP包给服务器。目的为服务器的中继传输。当服务器收到后,将生成Data indication消息给客户端,携带有XOR-PEER-ADDRESS属*。应用数据包含在DATA属*中。

接着,对端B发送UDP数据包回应给服务器的中继传输。服务器收到后,回应给客户端ChannelData消息,包含UDP数据*的数据。服务器知道是给哪个客户端发送ChannelData消息,这是因为收到的UDP数据*的目的(即服务器的中继传输),并且知道使用的是哪个通道号,这是因为通道已经与相应的传输绑定了。

二、使用docker搭建STUN/TURN服务器

前言

   STUN,首先在RFC3489中定义,作为一个完整的NAT穿透解决方案,英文全称是Simple Tr*ersal of UDP Through NATs,即简单的用UDP穿透NAT。

    TURN,首先在RFC5766中定义,英文全称是Tr*ersal Using Relays around NAT:Relay Extensions to Session Tr*ersal Utilities for NAT,即使用中继穿透NAT:STUN的扩展  

    简单的说,TURN与STURN的共同点都是通过修改应用层中的私网达到NAT穿透的效果,异同点是TURN是通过两方通讯的“中间人”方式实现穿透。

   ICE的全称Interactive Connectivity Establ.shment(互动式连接建立),由IETF的MMUSIC工作组开发出来的,它所提供的是一种框架,使各种NAT穿透技术可以实现统一。

    STUN和TURN服务器和ICE可以参考阅读: P2P技术详解(三):P2P技术之STUN、TURN、ICE详解

  本文介绍如何通过DOCKER搭建STUN和TURN服务器,步骤如下

1:创建Dockerfile,内容如下:

FROM    ubuntu:14.04

MAINTAINER Patxi Gortázar<>

RUN apt-get update&& apt-get install-y\

  curl\

  libevent-core-2.0-5\

  libevent-extra-2.0-5\

  libevent-openssl-2.0-5\

  libevent-pthreads-2.0-5\

  libhiredis0.10\

  libmysqlclient18\

  libpq5\

  tel\

  wget

RUN wget ;

 && tar xzf turnserver-4.4.2.2-debian-wheezy-ubuntu-mint-x86-64bits.tar.gz\

 && dpkg-i coturn_4.4.2.2-1_amd64.deb

COPY./turnserver.sh/turnserver.sh

ENV TURN_USERNAME test

ENV TURN_PASSWORD test

ENV REALM kurento.

ENV NAT true

EXPOSE 3478 3478/udp

ENTRYPOINT ["/turnserver.sh"]

2:创建turnserver.sh,内容如下

#!/bin/bash

set-e

if[$NAT="true"-a-z"$EXTERNAL_IP"];then

#Try to get public IP

PUBLIC_IP=$(curl )||echo"No public ip found on "

if[-z"$PUBLIC_IP"];then

PUBLIC_IP=$(curl )||exit1

fi

#Try to get private IP

PRIVATE_IP=$(ifconfig|awk'/i addr/{print substr($2,6)}'|grep-v 127.0.0.1)||exit1

exportEXTERNAL_IP="$PUBLIC_IP/$PRIVATE_IP"

echo"Starting turn server with external IP:$EXTERNAL_IP"

fi

echo'min-port=49152'>/etc/turnserver.conf

echo'max-port=65535'>>/etc/turnserver.conf

echo'fingerprint'>>/etc/turnserver.conf

echo'lt-cred-mech'>>/etc/turnserver.conf

echo"realm=$REALM">>/etc/turnserver.conf

echo'log-file stdout'>>/etc/turnserver.conf

echo"user=$TURN_USERNAME:$TURN_PASSWORD">>/etc/turnserver.conf

[$NAT="true"]&&echo"external-ip=$EXTERNAL_IP">>/etc/turnserver.conf

exec/usr/bin/turnserver"$@"

3:使用docker build创建镜像,执行结果如下

[root@]# docker build-t teststurn_1.

Sending build context to Docker daemon  4.096kB

Step 1/11: FROM    ubuntu:14.04

---> 6e4f1fe62ff1

Step 2/11: MAINTAINER Patxi Gortázar<>

---> Using cache

---> 4460f9f84053

Step 3/11: RUN apt-get update&& apt-get install-y  curl  libevent-core-2.0-5  libevent-extra-2.0-5  libevent-openssl-2.0-5  libevent-pthreads-2.0-5  libhiredis0.10  libmysqlclient18  libpq5  tel  wget

---> Using cache

---> 05ed9ced48a5

Step 4/11: RUN wget tar xzf turnserver-4.4.2.2-debian-wheezy-ubuntu-mint-x86-64bits.tar.gz && dpkg-i coturn_4.4.2.2-1_amd64.deb

---> Using cache

---> d82ed28fdac9

Step 5/11: COPY./turnserver.sh/turnserver.sh

---> Using cache

---> 1d37a488282c

Step 6/11: ENV TURN_USERNAME test

---> Running in bfd88f08db42

Removing intermediate container bfd88f08db42

---> cf8af0504b95

Step 7/11: ENV TURN_PASSWORD test

---> Running in b8ef33b7c213

Removing intermediate container b8ef33b7c213

---> 32a832f23169

Step 8/11: ENV REALM kurento.

---> Running in bbe129edf5b3

Removing intermediate container bbe129edf5b3

---> 21fdfe34689b

Step 9/11: ENV NAT true

---> Running in 5bdfe8555d5e

Removing intermediate container 5bdfe8555d5e

---> dc7fc896841c

Step 10/11: EXPOSE 3478 3478/udp

---> Running in 67aaa1966f68

Removing intermediate container 67aaa1966f68

---> a12646ed45ff

Step 11/11: ENTRYPOINT ["/turnserver.sh"]

---> Running in b8fc2ff09265

Removing intermediate container b8fc2ff09265

---> f5e5acad0f81

Suessfully built f5e5acad0f81

Suessfully tagged teststurn_1:latest

执行完后可以看到自己创建的镜像名称为teststurn_1 

4:启动docker的镜像,并开启端口3478

    docker run-d-p 3478:3478-p 3478:3478/udp teststurn_1

  启动后需要等待一两分钟才能测试顺畅

5:测试服务器效果

  打开 ;并输入自己的本机IP和端口,分别测试两种协议服务是否生效

三、webrtc P2P之turn协议介绍

TURN的全称为Tr*ersal Using Relays around NAT,是STUN/RFC5389的一个拓展,主要添加了Relay功能。如果终端在NAT之后,那么在特定的情景下,有可能使得终端无法和其对等端(peer)进行的通信,这时就需要公网的服务器作为一个中继,对来往的数据进行转发。这个转发的协议就被定义为TURN。TURN和其他中继协议的不同之处在于,它允许客户端使用同一个中继(relay address)与多个不同的peer进行通信。

使用TURN协议的客户端必须能够通过中继和对等端进行通讯,并且能够得知每个peer的的IP和端口(确切地说,应该是peer的服务器反射)。而这些行为如何完成,是不在TURN协议范围之内的。其中一个可用的方式是客户端通过email来告知对等端信息,另一种方式是客户端使用一些指定的协议,如“introduction”或“rendezvous”,详见RFC5128

如果TURN使用于ICE协议中,relay会作为一个候选,由ICE在多个候选中进行评估,选取最合适的通讯。一般来说中继的优先级都是最低的。TURN协议被设计为ICE协议(Interactive Connectivity Establishment)的一部分,而且也强烈建议用户在他们的程序里使用ICE,但是也可以独立于ICE的运行。值得一提的是,TURN协议本身是STUN的一个拓展,因此绝大部分TURN报文都是STUN类型的,作为STUN的一个拓展,TURN增加了新的方法(method)和属*(attribute)。

在典型的情况下,TURN客户端连接到内网中,并且通过一个或者多个NAT到达公网,TURN服务器架设在公网中,不同的客户端以TURN服务器为中继和其他peer进行通信,如下图所示:

在上图中,左边的TURN Client是位于NAT后面的一个客户端(内网是10.1.1.2:49721),连接公网的TURN服务器(默认端口3478)后,服务器会得到一个Client的反射(Reflexive Transport Address,即NAT分配的公网IP和端口)192.0.2.1:7000,此时Client会通过TURN命令创建或管理ALLOCATION,allocation是服务器上的一个数据结构,包含了中继的信息。服务器随后会给Client分配一个中继,即图中的192.0.2.15:50000,另外两个对等端若要通过TURN协议和Client进行通信,可以往中继收发数据即可,TURN服务器会把发往指定中继的数据转发到对应的Client,这里是其反射。

Server上的每一个allocation都唯一对应一个client,并且只有一个中继,因此当数据包到达某个中继时,服务器总是知道应该将其转发到什么地方。但值得一提的是,一个Client可能在同一时间在一个Server上会有多个allocation,这和上述规则是并不矛盾的。

在协议中,TURN服务器与peer之间的连接都是基于UDP的,但是服务器和客户端之间可以通过其他各种连接来传输STUN报文,比如TCP/UDP/TLS-over-TCP.客户端之间通过中继传输数据时候,如果用了TCP,也会在服务端转换为UDP,因此建议客户端使用

UDP来进行传输.至于为什么要支持TCP,那是因为一部分防火墙会完全阻挡UDP数据,而对于三次握手的TCP数据则不做隔离.

要在服务器端获得一个中继分配,客户端须使用分配事务.客户端发送分配请求(Allocate request)到服务器,然后服务器返回分配成功响应,并包含了分配的.客户端可以在属*字段描述其想要的分配类型(比如生命周期).由于中继数据实现了安全传输,服务器会要求对客户端进行验证,主要使用STUN的long-term credential mechanism.

一旦中继传输分配好,客户端必须要将其保活.通常的方法是发送刷新请求(Refresh request)到服务端.这在TURN中是一个标准的方法.刷新频率取决于分配的生命期,默认为10分钟.客户端也可以在刷新请求里指定一个更长的生命期,而服务器会返回一个实际上分配的时间.当客户端想中指通信时,可以发送一个生命期为0的刷新请求.

服务器和客户端都保存有一个成为五元组(5-TUPLE)的信息,比如对于客户端来说,五元组包括客户端本地/端口,服务器/端口,和传输协议;服务器也是类似,只不过将客户端的变为其反射,因为那才是服务器所见到的.服务器和客户端在分配

请求中都带有5-TUPLE信息,并且也在接下来的信息传输中使用,因此彼此都知道哪一次分配对应哪一次传输.

如上图所示,客户端首先发送Allocate请求,但是没带验证信息,因此STUN服务器会返回error response,客户端收到错误后加上所需的验证信息再次请求,才能进行成功的分配.

client和peer之间有两种方法通过TURN server*应用信息,第一种是使用Send和Data方法(method),第二种是使用通道(channels),两种方法都通过某种方式告知服务器哪个peer应该接收数据,以及服务器告知client数据来自哪个peer.

Send Mechanism使用了Send和Data指令(Indication).其中Send指令用来把数据从client发送到server,而Data指令用来把数据从server发送到client.当使用Send指令时,客户端发送一个Send Indication到服务端,其中包含:

当服务器收到Send Indication之后,会将DATA部分的数据解析出来,并将其以UDP的格式转发到对应的端点去,并且在封装数据包的时候把client的中继作为源.从而从对等端发送到中继的数据也会被服务器转发到client上.值得一提的是,Send/Data Indication是不支持验证的,因为长效验证机制不支持对indication的验证,因此为了防止攻击,TURN要求client在给对等端发送indication之前先安装一个到对等端的许可(permission),如下图所示,client到Peer B没有安装许可,导致其indication数据包将被服务器丢弃,对于peer B也是同样:

TURN支持两种方式来创建许可,一种是发送CreatePermission request

对于一些应用程序,比如VOIP(Voice over IP),在Send/Data Indication中多加的36字节格式信息会加重客户端和服务端之间的带宽压力.为改善这种情况,TURN提供了第二种方法来让client和peer交互数据.该方法使用另一种数据包格式,即ChannelData message,信道数据报文. ChannelData message不使用STUN头部,而使用一个4字节的头部,包含了一个称之为信道号的值(channel number).每一个使用中的信道号都与一个特定的peer绑定,即作为对等端的一个记号.

要将一个信道与对等端绑定,客户端首先发送一个信道绑定请求(ChannelBind Request)到服务器,并且指定一个未绑定的信道号以及对等端的信息.绑定后client和server都能通过ChannelData message来发送和转发数据.信道绑定默认持续10分钟,并且可以通过重新发送ChannelBind Request来刷新持续时间.和Allocation不同的是,并没有删除绑定的方法,只能等待其超时自动失效.

上图中0x1为信道号,即ChannelData message的头部中头2字节,值得一提的是信道号的选取有如下要求:

在上一章也提到过,因为RFC是标准协议,因此实现上往往有良好的兼容*和拓展*.现存的开源P2P应用程序,如果按照标准来设计,可以很容易与之对接.其中比较著名的就是PJSIP,PJSIP是一个开源的多媒体通信库,实现了许多标准协议,如SIP, SDP, RTP, STUN, TURN和 ICE.当然我们也能自己实现.比如GitHub上的TurnServer就是其中一个对TURN服务端的实现.下面在局域网环境下对TURN数据包进行简要分析.首先有如下机器情况:

这里使用wireshark来抓*析,首先TurnClient发送Allocation请求:

可以看到第一次requst被服务器拒绝,因为后者要求nonce验证信息,服务器的返回中包含了nonce信息,除此之外还包含了ERROR-CODE,SOFTWARE,FINGERPRINT属*.

在下一次request请求中,客户端加上了收到的nonce,以及USERNAME和REALM等属*,再次发送到TurnServer:

服务器如果通过验证,就会返回suess response,随后Client可以通过上文说到的两种方法与Peer进行通讯,比如下面的Send indication方法: