放牧代码和思想
专注自然语言处理、机器学习算法
    This thing called love. Know I would've. Thrown it all away. Wouldn't hesitate.

实战rfc5766-turn-server和ice4j广域网通讯

目录

前段时间上手了NAT打洞类库ice4j(ICE框架),当时使用了numb.viagenie.ca的公共STUN服务器。最近又编译了rfc5766-turn-server,于是今天将两者结合起来,一个作为服务端,一个作为Peer端的协议,试验广域网穿透多级路由即时点对点通讯,并取得了成功。

服务端

编译安装

rfc5766-turn-server是谷歌推荐的turn开源项目,经常作WebRTC的服务器端使用。关于rfc5766-turn-server在Windows或Ubuntu(Linux)下的编译,请参考 http://www.hankcs.com/program/network/compile-rfc5766-turn-server-to-build-turn-server.html 。这里假定你已经编译完成,输入

$ turnserver -h

得到如下结果说明编译成功:

配置

rfc5766-turn-server是利用配置文件来定义自己的功能表现的,安装完成后,在下列路径都有默认的配置文件:

/usr/local/etc/turnserver.conf.default
rfc5766-turn-server检出目录/examples/etc/turnserver.conf

随便cp一个出来就能用,如果你实在找不到,可以从我这里下载一个turnserver-conf.zip

配置项很多,但是初级用户用得上的只有两个。

配置外网IP

在配置文件中加入一句

external-ip=180.160.188.246

后面的IP是你的服务器的外网IP,比如:

我的试验条件比较简陋,没有自己的服务器,而是把自己的PC当作了服务器使用。又由于自己的PC是位于局域网中,没有自己的外网IP,所以需要在路由器上做一个端口映射:

其中,192.168.1.103是服务器的内网IP,3478是turnserver服务占用的端口。

配置用户名和密码

在产品级的场景中,rfc5766-turn-server支持数据库和动态增删用户。而在我的这种实验中,我选择静态定义两个用户,在配置文件中加入:

user=u1:p1
user=u2:p2

这代表两个用户,遵从 用户名:密码 的格式。

启动rfc5766-turn-server

sudo turnserver -c /你的路径/turnserver.conf

此时会输出:

0: log file opened: /var/log/turn_2409_2014-11-10.log
0: 
RFC 3489/5389/5766/5780/6062/6156 STUN/TURN Server
Version Citrix-3.2.4.6 'Marshal West'
0: 
Max number of open files/sockets allowed for this process: 4096
0: 
Due to the open files/sockets limitation,
max supported number of TURN Sessions possible is: 2000 (approximately)
0: 

==== Show him the instruments, Practical Frost: ====

0: TLS supported
0: DTLS supported
0: Redis supported
0: PostgreSQL supported
0: MySQL supported
0: OpenSSL compile-time version 0x1000106f: fresh enough
0: Default Net Engine version: 3 (UDP thread per CPU core)

=====================================================

0: WARNING: Cannot find userdb file: turnuserdb.conf: going without flat file user database.
0: WARNING: cannot find certificate file: turn_server_cert.pem (1)
0: WARNING: cannot start TLS and DTLS listeners because certificate file is not set properly
0: WARNING: cannot find private key file: turn_server_pkey.pem (1)
0: WARNING: cannot start TLS and DTLS listeners because private key file is not set properly
0: NO EXPLICIT LISTENER ADDRESS(ES) ARE CONFIGURED
0: ===========Discovering listener addresses: =========
0: Listener address to use: 127.0.0.1
0: Listener address to use: 192.168.1.103
0: Listener address to use: ::1
0: =====================================================
0: Total: 1 'real' addresses discovered
0: =====================================================
0: NO EXPLICIT RELAY ADDRESS(ES) ARE CONFIGURED
0: ===========Discovering relay addresses: =============
0: Relay address to use: 192.168.1.103
0: Relay address to use: ::1
0: =====================================================
0: Total: 2 relay addresses discovered
0: =====================================================
0: pid file created: /var/run/turnserver.pid
0: IO method (main listener thread): epoll (with changelist)
0: WARNING: I cannot support STUN CHANGE_REQUEST functionality because only one IP address is provided
0: Wait for relay ports initialization...
0:   relay 192.168.1.103 initialization...
0:   relay 192.168.1.103 initialization done
0:   relay ::1 initialization...
0:   relay ::1 initialization done
0: Relay ports initialization done
0: IO method (general relay thread): epoll (with changelist)
0: turn server id=0 created
0: IPv4. TCP listener opened on : 127.0.0.1:3478
0: IPv4. TCP listener opened on : 192.168.1.103:3478
0: IPv6. TCP listener opened on : ::1:3478
0: IO method (general relay thread): epoll (with changelist)
0: turn server id=1 created
0: IPv4. TCP listener opened on : 127.0.0.1:3478
0: IPv4. TCP listener opened on : 192.168.1.103:3478
0: IPv6. TCP listener opened on : ::1:3478
0: IPv4. UDP listener opened on: 127.0.0.1:3478
0: IPv4. UDP listener opened on: 192.168.1.103:3478
0: IPv6. UDP listener opened on: ::1:3478
0: Total UDP servers: 0
0: Total General servers: 2
0: IO method (auth thread): epoll (with changelist)
0: IO method (cli thread): epoll (with changelist)
0: IPv4. CLI listener opened on : 127.0.0.1:5766

这样就成功启动了。

Peer端

检出代码

我已将全部代码开源到https://github.com/hankcs/IceNAT ,需要读者添加ice4j的依赖项,并参考如下步骤进行试验。

修改配置项

Peer端的基础知识请参考我写的《试验UDP打洞穿透NAT》,这次我不再使用别人的服务器,编辑IceClient,修改turnServers和stunServers的地址和密码为:

    private String[] turnServers = new String[]{"180.160.188.246:3478"};

    private String[] stunServers = new String[]{"180.160.188.246:3478"};

    private String username = "u1";

    private String password = "p1";

编译一份,备用,我称它为Peer1。Pee1运行于我宿舍的工作站上,外网IP和服务器相同(位于同一局域网),都是:

然后将用户名和密码修改为:

    private String username = "u2";

    private String password = "p2";

拷贝到另一台电脑上去作为Peer2,其中Peer2的外网IP为:

这是校园网的外网IP,全校上万台PC分布在层层路由和防火墙之下,我的另一台PC也是其中一台,我的目标就是穿透障碍,达到广域网通讯的目的。

开始试验

获取信令

Peer1和Peer2运行,分别通过rfc5766-turn-server获取到了自己的SDP信息(信令):

Peer1——

v=0
o=ice4j.org 0 0 IN IP4 180.160.188.246
s=-
t=0 0
a=ice-options:trickle
a=ice-ufrag:2mu8s196cnbrvi
a=ice-pwd:3rapigabe9fl3b1949nb1c9637
m=text 59229 RTP/AVP 0
c=IN 180.160.188.246 IP4
a=mid:text
a=candidate:1 1 udp 2130706431 192.168.1.100 8888 typ host
a=candidate:2 1 udp 2130706431 fe80:0:0:0:38f0:a64b:a7a6:e8b6 8888 typ host
a=candidate:4 1 udp 2113937151 fe80:0:0:0:2891:b74a:6050:22b3 8888 typ host
a=candidate:3 1 udp 2113932031 192.168.124.1 8888 typ host
a=candidate:5 1 udp 1677724415 180.160.188.246 11252 typ srflx raddr 192.168.1.100 rport 8888
a=candidate:6 1 udp 2815 180.160.188.246 59229 typ relay raddr 180.160.188.246 rport 11252

Peer2——

v=0
o=ice4j.org 0 0 IN IP4 180.160.188.246
s=-
t=0 0
a=ice-options:trickle
a=ice-ufrag:8q3b1196cnip55
a=ice-pwd:7ua73q9prlqn1jbal7143fprsd
m=text 52430 RTP/AVP 0
c=IN 180.160.188.246 IP4
a=mid:text
a=candidate:1 1 udp 2130706431 10.2.203.96 6666 typ host
a=candidate:2 1 udp 2130706431 fe80:0:0:0:448b:e2e1:7f4b:f0b8 6666 typ host
a=candidate:3 1 udp 1677724415 58.32.217.55 6666 typ srflx raddr 10.2.203.96 rport 6666
a=candidate:4 1 udp 2815 180.160.188.246 52430 typ relay raddr 58.32.217.55 rport 6666

同时可以在rfc5766-turn-server的终端看到输出:

7: handle_udp_packet: New UDP endpoint: local addr 192.168.1.103:3478, remote addr 180.160.188.246:10898
7: session 000000000000000001: user <>: incoming packet BINDING processed, success
7: session 000000000000000001: user <>: incoming packet message processed, error 401: Unauthorised
7: IPv4. Local relay addr: 192.168.1.103:54837
7: session 000000000000000001: new, username=<u1>, lifetime=600
7: session 000000000000000001: user <u1>: incoming packet ALLOCATE processed, success

交换信令

由于我还没有实现自己的信令服务器(SIP),所以通过类似电子邮件复制粘贴的方式交换了两者的信令。

交换后,两个Peer开始配对,配对过程的输出请参考交换信息配,这里不再赘述。

聊天

之后两者就可以聊天了:

Peer1

/192.168.1.103:52430 says: hello, i am from hello
i get it,hi
/192.168.1.103:52430 says: 试试中文
没问题
/192.168.1.103:52430 says: 我在校园网,似乎不能走p2p,幸亏有turn服务器
对,你的ip是turn服务器的ip,也就是说我这条消息是从turn服务器那里获取的
/192.168.1.103:52430 says: 好吧,这样也算广域网通讯成功了
行,就这样,再见
/192.168.1.103:52430 says: bye

Peer2

hello, i am from hello
/192.168.1.100:8888 says: i get it,hi
试试中文
/192.168.1.100:8888 says: 没问题
我在校园网,似乎不能走p2p,幸亏有turn服务器
/192.168.1.100:8888 says: 对,你的ip是turn服务器的ip,也就是说我这条消息是从turn服务器那里获取的
好吧,这样也算广域网通讯成功了
/192.168.1.100:8888 says: 行,就这样,再见
bye

可以看到虽然两者处于不同的局域网,拥有不同的外网IP,但是由于Peer1(192.168.1.100)与服务器(192.168.1.103)处于同一局域网,所以Peer1一直在用局域网和rfc5766-turn-server通讯。

而Peer2与Peer1直连失败,通过turn服务器中转数据,所以在Peer1看来,Peer2的IP是192.168.1.103,与turn服务器一摸一样。

题外话,我是有多寂寞才能跟自己聊的那么开心的?

Reference

http://blog.wnotes.net/blog/article/stun-server-on-aws-ec2

https://code.google.com/p/rfc5766-turn-server/wiki/turnserver

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » 实战rfc5766-turn-server和ice4j广域网通讯

评论 44

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. #14

    请问timeout问题解决了吗? 我的配对经常出现timeout, 不过有时候可以成功。

    7年前 (2017-03-16)回复
  2. #13

    turnserver怎么设置这个解决Disabling: STUN harvester错误

    goweather8年前 (2016-11-21)回复
  3. #12

    Disableing:STUN harvester 这个问题怎么解决,请教。

    goweather8年前 (2016-11-21)回复
  4. #11

    能否发一个java demo呢

    1092570469年前 (2015-09-20)回复
  5. #10

    你好,我在工程中导入了commons-lang3-3.3.2.jar,log4j-1.2.17.jar,ice4j.jar三个包,但SdpUtils.java这个类还是会报错,于是又导入了jain-sdp.jar然后就没错误了,不知是否合适?
    2、博主发的turnser.conf.zip文件无法下载,我的配置可能有问题,不清楚什么原因???
    我打开turnserver后,客户端提示
    八月 01, 2015 9:54:21 上午 org.ice4j.ice.harvest.CandidateHarvesterSetElement setEnabled
    信息: Disabling: STUN harvester(srvr: 172.16.28.90:3478/udp)
    如果我关掉turnserver后,提示
    八月 01, 2015 9:57:06 上午 org.ice4j.ice.harvest.CandidateHarvesterSetElement setEnabled
    信息: Disabling: STUN harvester(srvr: 172.16.28.90:3478/udp)
    八月 01, 2015 9:57:07 上午 org.ice4j.ice.harvest.CandidateHarvesterSetElement setEnabled
    信息: Disabling: TURN harvester(srvr: 172.16.28.90:3478/udp)
    3、博主说的交换两者的信令,就是把服务器端返回的sdp信息发送给对方后,对方收到后粘贴到控制台然后回车建立连接?
    新手提问请多多见谅!

    赶路人keny9年前 (2015-08-01)回复
    • 1. 有问题再说
      2. 附件丢了
      3. 对,复制粘贴

      hankcs9年前 (2015-08-01)回复
      • 好的,谢谢,第二个问题配置好turnserver解决了。

        赶路人keny9年前 (2015-08-09)回复
        • 你好 你能告诉我你是怎么配置的吗 我现在也出现了这个问题 Disableing:STUN harvester

          eageor9年前 (2015-11-22)回复
  6. #9

    LZ 我安装了opensips 我看你都是手动的复制交换SDP,如何将这些SDP直接发给opensips呢?

    liverpoolilove9年前 (2015-05-08)回复
  7. #8

    sip 你可以试试 opensips
    请问,关于turnserver 能否查看当前有多少个relay 连接吗

    test9年前 (2015-04-24)回复
    • 我安装了opensips 如何将这些SDP直接发给opensips呢?

      liverpoolilove9年前 (2015-05-08)回复
  8. #7

    你好,最近在做P2P的项目,用到stun、turn、ICE协议。场景就是实现位于两个不同局域网(不同NAT下)的两台PC直连。试了一下你上面的例子。有如下几个疑问:
    1、你通过路由器做端口映射私网地址,那个端口3478是映射的路由器的端口还是私网地址的端口?
    2、我直接用公网IP作为服务器,但是运行了你的客户端的例子(两个客户端位于两个不同的NAT下),发现能返回SDP信息,但是双方交换SDP信息后,无法配对成功,所有配对提示timeout。不知道问题出在哪。

    用户24096060659年前 (2015-03-25)回复
    • 1、在TP路由器上做的映射
      2、我也不清楚

      hankcs9年前 (2015-03-27)回复
    • 请问你的问题解决了吗 我现在也是timeout

      eageor9年前 (2015-11-22)回复
  9. #6

    import test.SdpUtils;

    这段代码没上传啊。

    huaichao9年前 (2015-03-06)回复
    • SdpUtils is an util from ice4j’s test module. Anyway, I have copied it to com.hankcs.network.SdpUtils. It should work now.

      hankcs9年前 (2015-03-08)回复
  10. #5

    您好 我最近在用https://github.com/andyet/signalmaster 搭配https://github.com/HenrikJoreteg/SimpleWebRTC 进行webrtc开发,
    但是signalmaster里面需要用到turnserver的static shared secret 进行认证。我的turnserver也搭建好了,但是一直turn中转失败。我怀疑是我的turnserver的配置有问题(我在turnserver的conf里面增加了static-auth-secret=123456),signalmaster 里面的配置为
    {
    "isDev": true,
    "logLevel": 3,
    "server": {
    "port": 8888,
    "secure": false, /* whether this connects via https */
    "key": null,
    "cert": null,
    "password": null
    },
    "stunservers" : [
    {"url": "stun:180.169.0.99:3478"}
    ],
    "turnservers" : [

    { "url": "turn:180.169.0.99:3478",
    "secret": "123456",
    "expiry": 86400 }

    ]
    }

    但是turn中转失败,您能否也配置一下然后指点一下呢?谢谢!

    灵髑_吴康宁9年前 (2015-03-04)回复
    • 对不起,最近比较忙,问问作者比较好。

      hankcs9年前 (2015-03-08)回复
  11. #4

    你好 我最近也在研究turnserver 能加下你QQ么

    常凯毅9年前 (2015-01-15)回复
    • 你好,我不用扣扣,公事博客上交流,私事微博私信,谢谢。

      hankcs9年前 (2015-01-16)回复
  12. #3

    我弄好了。可是得到的结果并不如意。

    Dec 08, 2014 5:37:05 PM org.ice4j.ice.Agent gatherCandidates
    INFO: Gather candidates for component text.RTP
    Dec 08, 2014 5:37:07 PM org.ice4j.ice.harvest.CandidateHarvesterSetElement setEnabled
    INFO: Disabling: STUN harvester(srvr: 22.12.137.128:22/udp)
    Dec 08, 2014 5:37:08 PM org.ice4j.ice.harvest.CandidateHarvesterSetElement setEnabled
    INFO: Disabling: TURN harvester(srvr: 22.12.137.128:22/udp)
    Dec 08, 2014 5:37:08 PM org.ice4j.ice.Agent createComponent
    INFO: 192.168.1.35:8888/udp (host)
    Dec 08, 2014 5:37:08 PM org.ice4j.ice.Agent createComponent
    INFO: 192.168.1.101:8888/udp (host)

    为什么stun 和 turn disable 了呢?跪求解答。

    wilson10年前 (2014-12-08)回复
    • 网络很复杂,不处在你的具体环境中,我也无从推断

      hankcs10年前 (2014-12-08)回复
      • 会不会是因为我的stun/turnserver出了问题? 因为如果我把stun的ip address 换去google the stun address的,那就没问题了。很复杂啊!

        wilson10年前 (2014-12-09)回复
        • 应该是的

          hankcs10年前 (2014-12-09)回复
          • 我怀疑是我的turnserver.conf出了问题。不知你可不可以send我你的turnserver.conf file?

            wilson10年前 (2014-12-09)
          • 文章中有一份

            hankcs10年前 (2014-12-09)
          • 找到了。跟我的一样!我只需uncomment those line, 没有 # 符号对吗?对不起,我是刚学programming

            wilson10年前 (2014-12-09)
          • 对,祝好运

            hankcs10年前 (2014-12-09)
  13. #2

    你好。我可以成功compile你的code可是不能。出现error。
    Exception in thread "main" java.lang.NoClassDefFoundError: Peer.
    不知道当你run code时会不会出现同样问题呢?

    wilson10年前 (2014-12-05)回复
    • 没有遇到,估计是你的路径问题,Peer class在开发期间存在于类路径下但在运行期间却不在类路径下

      hankcs10年前 (2014-12-05)回复
      • 不知道你可不可以qq我一整套code?我用mac terminal compile and run.
        javac -cp /jar/commonslang3-3.3.2.jar:/jar/ice4j.jar:/log4j-1.2.17.jar IceClient.java Peer.java

        wilson10年前 (2014-12-05)回复
      • 不知道你可不可以qq我一整套code?我用mac terminal compile and run.
        javac -cp /jar/commons-lang3-3.3.2.jar :/jar/ice4j.jar :/log4j-1.2.17ar IceClient.java Peer.java

        wilson10年前 (2014-12-05)回复
        • 然后compile -> java -cp /jar/commons-lang3-3.3.2.jar :/jar/ice4j.jar :/log4j-1.2.17.jar Peer.
          这就是问题出.

          wilson10年前 (2014-12-05)回复
        • 这就是全部的代码,你路径错了怎么跑都是一样,我的class都是在com.hankcs.network这个包里的,至少java -classpath .;pathtoyourlib; com/hankcs/network/Peer 才行,或者在Eclipse等IDE下编译运行

          hankcs10年前 (2014-12-05)回复
          • 我换去Netbeans.可是还是有问题。
            log4j:WARN No appenders could be found for logger (IceClient).
            log4j:WARN Please initialize the log4j system properly.
            log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

            不知你又遇到吗?那些代码完全没改过。。。

            wilson10年前 (2014-12-05)
          • log4j需要一个配置文件,你的IDE没有智能地把IceNATsrclog4j2.xml放到classpath里面去,你手动放一下就行了。这种级别的问题Google一下很多解决方案的

            hankcs10年前 (2014-12-05)
  14. #1

    Hi ~拜讀您的文章,想請教:
    是否有將rfc5766-turn-server也當成stun server使用?類似numb那樣查詢自己的公網IP,並試著探測自己的NAT型態?
    目前我以pystun這個工具自server的外網測試,無法獲取正確的結果。(相較於對numb測試)

    Galen_Marek10年前 (2014-12-04)回复
    • 当然可以,-H your-rfc5766-turn-server-ip 就可以了,rfc5766-turn-server默认开启了stun和turn两种模式。
      不过pystun可能使用的是旧的stun协议,报错incoming packet OLD STUN message processed, error 420: Unknown attribute: TURN server was configured without RFC 5780 support,可能需要开启RFC 5780 support的配置项吧

      hankcs10年前 (2014-12-04)回复
      • 這個警告訊息沒影響嗎?WARNING: I cannot support STUN CHANGE_REQUEST functionality because only one IP address is provided

        Galen_Marek10年前 (2014-12-08)回复
        • 没遇到过,我也不清楚

          hankcs10年前 (2014-12-08)回复
          • 所以您的實例,能夠以rfc-5780的方式測得client的NAT Type?

            Galen_Marek10年前 (2014-12-08)
          • 没有测到,pystun似乎在使用rfc-5780,我没有查文档怎么开启它

            hankcs10年前 (2014-12-08)
          • 好的!~感謝您的回覆!我也再嘗試著開啟它的rfc-5780 support

            Galen_Marek10年前 (2014-12-08)
          • turn 是 stun的超集, stun 能做的 turn 都能做

            test9年前 (2015-04-24)

我的作品

HanLP自然语言处理包《自然语言处理入门》