目录
一. Sipdroid的请求超时和重传
二. SIP中超时和重传的定义
三. RFC中超时和重传的定义
一. Sipdroid的请求超时和重传
Sipdroid实现SIP协议栈系列, 之前的文章仅涉及了SIP消息的基本概念, 比如:
- 请求型消息: INVITE, REGISTER...
- 应答型消息: 100 Trying, 180 Ringing, 200 OK, BYE, ACK...
- 携带SDP信息
- 携带认证信息
这篇文章更深入一些, 介绍了SIP作为一种可靠传输, 涉及到的超时和重传机制. 也是在调试bug时发现的新大陆.
1.1 出现的问题
注册在同一个SIP Server的两个SIP Client互相呼叫时, 会话建立前的SIP信令一切正常, 双发也开始响铃, 但是被叫5s内不接听或拒接, 主叫就会发送CANCEL, 被叫就会回复100 Trying + 487 + 200 OK, 然后会话中断. 也就是响铃时间只有5s, 主叫就取消了呼叫.
1.2 初步推测
- 被叫回复的100 Trying和180 Ringing都没有收到?
- 服务器转发的被叫回复的100 Trying和180 Ringing都没有收到?
- 主叫的代码里是否有修改响铃时间的参数?
- 主叫本地有什么超时CANCEL的机制被魔法般地启动了?
1.3 解决方法
从主叫对象ua开始追踪基类: UA->InviteDialog->InviteTransactionClient, 发现在发送各类请求msg前有一个线程类InnerTimer对象的启动, 这个线程的休眠时间, 就是响铃时间.
1.4 问题原因
Sipdroid的请求超时参数设置错误. RFC推荐的32000被设置成5000, 导致INVITE请求在5s内没有收到200 OK回执, 就被视为超时, 主叫主动CANCEL了呼叫请求. 要修改的代码在SipStack类里, 该类描述了SIP协议栈的一些基本属性, 包括SIP默认使用端口, 默认传输协议, 默认超时时间等. 修改参数如下:
/** * starting retransmission timeout (milliseconds); called T1 in RFC2361; * they suggest T1=500ms */ public static long retransmission_timeout = 2000; /** * maximum retransmission timeout (milliseconds); called T2 in RFC2361; they * suggest T2=4sec */ public static long max_retransmission_timeout = 16000; /** transaction timeout (milliseconds); RFC2361 suggests 64*T1=32000ms */ //public static long transaction_timeout = 5000;[CHG]这里应该修改为RFC推荐值 public static long transaction_timeout = 32000; /** clearing timeout (milliseconds); T4 in RFC2361; they suggest T4=5sec */ public static long clearing_timeout = 5000;
一直知道TCP协议里有重传算法, 确保了传输可靠性, 而且是应用层基本无法质疑的可靠.
在有超时机制的msg发送前, 初始化类对象, 它们一般会被命名为transaction
class InnerTimer extends Thread { long timeout; InnerTimerListener listener; public InnerTimer(long timeout, InnerTimerListener listener) { this.timeout = timeout; this.listener = listener; start(); } public void run() { if (listener != null) { try { Thread.sleep(timeout); listener.onInnerTimeout(); } catch (Exception e) { e.printStackTrace(); } listener = null; } } }
class InnerTimerST extends java.util.TimerTask { static java.util.Timer single_timer = new java.util.Timer(true); // long timeout; InnerTimerListener listener; public InnerTimerST(long timeout, InnerTimerListener listener) { // this.timeout=timeout; this.listener = listener; single_timer.schedule(this, timeout); } public void run() { if (listener != null) { listener.onInnerTimeout(); listener = null; } } }
参考
[1] [SIP协议]学习初学笔记