2 htomarthas HtOmArthas 于 2016.04.12 21:29 提问

eXosip创建会话过程中接受到的事件的tid问题

刚开始学习eXosip,编译了网上一个大牛的代码,发现了一个问题。当UAC发送INVITE后,UAS收到后回发一个Ringing,他们的tid都为一,但是如果我把UAS关闭后再打开,UAC发送INVITE,UAS的tid为1,但是UAC收到的tid是2,这是为什么呢?
UAS代码:

include

include

include

include

int main (int argc, char *argv[])
{
eXosip_event_t *je = NULL;
osip_message_t *ack = NULL;
osip_message_t *invite = NULL;
osip_message_t *answer = NULL;
sdp_message_t *remote_sdp = NULL;
int call_id, dialog_id;
int i,j;
int id;
char *sour_call = "sip:140@127.0.0.1";
char *dest_call = "sip:133@127.0.0.1:15060";//client ip
char command;
char tmp[4096];
int pos = 0;
//初始化sip
i = eXosip_init ();
if (i != 0)
{
printf ("Can't initialize eXosip!\n");
return -1;
}
else
{
printf ("eXosip_init successfully!\n");
}
i = eXosip_listen_addr (IPPROTO_UDP, NULL, 15061, AF_INET, 0);
if (i != 0)
{
eXosip_quit ();
fprintf (stderr, "eXosip_listen_addr error!\nCouldn't initialize transport layer!\n");
}
for(;;)
{
//侦听是否有消息到来
je = eXosip_event_wait (0,50);
//协议栈带有此语句,具体作用未知
eXosip_lock ();
eXosip_default_action (je);
//printf("call_id is %d,dialog_id is %d \n", je->cid, je->did);
eXosip_automatic_refresh ();
eXosip_unlock ();
if (je == NULL)//没有接收到消息
continue;
// printf ("the cid is %s, did is %s/n", je->did, je->cid);
switch (je->type)
{
case EXOSIP_MESSAGE_NEW://新的消息到来
printf (" EXOSIP_MESSAGE_NEW!\n");
if (MSG_IS_MESSAGE (je->request))//如果接受到的消息类型是MESSAGE
{
{
osip_body_t *body;
osip_message_get_body (je->request, 0, &body);
printf ("I get the msg is: %s\n", body->body);
printf ("the cid is %s, did is %s/n", je->did, je->cid);
}
//按照规则,需要回复OK信息
eXosip_message_build_answer (je->tid, 200,&answer);
eXosip_message_send_answer (je->tid, 200,answer);
}
break;
case EXOSIP_CALL_INVITE:
//得到接收到消息的具体信息
printf ("Received a INVITE msg from %s:%s, UserName is %s, password is %s\n",je->request->req_uri->host,
je->request->req_uri->port, je->request->req_uri->username, je->request->req_uri->password);
//得到消息体,认为该消息就是SDP格式.
remote_sdp = eXosip_get_remote_sdp (je->did);
call_id = je->cid;
dialog_id = je->did;
printf("call_id is %d,dialog_id is %d \n", je->cid, je->did);

eXosip_lock ();
eXosip_call_send_answer (je->tid, 180, NULL);
printf("je->tid is %d \n",je->tid);
i = eXosip_call_build_answer (je->tid, 200, &answer);
printf("je->tid is %d \n", je->tid);
if (i != 0)
{
printf ("This request msg is invalid!Cann't response!\n");
eXosip_call_send_answer (je->tid, 400, NULL);
}
else
{
_snprintf_s (tmp, 4096,
"v=0\r\n"
"o=anonymous 0 0 IN IP4 0.0.0.0\r\n"
"t=1 10\r\n"
"a=username:rainfish\r\n"
"a=password:123\r\n");

  //设置回复的SDP消息体,下一步计划分析消息体
  //没有分析消息体,直接回复原来的消息,这一块做的不好。
  osip_message_set_body (answer, tmp, strlen(tmp));
  osip_message_set_content_type (answer, "application/sdp");

  eXosip_call_send_answer (je->tid, 200, answer);
  printf ("send 200 over!\n");
   }

eXosip_unlock ();

//显示出在sdp消息体中的attribute 的内容,里面计划存放我们的信息
printf ("the INFO is :\n");
while (!osip_list_eol ( &(remote_sdp->a_attributes), pos))
{
sdp_attribute_t *at;

  at = (sdp_attribute_t *) osip_list_get ( &remote_sdp->a_attributes, pos);
  printf ("%s : %s\n", at->a_att_field, at->a_att_value);//这里解释了为什么在SDP消息体中属性a里面存放必须是两列

  pos ++;
   }

break;
case EXOSIP_CALL_ACK:
printf ("ACK recieved!\n");
//printf ("the cid is %s, did is %s/n", je->did, je->cid);
break;
case EXOSIP_CALL_CLOSED:
printf ("the remote hold the session!\n");
// eXosip_call_build_ack(dialog_id, &ack);
//eXosip_call_send_ack(dialog_id, ack);
i = eXosip_call_build_answer (je->tid, 200, &answer);
if (i != 0)
{
printf ("This request msg is invalid!Cann't response!\n");
eXosip_call_send_answer (je->tid, 400, NULL);

  }

else
{
eXosip_call_send_answer (je->tid, 200, answer);
printf ("bye send 200 over!\n");
}

break;
case EXOSIP_CALL_MESSAGE_NEW://至于该类型和EXOSIP_MESSAGE_NEW的区别,源代码这么解释的
/*
// request related events within calls (except INVITE)
EXOSIP_CALL_MESSAGE_NEW, < announce new incoming request.
// response received for request outside calls
EXOSIP_MESSAGE_NEW, < announce new incoming request.
我也不是很明白,理解是:EXOSIP_CALL_MESSAGE_NEW是一个呼叫中的新的消息到来,比如ring trying都算,所以在接受到后必须判断
该消息类型,EXOSIP_MESSAGE_NEW而是表示不是呼叫内的消息到来。
该解释有不妥地方,仅供参考。
*/
printf(" EXOSIP_CALL_MESSAGE_NEW\n");
if (MSG_IS_INFO(je->request) ) //如果传输的是INFO方法
{
eXosip_lock ();
i = eXosip_call_build_answer (je->tid, 200, &answer);
if (i == 0)
{
eXosip_call_send_answer (je->tid, 200, answer);
}
eXosip_unlock ();
{
osip_body_t *body;
osip_message_get_body (je->request, 0, &body);
printf ("the body is %s\n", body->body);
}
}
break;
default:
printf ("Could not parse the msg!\n");
}
}
}
UAC的代码:
#include
#include
#include
#include

int main(int argc, char *argv[])
{

struct eXosip_t *context_eXosip;

eXosip_event_t *je;
osip_message_t *reg = NULL;
osip_message_t *invite = NULL;
osip_message_t *ack = NULL;
osip_message_t *info = NULL;
osip_message_t *message = NULL;

int call_id, dialog_id;
int i, flag;
int flag1 = 1;

char *source_call = "sip:140@127.0.0.1";//UAC1,端口是15060
char *dest_call = "sip:133@127.0.0.1:15061";//UAS,端口是15061
//在这个例子中,uac和uas通信,则source就是自己的地址,而目的地址就是uac1的地址
char command;
char tmp[4096];

printf("i   发起呼叫请求\n\n");
printf("h   挂断\n\n");
printf("q   退出程序\n\n");
printf("s   执行方法INFO\n\n");
printf("m   执行方法MESSAGE\n\n");

//初始化
i = eXosip_init();

if (i != 0)
{
    printf("Couldn't initialize eXosip!\n");
    return -1;
}
else
{
    printf("eXosip_init successfully!\n");
}

//绑定uac自己的端口15060,并进行端口监听
i = eXosip_listen_addr(IPPROTO_UDP, NULL, 15060, AF_INET, 0);
if (i != 0)
{
    eXosip_quit();
    fprintf(stderr, "Couldn't initialize transport layer!\n");
    return -1;
}
flag = 1;

while (flag)
{
    //输入命令
    printf("Please input the command:\n");
    scanf_s("%c", &command,1);
    getchar();

    switch (command)
    {
    case 'i'://INVITE,发起呼叫请求
        i = eXosip_call_build_initial_invite(&invite, dest_call, source_call, NULL, "This is a call for conversation");

        if (i != 0)
        {
            printf("Initial INVITE failed!\n");
            break;
        }
        //格式vot必不可少,原因未知,估计是协议栈在传输时需要检查的
        _snprintf_s(tmp, 4096,
            "v=0\r\n"
            "o=anonymous 0 0 IN IP4 0.0.0.0\r\n"
            "t=1 10\r\n"
            "a=username:htc\r\n"
            "a=password:070011\r\n");

        osip_message_set_body(invite, tmp, strlen(tmp));
        osip_message_set_content_type(invite, "application/sdp");

        eXosip_lock();
        i = eXosip_call_send_initial_invite(invite); //invite SIP INVITE message to send
        eXosip_unlock();

        //发送了INVITE消息,等待应答
        flag1 = 1;
        while (flag1)
        {
            je = eXosip_event_wait(0, 200); //Wait for an eXosip event
            //(超时时间秒,超时时间毫秒) 
            printf("je->tid is  %d \n", je->tid);
            if (je == NULL)
            {
                printf("No response or the time is over!\n");
                break;
            }
            switch (je->type)   //可能会到来的事件类型
            {
            case EXOSIP_CALL_PROCEEDING: //收到100 trying消息,表示请求正在处理中
                printf("proceeding!\n");
                break;
            case EXOSIP_CALL_RINGING:   //收到180 Ringing应答,表示接收到INVITE请求的UAS正在向被叫用户振铃
                printf("ringing!\n");
                printf("je->tid is  %d \n", je->tid);
                printf("call_id is %d,dialog_id is %d \n", je->cid, je->did);
                break;
            case EXOSIP_CALL_ANSWERED: //收到200 OK,表示请求已经被成功接受,用户应答
                printf("ok!connected!\n");
                call_id = je->cid;
                dialog_id = je->did;
                printf("call_id is %d,dialog_id is %d \n", je->cid, je->did);

                //回送ack应答消息
                eXosip_call_build_ack(je->did, &ack);
                eXosip_call_send_ack(je->did, ack);
                flag1 = 0; //推出While循环
                break;
            case EXOSIP_CALL_CLOSED: //a BYE was received for this call
                printf("the other sid closed!\n");
                break;
            case EXOSIP_CALL_ACK: //ACK received for 200ok to INVITE 
                printf("ACK received!\n");
                break;
            default: //收到其他应答
                printf("other response!\n");
                break;
            }
            eXosip_event_free(je); //Free ressource in an eXosip event
        }
        break;

    case 'h':   //挂断
        printf("Holded!\n");

        eXosip_lock();
        eXosip_call_terminate(call_id, dialog_id);
        eXosip_unlock();
        break;

    case 's': //传输INFO方法
        eXosip_call_build_info(dialog_id, &info);
        _snprintf_s(tmp, 4096, "\nThis is a sip message(Method:INFO)");
        osip_message_set_body(info, tmp, strlen(tmp));
        //格式可以任意设定,text/plain代表文本信息;
        osip_message_set_content_type(info, "text/plain");
        eXosip_call_send_request(dialog_id, info);
        break;

    case 'm':
        //传输MESSAGE方法,也就是即时消息,和INFO方法相比,我认为主要区别是:
        //MESSAGE不用建立连接,直接传输信息,而INFO消息必须在建立INVITE的基础上传输
        printf("the method : MESSAGE\n");
        eXosip_message_build_request(&message, "MESSAGE", dest_call, source_call, NULL);
        //内容,方法,      to       ,from      ,route
        _snprintf_s(tmp, 4096, "This is a sip message(Method:MESSAGE)");
        osip_message_set_body(message, tmp, strlen(tmp));
        //假设格式是xml
        osip_message_set_content_type(message, "text/xml");
        eXosip_message_send_request(message);
        break;

    case 'q':
        eXosip_quit();
        printf("Exit the setup!\n");
        flag = 0;
        break;
    }
}

return(0);

}

2个回答

never715
never715   2016.05.19 15:02
已采纳

呼叫过程未结束时异常断开情况下,心跳和会话保活机制都保证不了100%的双向会话状态都是OK的。
我以前做的时候注册注销这块也出现过类似的问题,当时是比较两次收到的请求头域中的callid是不是一样的,
不一样代表是两个transaction,就得告诉底层将之前异常的transaction销毁。保证网路两端的回话是一一对应的

never715
never715 回复HtOmArthas: 客气~
大约一年之前 回复
HtOmArthas
HtOmArthas 谢谢您啊!
大约一年之前 回复
CSDNXIAOD
CSDNXIAOD   2016.04.12 21:32

Timer事件过程中的一个问题
----------------------biu~biu~biu~~~在下问答机器人小D,这是我依靠自己的聪明才智给出的答案,如果不正确,你来咬我啊!

Csdn user default icon
上传中...
上传图片
插入图片
准确详细的回答,更有利于被提问者采纳,从而获得C币。复制、灌水、广告等回答会被删除,是时候展现真正的技术了!