zoukankan      html  css  js  c++  java
  • eXosip、osip,以及UAC和UAS的例子

    UAC(User Agent Client) 和 UAS(User Agent Server)

    SIP协议采用Client/Server模型。每一个请求(Request)触发服务器的一个操作;每个操作被称为方法(Method);每个请求除了指明具体方法外,还携带了一系列的头域(Header field),这些头域携带了各种业务信息;除了头域外,消息中还可以携带任意类型的消息体(Body),呼叫中最常使用的SDP信息就是通过消息体在Client和Server之间传递的。Server从接收到请求到处理完毕,要回复多个临时响应(Response),和一个终结响应(Response),终结响应有且只有一个。
    请求和他的所有响应构成一个事务(Transaction),一个完整的呼叫过程包含多个事务,比如呼叫建立和呼叫释放就是
    两个相互独立的事务。
    用户代理(User Agent)是发起或者接收呼叫的逻辑实体。

    用户代理客户端-UAC(User Agent Client),用于发起请求;
    用户代理服务器-UAS(User Agent Server),用于接收请求。UAC/UAS的划分是针对一个事务的。在一个呼叫中的多个事务里,UACUAS的角色是可以互换的。例如在A和B的呼叫中,A向B发起呼叫,在呼叫建立的事务中,A是UAC,B是UAS;呼叫结束时,B先挂机,在呼叫释放的事务中,B是UAC,A是UAS。换句话说,每个一般的UA都是UASUAC的结合体。
    UA的实际物理形态有:IP Phone,SoftPhone,GateWay......
    Proxy Server作为一个网络逻辑实体代理客户端转发请求或者响应;同Proxy Server类似的还有一种设备是B2BUA,
    顾名思义,就是背背的两个UA组成的一个逻辑实体,它作为UAS终结一个事务,同时作为UAC发起另外一个事务。Proxy Server同B2BUA相比,Proxy Server是一个事务传递过程中的中间节点,而B2BUA将一个事务转变成另一个事务。
    在SIP组网中还包括Location Server、Registrar、Redirect Server,分别负责维护地址映射表,注册管理,呼叫重定向。他们和Proxy Server 可以在同一台设备上也可以运行于不同的设备上。SIP Server是Proxy Server、Location Server、Registrar、Redirect Server的总称。
    SIP Server采用B2BUA模型。接受请求端为UAS端,代理转发或主动发起请求端为UAC端,整个SIPServr为UAC/UAS的组合体。通过UAC/UAS之间消息交互完成会话的建立、改变、结束的阶段。SIP Server协助网关交换媒体信息,
    但不参与会话建立后媒体流传输。 
     
     
    代码 uac.c
    #include <eXosip2/eXosip.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdarg.h>
    #include <netinet/in.h>
    //#include <winsock2.h>
    
    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 *identity="sip:140@127.0.0.1";   //UAC1,端口是15060
        char *registar="sip:133@127.0.0.1:15061"; //UAS,端口是15061
        char *source_call="sip:140@127.0.0.1";
        char *dest_call="sip:133@127.0.0.1:15061";
        //identify和register这一组地址是和source和destination地址相同的
        //在这个例子中,uac和uas通信,则source就是自己的地址,而目的地址就是uac1的地址
        char command;
        char tmp[4096];
    
        printf("r   向服务器注册
    
    ");
        printf("c   取消注册
    
    ");
        printf("i   发起呼叫请求
    
    ");
        printf("h   挂断
    
    ");
        printf("q   推出程序
    
    ");
        printf("s   执行方法INFO
    
    ");
        printf("m   执行方法MESSAGE
    
    ");
    
        //初始化
        i=eXosip_init();
    
        if(i!=0)
        {
            printf("Couldn't initialize eXosip!
    ");
            return -1;
        }
        else
        {
            printf("eXosip_init successfully!
    ");
        }
    
        //绑定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!
    ");
            return -1;
        }
        flag=1;
    
        while(flag)
        {
            //输入命令
            printf("Please input the command:
    ");
            scanf("%c",&command);
            getchar();
    
            switch(command)
            {
            case 'r':
                printf("This modal is not completed!
    ");
                break;
            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!
    ");
                    break;
                }
                //符合SDP格式,其中属性a是自定义格式,也就是说可以存放自己的信息,
                //但是只能有两列,比如帐户信息
                //但是经过测试,格式vot必不可少,原因未知,估计是协议栈在传输时需要检查的
                snprintf(tmp,4096,
                          "v=0
    "
                          "o=anonymous 0 0 IN IP4 0.0.0.0
    "
                          "t=1 10
    "
                          "a=username:rainfish
    "
                          "a=password:123
    ");
    
                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
                    //(超时时间秒,超时时间毫秒)
                    if(je==NULL)
                    {
                        printf("No response or the time is over!
    ");
                        break;
                    }
                    switch(je->type)   //可能会到来的事件类型
                    {
                    case EXOSIP_CALL_INVITE:   //收到一个INVITE请求
                        printf("a new invite received!
    ");
                        break;
                    case EXOSIP_CALL_PROCEEDING: //收到100 trying消息,表示请求正在处理中
                        printf("proceeding!
    ");
                        break;
                    case EXOSIP_CALL_RINGING:   //收到180 Ringing应答,表示接收到INVITE请求的UAS正在向被叫用户振铃
                        printf("ringing!
    ");
                        printf("call_id is %d,dialog_id is %d 
    ",je->cid,je->did);
                        break;
                    case EXOSIP_CALL_ANSWERED: //收到200 OK,表示请求已经被成功接受,用户应答
                        printf("ok!connected!
    ");
                        call_id=je->cid;
                        dialog_id=je->did;
                        printf("call_id is %d,dialog_id is %d 
    ",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!
    ");
                        break;
                    case EXOSIP_CALL_ACK: //ACK received with 200ok to INVITE  (就是发送INVITE后,收到了对该INVITE的ACK)
                        printf("ACK received!
    ");
                        break;
                    default: //收到其他应答
                        printf("other response!
    ");
                        break;
                    }
                    eXosip_event_free(je); //Free ressource in an eXosip event
                }
                break;
    
            case 'h':   //挂断
                printf("Holded!
    ");
    
                eXosip_lock();
                eXosip_call_terminate(call_id,dialog_id);
                eXosip_unlock();
                break;
    
            case 'c':
                printf("This modal is not commpleted!
    ");
                break;
    
            case 's': //传输INFO方法
                eXosip_call_build_info(dialog_id,&info);
                snprintf(tmp,4096,"
    This 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
    ");
                eXosip_message_build_request(&message,"MESSAGE",dest_call,source_call,NULL);
                //内容,方法,      to       ,from      ,route
                snprintf(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!
    ");
                flag=0;
                break;
            }
        }
    
        return(0);
    }

    代码 uas.c

    # include <eXosip2/eXosip.h>
    # include <stdio.h>
    # include <stdlib.h>
    # include <stdarg.h>
    # include <netinet/in.h>
    
    //# include <Winsock2.h>
    
    
    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];
        char localip[128];
        int pos = 0;
        //初始化sip
        i = eXosip_init ();
        if (i != 0)
        {
            printf ("Can't initialize eXosip!
    ");
            return -1;
        }
        else
        {
            printf ("eXosip_init successfully!
    ");
        }
        i = eXosip_listen_addr (IPPROTO_UDP, NULL, 15061, AF_INET, 0);
        if (i != 0)
        {
            eXosip_quit ();
            fprintf (stderr, "eXosip_listen_addr error!
    Couldn't initialize transport layer!
    ");
        }
        for(;;)
        {
            //侦听是否有消息到来
            je = eXosip_event_wait (0,50);
            //协议栈带有此语句,具体作用未知
            eXosip_lock ();
            eXosip_default_action (je);
            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!
    ");
                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
    ", 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
    ",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;
    
                eXosip_lock ();
                eXosip_call_send_answer (je->tid, 180, NULL);
                i = eXosip_call_build_answer (je->tid, 200, &answer);
                if (i != 0)
                {
                    printf ("This request msg is invalid!Cann't response!
    ");
                    eXosip_call_send_answer (je->tid, 400, NULL);
                }
                else
                {
                    snprintf (tmp, 4096,
                              "v=0
    "
                              "o=anonymous 0 0 IN IP4 0.0.0.0
    "
                              "t=1 10
    "
                              "a=username:rainfish
    "
                              "a=password:123
    ");
    
                    //设置回复的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!
    ");
                }
                eXosip_unlock ();
    
                //显示出在sdp消息体中的attribute 的内容,里面计划存放我们的信息
                printf ("the INFO is :
    ");
                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
    ", at->a_att_field, at->a_att_value);//这里解释了为什么在SDP消息体中属性a里面存放必须是两列
    
                    pos ++;
                }
                break;
            case EXOSIP_CALL_ACK:
                printf ("ACK recieved!
    ");
                // printf ("the cid is %s, did is %s/n", je->did, je->cid);
                break;
            case EXOSIP_CALL_CLOSED:
                printf ("the remote hold the session!
    ");
                // 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!
    ");
                    eXosip_call_send_answer (je->tid, 400, NULL);
    
                }
                else
                {
                    eXosip_call_send_answer (je->tid, 200, answer);
                    printf ("bye send 200 over!
    ");
                }
                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
    ");
                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
    ", body->body);
                    }
                }
                break;
            default:
                printf ("Could not parse the msg!
    ");
            }
        }
    }
     
     
     
     
     
     
  • 相关阅读:
    Practice3_5_vector_sort_struct_gold_silver_bronze_playerName1
    Practice3_4_vector_sort_struct_string
    Practice3_3_vector_sort_struct
    Practice3_2_vector_sort_struct
    2017多校第9场 HDU 6170 Two strings DP
    BZOJ 3771 生成函数,FFT
    BZOJ 3028 食物 生成函数
    Codeforces Round #428 (Div. 2) 题解
    2017中国大学生程序设计竞赛
    2017多校第8场 HDU 6133 Army Formations 线段树合并
  • 原文地址:https://www.cnblogs.com/welhzh/p/11857359.html
Copyright © 2011-2022 走看看