iOS linphone进行语音通话时出现以下错误:
1.拨打方 拨出电话,收到405 Method not allowed 导致无法拨出
2.通话成功后,挂断 收到501 Not implement 导致另一方无法挂断
一开始以为都是服务器发出的信令,后来发现其实是linphone自己发出的,服务器只是做了转发。
Linphone 被叫方如何解析来电SIP消息中的自定义头消息
参考以上连接可以了解一下sip消息的解析流程。
下面是问题调试流程
1.首先linphone 源码要是debug编译才能跳转到源码内容。(编译命令 ./prepare.py -c && ./prepare.py --debug && make)
2.在LinphoneManager中断点在 -(void)iterate 这个定时方法中,在控制台输入
b channel.c:592
断点在channel.c 方法中的void belle_sip_channel_parse_stream 方法中
点击传入的参数obj,可以看到obj->input_stream下buff属性为sip信令的文本展示
可以看到这个sip信令中开头出现了很多hello。所以导致linphone解析信令的时候出错了。有时信令正常,没有多余的hello,就可以正常拨出,一旦多了hello就会出现405 Method not allowed。
对于405 的信令发出,在sal.cpp line 94 ,这里对method进行了匹配处理,当method 为hellohelloINVITE的时候就会匹配不到返回405 信令。
其实对sip信令 开头有多余的字符,linphone有处理的地方,在channel.c line555通过get_message_start_pos方法计算得到偏移offset。
问题就是出现在了这个get_message_start_pos方法中。方法实现在channel.c line232
static int get_message_start_pos(char *buff, size_t bufflen) {
/*FIXME still to optimize and better test, specially REQUEST PATH and error path*/
int i;
int res=0;
int status_code;
char method[17]={0};
char saved_char1;
char sip_version[10]={0};
size_t saved_char1_index;
for(i=0; i<(int)bufflen-12;i++) { /*9=strlen( SIP/2.0\r\n)*/
switch (buff[i]) { /*to avoid this character to be ignored by scanf*/
case '\r':
case '\n':
case ' ' :
case '\t':
continue;
default:
break;
}
saved_char1_index=bufflen-1;
saved_char1=buff[saved_char1_index]; /*make sure buff is null terminated*/
buff[saved_char1_index]='\0';
res=sscanf(buff+i,"SIP/2.0 %d ",&status_code);
if (res!=1) res=sscanf(buff+i,"HTTP/1.%*i %d ",&status_code); /*might be HTTP ?*/
if (res!=1) {
res= sscanf(buff+i,"%16s %*s %9s\r\n",method,sip_version)==2
&& is_token(method,sizeof(method))
&& (strcmp("SIP/2.0",sip_version)==0 || strncmp("HTTP/1.",sip_version,strlen("HTTP/1."))==0);
}
buff[saved_char1_index]=saved_char1;
if (res==1) return i;
}
return -1;
}
在这个方法中数组method被申明了17的长度,所以当sip信令前面有很多hello的时候,method会被解析成 hellohelloINVITE。method的解析是通过c函数sscanf
res= sscanf(buff+i,"%16s %*s %9s\r\n",method,sip_version)==2
&& is_token(method,sizeof(method))
&& (strcmp("SIP/2.0",sip_version)==0 || strncmp("HTTP/1.",sip_version,strlen("HTTP/1."))==0);
最后我的解决办法是 去除掉多余的hello。代码如下
//判断是否包含某个字符串
_Bool isCoincide(char *a, char *p)
{
char *ptemp = p;
while (*a != '\0')
{
if (*a == *p)
{
a++;
p++;
}
else
{
a++;
p = ptemp;
}
if (*p == '\0')
{
return 1;
}
}
return 0;
}
static int get_message_start_pos(char *buff, size_t bufflen) {
/*FIXME still to optimize and better test, specially REQUEST PATH and error path*/
int i;
int res=0;
int status_code;
char method[17]={0};
char saved_char1;
char sip_version[10]={0};
size_t saved_char1_index;
for(i=0; i<(int)bufflen-12;i++) { /*9=strlen( SIP/2.0\r\n)*/
switch (buff[i]) { /*to avoid this character to be ignored by scanf*/
case '\r':
case '\n':
case ' ' :
case '\t':
continue;
default:
break;
}
saved_char1_index=bufflen-1;
saved_char1=buff[saved_char1_index]; /*make sure buff is null terminated*/
buff[saved_char1_index]='\0';
res=sscanf(buff+i,"SIP/2.0 %d ",&status_code);
if (res!=1) res=sscanf(buff+i,"HTTP/1.%*i %d ",&status_code); /*might be HTTP ?*/
if (res!=1) {
// res= sscanf(buff+i,"%16s %*s %9s\r\n",method,sip_version)==2
// && is_token(method,sizeof(method))
// && (strcmp("SIP/2.0",sip_version)==0 || strncmp("HTTP/1.",sip_version,strlen("HTTP/1."))==0);
res = (sscanf(buff+i,"%16s %*s %9s\r\n",method,sip_version)==2);
if (res==1) {
//去除hello
_Bool j;
j = isCoincide(method, "hello");
if(j==1){
//包含hello
belle_sip_message("sip信令包含hello 执行去除");
do{
//执行去除
sscanf(buff+i+5,"%16s %*s %9s\r\n",method,sip_version);
j=isCoincide(method, "hello");
i+=5;
}while(j>0);
}
res= sscanf(buff+i,"%16s %*s %9s\r\n",method,sip_version)==2
&& is_token(method,sizeof(method))
&& (strcmp("SIP/2.0",sip_version)==0 || strncmp("HTTP/1.",sip_version,strlen("HTTP/1."))==0);
}
}
buff[saved_char1_index]=saved_char1;
if (res==1) return i;
}
return -1;
}
新增了一个isCoincide方法判断字符串是否包含hello
然后通过sscanf多次匹配 去除hello
最后返回正确的i 即为正确的偏移量offset
重新编译后替换工程中的sdk
通过这样,解决了405 和501 的问题,后来发现除了invite、bye会出现多余的hello,有的时候ack也有多余的hello,不过通过这样都解决了。