原创文章,引用请保证原文完整性,尊重作者劳动,原文地址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. 【POJ】3283 Card Hands

    字典树. #include <iostream> #include <cstdio> #include <cstring> #include <string& ...

  2. Unity NGUI实现技能CD效果

    unity版本:4.5.1 NGUI版本:3.6.5 脚本代码:C# 在游戏中经常要实现技能的CD效果,NGUI中已经实现了这个功能,即在button上创建一个半透明的Sprite实现这个功能. 首先 ...

  3. BZOJ 1072 [SCOI2007]排列perm

    1072: [SCOI2007]排列perm Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1268  Solved: 782[Submit][Sta ...

  4. 动态规划:NOI2013 快餐店

    Description 小 T打算在城市C开设一家外送快餐店.送餐到某一个地点的时间与外卖店到该地点之间最短路径长度是成正比的,小T希望快餐店的地址选在离最远的顾客距离最近 的地方. 快餐店的顾客分布 ...

  5. svn server安装配置

    安装平台:RHEL5 1.安装软件:httpd.subversion.mod_dav_svn 2.修改配置 修改/etc/httpd/conf.d/subversion.conf.eg: LoadMo ...

  6. [Locked] Alien Dictionary

    Alien Dictionary There is a new alien language which uses the latin alphabet. However, the order amo ...

  7. Mac下Intellij IDea发布Java Web项目详解五 开始测试

    测试前准备工作目录 Mac下Intellij IDea发布Web项目详解一 Mac下Intellij IDea发布Java Web项目(适合第一次配置Tomcat的家伙们)详解二 Mac下Intell ...

  8. Lucene 4.10.2开发示例

    这里面用的是比较新的Lucene4.10.2 做的一个实例.(lucene的索引不能太大,要不然效率会很低.大于1G的时候就必须考虑分布索引的问题) 先介绍一下Lucene的几个参数意义: Index ...

  9. Linux 上Oracle RAC 10g 升级到 Oracle RAC 11g

    了解如何在 Oracle Enterprise Linux 5 上逐步将 Oracle RAC 10g 第 2 版升级到 Oracle RAC 11g. Oracle 数据库 11g(即,新一代网格计 ...

  10. Android Fragment动态添加 FragmentTransaction FragmentManager

    Fragment常用的三个类:android.app.Fragment 主要用于定义Fragmentandroid.app.FragmentManager 主要用于在Activity中操作Fragme ...