原创文章,引用请保证原文完整性,尊重作者劳动,原文地址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库,这一步必须在所有使用之前完成。

     #include <eXosip2/eXosip.h>
//库处理结果
int result = OSIP_SUCCESS;
//初始化库
if (OSIP_SUCCESS != (result = eXosip_init()))
{
printf("eXosip_init failure.\n");
return ;
}
cout << "eXosip_init success." << endl;
//监听
if (OSIP_SUCCESS != eXosip_listen_addr(IPPROTO_UDP, NULL, UACPORTINT,
AF_INET, ))
{
printf("eXosip_listen_addr failure.\n");
eXosip_quit ();
return ;
}

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

1.2 接受事件

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

         //开启循环消息,实际应用中可以开启多线程同时接收信号
eXosip_event_t* osipEventPtr = NULL; while (true)
{
// Wait the osip event.
osipEventPtr = ::eXosip_event_wait(, );
eXosip_lock();
//一般处理401/407采用库默认处理
eXosip_default_action(osipEventPtr);
eXosip_unlock();
// If get nothing osip event,then continue the loop.
if (NULL == osipEventPtr)
{
continue;
}
// 事件处理 switch (osipEventPtr->type)
{
//需要继续验证REGISTER是什么类型
case EXOSIP_REGISTRATION_NEW:
{
//注册事件处理
}
break;
case EXOSIP_MESSAGE_NEW:
{
//消息事件处理
}
break;
case XXXXX:
{
//事件处理
}
break;
case XXXXX:
{
//事件处理
}
break;
default:
cout << "未处理消息 : " << osipEventPtr->type<<endl;
break;
}
eXosip_event_free(osipEventPtr);
osipEventPtr = NULL;

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

1.3 消息分析

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

     osip_header_t* header = NULL;
osip_message_header_get_byname(request, "expires", , &header);
if (NULL != header && NULL != header->hvalue)
{
......
}

2.UAC代码实例 

 /*
===============================================================
GBT28181 基于eXosip2,osip库实现注册UAC功能
作者:程序人生
博客地址:http://blog.csdn.net/hiwubihe
QQ:1269122125
注:请尊重原作者劳动成果,仅供学习使用,请勿盗用,违者必究!
================================================================
*/ #include <iostream>
#include <string>
#include <sstream>
#include <osipparser2/osip_message.h>
#include <osipparser2/osip_parser.h>
#include <osipparser2/osip_port.h> #include <eXosip2/eXosip.h>
#include <eXosip2/eX_setup.h>
#include <eXosip2/eX_register.h>
#include <eXosip2/eX_options.h>
#include <eXosip2/eX_message.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h> using namespace std; //本地监听IP
#define LISTEN_ADDR ("192.168.50.57")
//本地监听端口
#define UACPORT ("5061")
#define UACPORTINT (5061)
//本UAC地址编码
#define UACCODE ("100110000201000000")
//本地UAC密码
#define UACPWD ("12345")
//远程UAS IP
#define UAS_ADDR ("192.168.50.57")
//远程UAS 端口
#define UAS_PORT ("5060")
//超时
#define EXPIS 300 //当前服务状态 1 已经注册 0 未注册
static int iCurrentStatus;
//注册成功HANDLE
static int iHandle = -; //SIP From/To 头部
class CSipFromToHeader
{
public:
CSipFromToHeader()
{
}
~CSipFromToHeader()
{
}
void SetHeader(string addrCod, string addrI, string addrPor)
{
addrCode = addrCod;
addrIp = addrI;
addrPort = addrPor;
}
string GetFormatHeader()
{
std::stringstream stream;
stream << "sip: " << addrCode << "@" << addrIp << ":" << addrPort;
return stream.str();
}
//主机名称
string GetCode()
{
std::stringstream stream;
stream << addrCode;
return stream.str();
}
//主机地址
string GetAddr()
{
std::stringstream stream;
stream << addrIp;
return stream.str();
}
//端口
string GetPort()
{
std::stringstream stream;
stream << addrPort;
return stream.str();
} private:
string addrCode;
string addrIp;
string addrPort;
}; //SIP Contract头部
class CContractHeader: public CSipFromToHeader
{
public:
CContractHeader()
{
}
~CContractHeader()
{
}
void SetContractHeader(string addrCod, string addrI, string addrPor)
{
SetHeader(addrCod, addrI, addrPor);
}
string GetContractFormatHeader()
{ std::stringstream stream;
stream << "<sip:" << GetCode() << "@" << GetAddr() << ":" << GetPort()
<< ">";
return stream.str();
}
}; //发送注册信息
int SendRegister(int& registerId, CSipFromToHeader &from, CSipFromToHeader &to,
CContractHeader &contact, const string& userName, const string& pwd,
const int expires, int iType)
{
cout << "=============================================" << endl;
if (iType == )
{
cout << "注册请求信息:" << endl;
}
else if (iType == )
{
cout << "刷新注册信息:" << endl;
}
else
{
cout << "注销信息:" << endl;
}
cout << "registerId " << registerId << endl;
cout << "from " << from.GetFormatHeader() << endl;
cout << "to " << to.GetFormatHeader() << endl;
cout << "contact" << contact.GetContractFormatHeader() << endl;
cout << "userName" << userName << endl;
cout << "pwd" << pwd << endl;
cout << "expires" << expires << endl;
cout << "=============================================" << endl;
//服务器注册
static osip_message_t *regMsg = ;
int ret; ::eXosip_add_authentication_info(userName.c_str(), userName.c_str(),
pwd.c_str(), "MD5", NULL);
eXosip_lock();
//发送注册信息 401响应由eXosip2库自动发送
if ( == registerId)
{
// 注册消息的初始化
registerId = ::eXosip_register_build_initial_register(
from.GetFormatHeader().c_str(), to.GetFormatHeader().c_str(),
contact.GetContractFormatHeader().c_str(), expires, &regMsg);
if (registerId <= )
{
return -;
}
}
else
{
// 构建注册消息
ret = ::eXosip_register_build_register(registerId, expires, &regMsg);
if (ret != OSIP_SUCCESS)
{
return ret;
}
//添加注销原因
if (expires == )
{
osip_contact_t *contact = NULL;
char tmp[]; osip_message_get_contact(regMsg, , &contact);
{
sprintf(tmp, "<sip:%s@%s:%s>;expires=0",
contact->url->username, contact->url->host,
contact->url->port);
}
//osip_contact_free(contact);
//reset contact header
osip_list_remove(&regMsg->contacts, );
osip_message_set_contact(regMsg, tmp);
osip_message_set_header(regMsg, "Logout-Reason", "logout");
}
}
// 发送注册消息
ret = ::eXosip_register_send_register(registerId, regMsg);
if (ret != OSIP_SUCCESS)
{
registerId = ;
}eXosip_unlock(); return ret;
} //注册
void Register()
{
if (iCurrentStatus == )
{
cout << "当前已经注册" << endl;
return;
}
CSipFromToHeader stFrom;
stFrom.SetHeader(UACCODE, UAS_ADDR, UAS_PORT);
CSipFromToHeader stTo;
stTo.SetHeader(UACCODE, UAS_ADDR, UAS_PORT);
CContractHeader stContract;
stContract.SetContractHeader(UACCODE, LISTEN_ADDR, UACPORT);
//发送注册信息
int registerId = ;
if ( > SendRegister(registerId, stFrom, stTo, stContract, UACCODE, UACPWD,
, ))
{
cout << "发送注册失败" << endl;
return;
}
iCurrentStatus = ;
iHandle = registerId;
}
//刷新注册
void RefreshRegister()
{
if (iCurrentStatus == )
{
cout << "当前未注册,不允许刷新" << endl;
return;
}
CSipFromToHeader stFrom;
stFrom.SetHeader(UACCODE, UAS_ADDR, UAS_PORT);
CSipFromToHeader stTo;
stTo.SetHeader(UACCODE, UAS_ADDR, UAS_PORT);
CContractHeader stContract;
stContract.SetContractHeader(UACCODE, LISTEN_ADDR, UACPORT);
//发送注册信息
if ( > SendRegister(iHandle, stFrom, stTo, stContract, UACCODE, UACPWD,
, ))
{
cout << "发送刷新注册失败" << endl;
return;
}
}
//注销
void UnRegister()
{
if (iCurrentStatus == )
{
cout << "当前未注册,不允许注销" << endl;
return;
}
CSipFromToHeader stFrom;
stFrom.SetHeader(UACCODE, UAS_ADDR, UAS_PORT);
CSipFromToHeader stTo;
stTo.SetHeader(UACCODE, UAS_ADDR, UAS_PORT);
CContractHeader stContract;
stContract.SetContractHeader(UACCODE, LISTEN_ADDR, UACPORT);
//发送注册信息
if ( > SendRegister( iHandle, stFrom, stTo, stContract, UACCODE, UACPWD,
, ))
{
cout << "发送注销失败" << endl;
return;
}
iCurrentStatus = ;
iHandle = -;
}
static void help()
{
const char
*b =
"-------------------------------------------------------------------------------\n"
"SIP Library test process - uac v 1.0 (June 13, 2014)\n\n"
"SIP UAC端 注册,刷新注册,注销实现\n\n"
"Author: 程序人生\n\n"
"博客地址:http://blog.csdn.net/hiwubihe QQ:1269122125\n\n"
"-------------------------------------------------------------------------------\n"
"\n"
" 0:Register\n"
" 1:RefreshRegister\n"
" 2:UnRegister\n"
" 3:clear scream\n"
" 4:exit\n"
"-------------------------------------------------------------------------------\n"
"\n";
fprintf(stderr, b, strlen(b));
cout << "please select method :";
}
//服务处理线程
void *serverHandle(void *pUser)
{
sleep();
help();
char ch = getchar();
getchar();
while ()
{
switch (ch)
{
case '':
//注册
Register();
break;
case '':
//刷新注册
RefreshRegister();
break;
case '':
//注销
UnRegister();
break;
case '':
if (system("clear") < )
{
cout << "clear scream error" << endl;
exit();
}
break;
case '':
cout << "exit sipserver......" << endl;
getchar();
exit();
default:
cout << "select error" << endl;
break;
}
cout << "press any key to continue......" << endl;
getchar();
help();
ch = getchar();
getchar();
}
return NULL;
} //事件处理线程
void *eventHandle(void *pUser)
{
eXosip_event_t* osipEventPtr = (eXosip_event_t*) pUser;
switch (osipEventPtr->type)
{
//需要继续验证REGISTER是什么类型
case EXOSIP_REGISTRATION_SUCCESS:
case EXOSIP_REGISTRATION_FAILURE:
{
cout<<"收到状态码:"<<osipEventPtr->response->status_code<<"报文"<<endl;
if(osipEventPtr->response->status_code == )
{
cout<<"发送鉴权报文"<<endl;
}
else if(osipEventPtr->response->status_code == )
{
cout<<"接收成功"<<endl;
}
else
{}
}
break;
default:
cout << "The sip event type that not be precessed.the event "
"type is : " << osipEventPtr->type << endl;
break;
}
eXosip_event_free(osipEventPtr);
return NULL;
} int main()
{
iCurrentStatus = ;
//库处理结果
int result = OSIP_SUCCESS;
//初始化库
if (OSIP_SUCCESS != (result = eXosip_init()))
{
printf("eXosip_init failure.\n");
return ;
}
cout << "eXosip_init success." << endl;
eXosip_set_user_agent(NULL);
//监听
if (OSIP_SUCCESS != eXosip_listen_addr(IPPROTO_UDP, NULL, UACPORTINT,
AF_INET, ))
{
printf("eXosip_listen_addr failure.\n");
return ;
}
//设置监听网卡
if (OSIP_SUCCESS != eXosip_set_option(
EXOSIP_OPT_SET_IPV4_FOR_GATEWAY,
LISTEN_ADDR))
{
return -;
}
//开启服务线程
pthread_t pthser;
if ( != pthread_create(&pthser, NULL, serverHandle, NULL))
{
printf("创建主服务失败\n");
return -;
}
//事件用于等待
eXosip_event_t* osipEventPtr = NULL;
//开启事件循环
while (true)
{
//等待事件 0的单位是秒,500是毫秒
osipEventPtr = ::eXosip_event_wait(, );
//处理eXosip库默认处理
{
usleep( * );
eXosip_lock();
//一般处理401/407采用库默认处理
eXosip_default_action(osipEventPtr);
eXosip_unlock();
}
//事件空继续等待
if (NULL == osipEventPtr)
{
continue;
}
//开启线程处理事件并在事件处理完毕将事件指针释放
pthread_t pth;
if ( != pthread_create(&pth, NULL, eventHandle, (void*) osipEventPtr))
{
printf("创建线程处理事件失败\n");
continue;
}
osipEventPtr = NULL;
}
}

3.测试效果

3.1 启动后

3.2 输入0 注册

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

3.3 然后输入1刷新

可以看到收到200OK报文

3.4 输入2注销后

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

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

基于GBT28181:SIP协议组件开发-----------第五篇SIP注册流程eXosip2实现(二)的更多相关文章

  1. 基于GBT28181:SIP协议组件开发-----------第四篇SIP注册流程eXosip2实现(一)

    原创文章,引用请保证原文完整性,尊重作者劳动,原文地址http://www.cnblogs.com/qq1269122125/p/3945294.html. 上章节讲解了利用自主开发的组件SIP组件l ...

  2. 基于GBT28181:SIP协议组件开发-----------第三篇SIP注册流程分析实现

    原创文章,引用请保证原文完整性,尊重作者劳动,原文地址http://www.cnblogs.com/qq1269122125/p/3941172.html,qq:1269122125. 上两章节简要的 ...

  3. 基于GBT28181:SIP协议组件开发-----------第一篇环境搭建

    原创文章,引用请保证原文完整性,尊重作者劳动,原文地址http://www.cnblogs.com/qq1269122125/p/3930018.html,qq:1269122125. SIP协议在安 ...

  4. 基于GBT28181:SIP协议组件开发-----------第二篇SIP组件开发原理

    原创文章,引用请保证原文完整性,尊重作者劳动,原文地址http://www.cnblogs.com/qq1269122125/p/3937590.html,qq:1269122125. 上一节中讲的S ...

  5. ASP.NET自定义控件组件开发 第五章 模板控件开发

    原文:ASP.NET自定义控件组件开发 第五章 模板控件开发 第五章 模板控件开发 系列文章链接: ASP.NET自定义控件组件开发 第一章 待续 ASP.NET自定义控件组件开发 第一章 第二篇 接 ...

  6. 基于vue的新组件开发

    前天完成了一个新组件的开发,做的过程也是各种遇到问题,彻底弄懂了slot,巩固了一些flex布局和jquery的知识,比起自己第一次做组件开发,现在已经是能够下手做,遇到问题解决问题,还算有进步. 但 ...

  7. tornado 基于MongoDB存储 session组件开发

    1.开发伊始 根据源码中RequestHandler类中发现__init__函数中会调用自身initialize函数,此函数中为pass,即可以围绕initialize开发一系列的组件 2.开发实现 ...

  8. 基于Vue的WebApp项目开发(五)

    实现图片分享列表 步骤一:新增图片列表文件photolist.vue <template> <div id="tml"> 图片分享页面 </div&g ...

  9. asp.net微信开发第五篇----用户分组管理

    上一篇已讲解到新建用户分组,移动用户到分组的功能,这一章主要讲解修改分组名称和删除分组 开发者可以使用接口,对公众平台的分组进行查询.创建.修改.删除等操作,也可以使用接口在需要时移动用户到某个分组. ...

随机推荐

  1. java学习之数组(一)【内存】

    在java语言当中,为了更方便多个数据的管理,这里提供数组. 比如说,现在我们有一组数据,7,8,9,9,为了保存这四个数据,我们分别要定义变量来保存,少了还好说.但是假如,有100多个数据呢,我们一 ...

  2. Swap Nodes in Pairs——LeetCode

    Given a linked list, swap every two adjacent nodes and return its head. For example,Given 1->2-&g ...

  3. C++基础复习

    1.Object-C也是面向对象的语言:2.#include<iostream> //#include是一个预处理指令3.using namespace std; //std是命名空间,u ...

  4. motan源码分析九:开关

    在前面的文章中,我们已经发现了开关的踪影,例如cluster,motan支持多个cluster,当前的cluster因为开关关闭的情况下,就会使用下一个cluster. 1.开关相关的类和接口主要都在 ...

  5. ie8下$(document).on('mouseover mouseout','ul li',function(){})的bug

    $(document).on('mouseover mouseout','ul li',function(){ if (event.type == 'mouseover') {           c ...

  6. sqlserver 查找某个字符在字符串中第N次出现的位置

    例如:查找'A,' 在'A,B,C,D,A,B,C,D,C,D,B,A,C,E,'中第二次出现的位置怎么实现,SQL 中有这样的函数吗? SQL code /* 方法很多,这里简单写一个 返回@fin ...

  7. Gradle 1.12 翻译——第十三章 编写构建脚本

    有关其它已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或訪问:http://gradledoc.qiniudn.com ...

  8. 互联网TCP/IP五层模型(一)

    转载自:阮一峰 我们每天使用互联网.你是否想过,它是怎样实现的? 全世界几十亿台电脑,连接在一起,两两通信. 上海的某一块网卡送出信号,洛杉矶的还有一块网卡竟然就收到了.两者实际上根本不知道对方的物理 ...

  9. Lucene中string docvalues使用utf-16的优化

    原来的string docvalues使用utf-8编码,载入时转码花费大量时间,我们把转码实现从new String(bytes, "UTF-8")改用lucene的bytesR ...

  10. 从零单排c++ primer(17)

    (1)假设一个名字在派生类的作用域内无法正确解析,则编译器将继续在外层的基类作用域中寻找该名字的定义. (2)派生类的成员将隐藏同名的基类成员. (3)除了覆盖继承而来的虚函数之外,派生类最好不要重用 ...