关于这个问题我们还是按照故事来讲吧。
首先是有一个网友在QQ里私下问我问题,一般来说,我是不会私下回答任何问题的。因此,我让他到知识星球里问,后来就看到知识星球里有人问一个在NAT环境下电话不通的问题(不知道是不是跟私聊的一个人,私聊跟知识星球里对不上号,呵呵)。
问题是:如果不开视频,则经过NAT设备的通话是通的,一开视频就不通了。
看起来很奇怪,原因也可能有很多,因此,我提议他打开SIP Trace(使用sofia global siptrace on
命令)将抓包的数据放到Pastebin上。接着他问什么是Pastebin,我说先看《FreeSWITCH新手指南》,接着他问什么是《新手指南》,我说那得看http://www.freeswitch.org.cn 。是的,我建议所有提问题的人都先看新手指南。最后他还真看了。
正说到这里,群里还有个人说他也有同样的问题。这就是在群里问问题比私下问的好处,你能找到与你有同样问题的人,他们会跟你分享经验。
当日志贴到Pastebin上以后,我看了一下,客户端发了INVITE以后,FreeSWITCH回了407要求认证,这时候客户端回了ACK,然后客户端应该重新发带认证信息的INVITE。结果FreeSWITCH等了半天没有收到,因此报WRONG_CALL_STATE
错误,呼叫失败(如果有对SIP呼叫流程不清楚的读者可以看《FreeSWITCH权威指南》第7章)。
这里顺便说一下,有了这个日志我们马上就定位到问题了,所以,贴日志很重要。
出现这个问题的原因可能是客户端根本没回下一个INVITE(这不大可能,但也不是不可能),或者是路由器等NAT设备将该INVITE包拦截或丢掉了。
由于现象是音频电话通,视频电话不通。而这两种电话的区别一般是INVITE包中的SDP不同,后者消息长度比较大一些。从收到的第一个INVITE包来看,大小已有1265字节(recv 1265 bytes from udp/[10.0.10.1]:62468 at ...
),也有可能是后续的INVITE包更大而超过了MTU,被路由器分包或导致了FreeSWITCH收不到完整的INVITE包。
因此我建议他把SIP换成TCP试试,TCP是面向连接的协议,具有丢包重传机制,因而能保证IP包的完整。但他的回答是试了以后问题依旧(这个是个迷,因为没有进一步的确认)。
后来,笔者在忙别的事情,再回来看时知识星球里已经有朋友帮他解决了,说是路由器有ALG(Application Layer Gateway),他改了SIP服务端口就好了。ALG是一个看起来很美好但到处都是Bug的NAT解决方案,因此在使用FreeSWITCH的时候,我们都建议关掉它。不过,不知道该问题中的ALG为什么只对视频请求有问题,音频却没问题。总之,根本原因还不知道。但从这个问题中我们学到一些东西:
- 有问题在公开的地方(如知识星球)问,这样,会有更多的人帮助你;
- 问题所在有时跟你猜的不一样,所以,一定要让别人看到你的日志和SIP Trace;
- 仔细问题问题,多测试、对比,缩小问题的范围;
- UDP通信在有大数据包时(超过MTU时)不靠谱,试试TCP;
- 关掉SIP ALG,大多数情况下它只会帮倒忙;
- NAT问题无处不在,没有统一的解决方案。学好基础知识才能以不变应万变。
后来,又得到那位提问者的反馈。最终原因还是因为SIP包过长的原因,他最后的解决方案不是使用TCP,但是他在客户央上去掉了了几个不用的音、视频编码,把SIP包(其实是SDP的部分)减小了一点,最后问题解决了。由此,我们又学到以下三点:
- 其实这印证了我最初的猜测,根本原因是SIP包过长;
- 有时候,一个问题解决了你却不知道为什么的时候,好好回忆你都做了什么,多验证一下,找到真正的原因,才能进步;
- 向回答你问题的人反馈很重要,要不然,大家就看不到最后这一段了。
其实,最后这个问题根本不是NAT问题。