zoukankan      html  css  js  c++  java
  • 基于GBT28181:SIP协议组件开发-----------第五篇SIP注册流程eXosip2实现(二)

    原创文章,引用请保证原文完整性,尊重作者劳动,原文地址http://www.cnblogs.com/qq1269122125/p/3966794.html。

    上章节讲解了讲解一个用eXosip2库实现的Demo 程序。Demo讲的是注册的过程,因为篇幅比较长,再分一节写。本节是上一节的继续,主要实现UAC用eXosip2库实现的Demo 程序。本节讲的比较全面,处理实现注册问题还添加了注销和刷新注册的过程。刷新相当于心跳的功能。注意这个函数eXosip_default_action()实现在sip中401和407错误类型的eXosip2库的自动处理。网上有的人问注册报文发送后,只收到401返回码。这是对SIP注册不了解造成的。至于这个过程在前面注册理论部分已经讲解。我也尝试不用eXosip_default_action()这个函数,我自己发送鉴权信息,可惜没成功,不知道什么原因,有时返回200OK,有时不返回,所以还是用了eXosip_default_action()这个函数,让401的响应报文由eXosip2库去发送。

    1.eXosip2 API介绍

    本章中要用的eXosip2库的API做个简单的介绍和使用方法

    1.1 初始化库

    在使用eXosip2前你需要初始化eXosip环境和libeXosip2.so库,这一步必须在所有使用之前完成。

     1     #include <eXosip2/eXosip.h>
     2     //库处理结果
     3     int result = OSIP_SUCCESS;
     4     //初始化库
     5     if (OSIP_SUCCESS != (result = eXosip_init()))
     6     {
     7         printf("eXosip_init failure.
    ");
     8         return 1;
     9     }
    10     cout << "eXosip_init success." << endl;
    11     //监听
    12     if (OSIP_SUCCESS != eXosip_listen_addr(IPPROTO_UDP, NULL, UACPORTINT,
    13             AF_INET, 0))
    14     {
    15         printf("eXosip_listen_addr failure.
    ");
    16                 eXosip_quit ();
    17         return 1;
    18     }                        

    初始化完成之后,用户就可以发送SIP消息和等待接受SIP事件了。

    1.2 接受事件

    初始化eXosip2库后,主服务程序就可以接受事件并处理事件了,下面是一些从eXosip2协议栈接受处理事件的实例代码。

     1         //开启循环消息,实际应用中可以开启多线程同时接收信号
     2     eXosip_event_t* osipEventPtr = NULL;
     3 
     4     while (true)
     5     {
     6         // Wait the osip event.
     7         osipEventPtr = ::eXosip_event_wait(0, 200);
     8                 eXosip_lock();
     9         //一般处理401/407采用库默认处理
    10         eXosip_default_action(osipEventPtr);
    11         eXosip_unlock();
    12         // If get nothing osip event,then continue the loop.
    13         if (NULL == osipEventPtr)
    14         {
    15             continue;
    16         }
    17         // 事件处理
    18 
    19         switch (osipEventPtr->type)
    20         {
    21         //需要继续验证REGISTER是什么类型
    22         case EXOSIP_REGISTRATION_NEW:
    23         {
    24             //注册事件处理
    25         }
    26             break;
    27         case EXOSIP_MESSAGE_NEW:
    28         {
    29                 //消息事件处理
    30         }
    31             break;
    32             case XXXXX:
    33         {
    34                 //事件处理
    35         }
    36             break;
    37            case XXXXX:
    38         {
    39                 //事件处理
    40         }
    41             break;
    42         default:
    43             cout << "未处理消息 : " << osipEventPtr->type<<endl;
    44             break;
    45         }
    46         eXosip_event_free(osipEventPtr);
    47         osipEventPtr = NULL;                    

    实际在应用的时候为了提高服务的并发性,一般并不用上面这种服务方式,因为上面这样,如果接受到某个事件,而这个事件的处理事件非常长的话,会影响效率,导致其他事件阻塞等待,得不到即使处理。所以一般采用并发服务器的设计思想,即在接受到一个事件后,分配一个线程来处理。当然如果想效率更高,想节省创建线程的时间,可以在系统一起动后,分配一定大小的线程池,有事件到来的话,直接从线程池中获取线程处理。

    1.3 消息分析

    每个UAC或者UAS发送一个SIP信息后,相对应的UAS或者UAC都会接受到响应的,即事件。每个事件包含两个部分,一个是request和response。request即是这个事件的请求报文, 而response是本次会话建立过程中,最后一次响应请求的报文。用户可以从获取的消息中,分析出message并且获取SIP头部,保存成用户自己的数据形式。下面实例获取expires头部内容。

    1     osip_header_t* header = NULL;
    2     osip_message_header_get_byname(request, "expires", 0, &header);
    3     if (NULL != header && NULL != header->hvalue)
    4     {
    5            ......
    6     }
    7     

    2.UAC代码实例 

      1 /*
      2  ===============================================================
      3  GBT28181 基于eXosip2,osip库实现注册UAC功能
      4  作者:程序人生
      5  博客地址:http://blog.csdn.net/hiwubihe
      6  QQ:1269122125
      7  注:请尊重原作者劳动成果,仅供学习使用,请勿盗用,违者必究!
      8  ================================================================
      9  */
     10 
     11 #include <iostream>
     12 #include <string>
     13 #include <sstream>
     14 #include <osipparser2/osip_message.h>
     15 #include <osipparser2/osip_parser.h>
     16 #include <osipparser2/osip_port.h>
     17 
     18 #include <eXosip2/eXosip.h>
     19 #include <eXosip2/eX_setup.h>
     20 #include <eXosip2/eX_register.h>
     21 #include <eXosip2/eX_options.h>
     22 #include <eXosip2/eX_message.h>
     23 #include <arpa/inet.h>
     24 #include <sys/types.h>
     25 #include <sys/socket.h>
     26 
     27 using namespace std;
     28 
     29 //本地监听IP
     30 #define LISTEN_ADDR ("192.168.50.57")
     31 //本地监听端口
     32 #define UACPORT ("5061")
     33 #define UACPORTINT (5061)
     34 //本UAC地址编码
     35 #define UACCODE ("100110000201000000")
     36 //本地UAC密码
     37 #define UACPWD ("12345")
     38 //远程UAS IP
     39 #define UAS_ADDR ("192.168.50.57")
     40 //远程UAS 端口
     41 #define UAS_PORT ("5060")
     42 //超时
     43 #define EXPIS 300
     44 
     45 //当前服务状态 1 已经注册 0 未注册
     46 static int iCurrentStatus;
     47 //注册成功HANDLE
     48 static int iHandle = -1;
     49 
     50 //SIP From/To 头部
     51 class CSipFromToHeader
     52 {
     53 public:
     54     CSipFromToHeader()
     55     {
     56     }
     57     ~CSipFromToHeader()
     58     {
     59     }
     60     void SetHeader(string addrCod, string addrI, string addrPor)
     61     {
     62         addrCode = addrCod;
     63         addrIp = addrI;
     64         addrPort = addrPor;
     65     }
     66     string GetFormatHeader()
     67     {
     68         std::stringstream stream;
     69         stream << "sip: " << addrCode << "@" << addrIp << ":" << addrPort;
     70         return stream.str();
     71     }
     72     //主机名称
     73     string GetCode()
     74     {
     75         std::stringstream stream;
     76         stream << addrCode;
     77         return stream.str();
     78     }
     79     //主机地址
     80     string GetAddr()
     81     {
     82         std::stringstream stream;
     83         stream << addrIp;
     84         return stream.str();
     85     }
     86     //端口
     87     string GetPort()
     88     {
     89         std::stringstream stream;
     90         stream << addrPort;
     91         return stream.str();
     92     }
     93 
     94 private:
     95     string addrCode;
     96     string addrIp;
     97     string addrPort;
     98 };
     99 
    100 //SIP Contract头部
    101 class CContractHeader: public CSipFromToHeader
    102 {
    103 public:
    104     CContractHeader()
    105     {
    106     }
    107     ~CContractHeader()
    108     {
    109     }
    110     void SetContractHeader(string addrCod, string addrI, string addrPor)
    111     {
    112         SetHeader(addrCod, addrI, addrPor);
    113     }
    114     string GetContractFormatHeader()
    115     {
    116 
    117         std::stringstream stream;
    118         stream << "<sip:" << GetCode() << "@" << GetAddr() << ":" << GetPort()
    119                 << ">";
    120         return stream.str();
    121     }
    122 };
    123 
    124 //发送注册信息
    125 int SendRegister(int& registerId, CSipFromToHeader &from, CSipFromToHeader &to,
    126         CContractHeader &contact, const string& userName, const string& pwd,
    127         const int expires, int iType)
    128 {
    129     cout << "=============================================" << endl;
    130     if (iType == 0)
    131     {
    132         cout << "注册请求信息:" << endl;
    133     }
    134     else if (iType == 1)
    135     {
    136         cout << "刷新注册信息:" << endl;
    137     }
    138     else
    139     {
    140         cout << "注销信息:" << endl;
    141     }
    142     cout << "registerId " << registerId << endl;
    143     cout << "from " << from.GetFormatHeader() << endl;
    144     cout << "to " << to.GetFormatHeader() << endl;
    145     cout << "contact" << contact.GetContractFormatHeader() << endl;
    146     cout << "userName" << userName << endl;
    147     cout << "pwd" << pwd << endl;
    148     cout << "expires" << expires << endl;
    149     cout << "=============================================" << endl;
    150     //服务器注册
    151     static osip_message_t *regMsg = 0;
    152     int ret;
    153 
    154     ::eXosip_add_authentication_info(userName.c_str(), userName.c_str(),
    155             pwd.c_str(), "MD5", NULL);
    156     eXosip_lock();
    157     //发送注册信息 401响应由eXosip2库自动发送
    158     if (0 == registerId)
    159     {
    160         // 注册消息的初始化
    161         registerId = ::eXosip_register_build_initial_register(
    162                 from.GetFormatHeader().c_str(), to.GetFormatHeader().c_str(),
    163                 contact.GetContractFormatHeader().c_str(), expires, &regMsg);
    164         if (registerId <= 0)
    165         {
    166             return -1;
    167         }
    168     }
    169     else
    170     {
    171         // 构建注册消息
    172         ret = ::eXosip_register_build_register(registerId, expires, &regMsg);
    173         if (ret != OSIP_SUCCESS)
    174         {
    175             return ret;
    176         }
    177         //添加注销原因
    178         if (expires == 0)
    179         {
    180             osip_contact_t *contact = NULL;
    181             char tmp[128];
    182 
    183             osip_message_get_contact(regMsg, 0, &contact);
    184             {
    185                 sprintf(tmp, "<sip:%s@%s:%s>;expires=0",
    186                         contact->url->username, contact->url->host,
    187                         contact->url->port);
    188             }
    189             //osip_contact_free(contact);
    190             //reset contact header
    191             osip_list_remove(&regMsg->contacts, 0);
    192             osip_message_set_contact(regMsg, tmp);
    193             osip_message_set_header(regMsg, "Logout-Reason", "logout");
    194         }
    195     }
    196     // 发送注册消息
    197     ret = ::eXosip_register_send_register(registerId, regMsg);
    198     if (ret != OSIP_SUCCESS)
    199     {
    200         registerId = 0;
    201     }eXosip_unlock();
    202 
    203     return ret;
    204 }
    205 
    206 //注册
    207 void Register()
    208 {
    209     if (iCurrentStatus == 1)
    210     {
    211         cout << "当前已经注册" << endl;
    212         return;
    213     }
    214     CSipFromToHeader stFrom;
    215     stFrom.SetHeader(UACCODE, UAS_ADDR, UAS_PORT);
    216     CSipFromToHeader stTo;
    217     stTo.SetHeader(UACCODE, UAS_ADDR, UAS_PORT);
    218     CContractHeader stContract;
    219     stContract.SetContractHeader(UACCODE, LISTEN_ADDR, UACPORT);
    220     //发送注册信息
    221     int registerId = 0;
    222     if (0 > SendRegister(registerId, stFrom, stTo, stContract, UACCODE, UACPWD,
    223             3000, 0))
    224     {
    225         cout << "发送注册失败" << endl;
    226         return;
    227     }
    228     iCurrentStatus = 1;
    229     iHandle = registerId;
    230 }
    231 //刷新注册
    232 void RefreshRegister()
    233 {
    234     if (iCurrentStatus == 0)
    235     {
    236         cout << "当前未注册,不允许刷新" << endl;
    237         return;
    238     }
    239     CSipFromToHeader stFrom;
    240     stFrom.SetHeader(UACCODE, UAS_ADDR, UAS_PORT);
    241     CSipFromToHeader stTo;
    242     stTo.SetHeader(UACCODE, UAS_ADDR, UAS_PORT);
    243     CContractHeader stContract;
    244     stContract.SetContractHeader(UACCODE, LISTEN_ADDR, UACPORT);
    245     //发送注册信息
    246     if (0 > SendRegister(iHandle, stFrom, stTo, stContract, UACCODE, UACPWD,
    247             3000, 1))
    248     {
    249         cout << "发送刷新注册失败" << endl;
    250         return;
    251     }
    252 }
    253 //注销
    254 void UnRegister()
    255 {
    256     if (iCurrentStatus == 0)
    257     {
    258         cout << "当前未注册,不允许注销" << endl;
    259         return;
    260     }
    261     CSipFromToHeader stFrom;
    262     stFrom.SetHeader(UACCODE, UAS_ADDR, UAS_PORT);
    263     CSipFromToHeader stTo;
    264     stTo.SetHeader(UACCODE, UAS_ADDR, UAS_PORT);
    265     CContractHeader stContract;
    266     stContract.SetContractHeader(UACCODE, LISTEN_ADDR, UACPORT);
    267     //发送注册信息
    268269     if (0 > SendRegister( iHandle, stFrom, stTo, stContract, UACCODE, UACPWD,
    270             0, 2))
    271     {
    272         cout << "发送注销失败" << endl;
    273         return;
    274     }
    275     iCurrentStatus = 0;
    276     iHandle = -1;
    277 }
    278 static void help()
    279 {
    280     const char
    281             *b =
    282     "-------------------------------------------------------------------------------
    "
    283     "SIP Library test process - uac v 1.0 (June 13, 2014)
    
    "
    284     "SIP UAC端 注册,刷新注册,注销实现
    
    "
    285     "Author: 程序人生
    
    "
    286     "博客地址:http://blog.csdn.net/hiwubihe QQ:1269122125
    
    "
    287     "-------------------------------------------------------------------------------
    "
    288     "
    "
    289     "              0:Register
    "
    290     "              1:RefreshRegister
    "
    291     "              2:UnRegister
    "
    292     "              3:clear scream
    "
    293     "              4:exit
    "
    294     "-------------------------------------------------------------------------------
    "
    295     "
    ";
    296     fprintf(stderr, b, strlen(b));
    297     cout << "please select method :";
    298 }
    299 //服务处理线程
    300 void *serverHandle(void *pUser)
    301 {
    302     sleep(3);
    303     help();
    304     char ch = getchar();
    305     getchar();
    306     while (1)
    307     {
    308         switch (ch)
    309         {
    310         case '0':
    311             //注册
    312             Register();
    313             break;
    314         case '1':
    315             //刷新注册
    316             RefreshRegister();
    317             break;
    318         case '2':
    319             //注销
    320             UnRegister();
    321             break;
    322         case '3':
    323             if (system("clear") < 0)
    324             {
    325                 cout << "clear scream error" << endl;
    326                 exit(1);
    327             }
    328             break;
    329         case '4':
    330             cout << "exit sipserver......" << endl;
    331             getchar();
    332             exit(0);
    333         default:
    334             cout << "select error" << endl;
    335             break;
    336         }
    337         cout << "press any key to continue......" << endl;
    338         getchar();
    339         help();
    340         ch = getchar();
    341         getchar();
    342     }
    343     return NULL;
    344 }
    345 
    346 //事件处理线程
    347 void *eventHandle(void *pUser)
    348 {
    349     eXosip_event_t* osipEventPtr = (eXosip_event_t*) pUser;
    350     switch (osipEventPtr->type)
    351     {
    352     //需要继续验证REGISTER是什么类型
    353     case EXOSIP_REGISTRATION_SUCCESS:
    354     case EXOSIP_REGISTRATION_FAILURE:
    355     {
    356         cout<<"收到状态码:"<<osipEventPtr->response->status_code<<"报文"<<endl;
    357         if(osipEventPtr->response->status_code == 401)
    358         {
    359             cout<<"发送鉴权报文"<<endl;
    360         }
    361         else if(osipEventPtr->response->status_code == 200)
    362         {
    363             cout<<"接收成功"<<endl;
    364         }
    365         else
    366         {}
    367     }
    368         break;
    369     default:
    370         cout << "The sip event type that not be precessed.the event "
    371             "type is : " << osipEventPtr->type << endl;
    372         break;
    373     }
    374     eXosip_event_free(osipEventPtr);
    375     return NULL;
    376 }
    377 
    378 int main()
    379 {
    380     iCurrentStatus = 0;
    381     //库处理结果
    382     int result = OSIP_SUCCESS;
    383     //初始化库
    384     if (OSIP_SUCCESS != (result = eXosip_init()))
    385     {
    386         printf("eXosip_init failure.
    ");
    387         return 1;
    388     }
    389     cout << "eXosip_init success." << endl;
    390     eXosip_set_user_agent(NULL);
    391     //监听
    392     if (OSIP_SUCCESS != eXosip_listen_addr(IPPROTO_UDP, NULL, UACPORTINT,
    393             AF_INET, 0))
    394     {
    395         printf("eXosip_listen_addr failure.
    ");
    396         return 1;
    397     }
    398     //设置监听网卡
    399     if (OSIP_SUCCESS != eXosip_set_option(
    400     EXOSIP_OPT_SET_IPV4_FOR_GATEWAY,
    401             LISTEN_ADDR))
    402     {
    403         return -1;
    404     }
    405     //开启服务线程
    406     pthread_t pthser;
    407     if (0 != pthread_create(&pthser, NULL, serverHandle, NULL))
    408     {
    409         printf("创建主服务失败
    ");
    410         return -1;
    411     }
    412     //事件用于等待
    413     eXosip_event_t* osipEventPtr = NULL;
    414     //开启事件循环
    415     while (true)
    416     {
    417         //等待事件 0的单位是秒,500是毫秒
    418         osipEventPtr = ::eXosip_event_wait(0, 200);
    419         //处理eXosip库默认处理
    420         {
    421             usleep(500 * 1000);
    422             eXosip_lock();
    423             //一般处理401/407采用库默认处理
    424             eXosip_default_action(osipEventPtr);
    425             eXosip_unlock();
    426         }
    427         //事件空继续等待
    428         if (NULL == osipEventPtr)
    429         {
    430             continue;
    431         }
    432         //开启线程处理事件并在事件处理完毕将事件指针释放
    433         pthread_t pth;
    434         if (0 != pthread_create(&pth, NULL, eventHandle, (void*) osipEventPtr))
    435         {
    436             printf("创建线程处理事件失败
    ");
    437             continue;
    438         }
    439         osipEventPtr = NULL;
    440     }
    441 }

    3.测试效果

     3.1 启动后

    3.2 输入0 注册

    可以看到第一次收到了401报文,库自动发送鉴权信息,然后收到了200OK报文。

    3.3 然后输入1刷新

    可以看到收到200OK报文

    3.4 输入2注销后

    收到200OK报文。并且可以看到expires为0了。

    至此eXosip2库实现注册,全部功能完成。

    欢迎技术交流沟通,转载请注明出处并保持作品的完整性。 作者:程序人生 qq1269122125
  • 相关阅读:
    linux软件安装方式
    docker 安装 jenkins touch: cannot touch ‘/var/jenkins_home/copy_reference_file.log’: Permission denied Can not write to /var/jenkins_home/copy_reference_file.log. Wrong volume permissions?
    [ERR] Node goodsleep.vip:6379 is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0.
    Linux 常用命令 服务器间scp 用户 export 创建文件、软连接
    redis 安装 集群 主从 哨兵 docker
    WPF密码框中禁止复制、粘贴
    Application 统计在线人数
    【转义字符】HTML 字符实体&lt; &gt: &amp;等
    SQL语句统计每天的数据
    正则表达式计算代码数
  • 原文地址:https://www.cnblogs.com/qq1269122125/p/3966794.html
Copyright © 2011-2022 走看看