基于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库,这一步必须在所有使用之前完成。
#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, ®Msg);
if (registerId <= )
{
return -;
}
}
else
{
// 构建注册消息
ret = ::eXosip_register_build_register(registerId, expires, ®Msg);
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(®Msg->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实现(二)的更多相关文章
- 基于GBT28181:SIP协议组件开发-----------第四篇SIP注册流程eXosip2实现(一)
原创文章,引用请保证原文完整性,尊重作者劳动,原文地址http://www.cnblogs.com/qq1269122125/p/3945294.html. 上章节讲解了利用自主开发的组件SIP组件l ...
- 基于GBT28181:SIP协议组件开发-----------第三篇SIP注册流程分析实现
原创文章,引用请保证原文完整性,尊重作者劳动,原文地址http://www.cnblogs.com/qq1269122125/p/3941172.html,qq:1269122125. 上两章节简要的 ...
- 基于GBT28181:SIP协议组件开发-----------第一篇环境搭建
原创文章,引用请保证原文完整性,尊重作者劳动,原文地址http://www.cnblogs.com/qq1269122125/p/3930018.html,qq:1269122125. SIP协议在安 ...
- 基于GBT28181:SIP协议组件开发-----------第二篇SIP组件开发原理
原创文章,引用请保证原文完整性,尊重作者劳动,原文地址http://www.cnblogs.com/qq1269122125/p/3937590.html,qq:1269122125. 上一节中讲的S ...
- ASP.NET自定义控件组件开发 第五章 模板控件开发
原文:ASP.NET自定义控件组件开发 第五章 模板控件开发 第五章 模板控件开发 系列文章链接: ASP.NET自定义控件组件开发 第一章 待续 ASP.NET自定义控件组件开发 第一章 第二篇 接 ...
- 基于vue的新组件开发
前天完成了一个新组件的开发,做的过程也是各种遇到问题,彻底弄懂了slot,巩固了一些flex布局和jquery的知识,比起自己第一次做组件开发,现在已经是能够下手做,遇到问题解决问题,还算有进步. 但 ...
- tornado 基于MongoDB存储 session组件开发
1.开发伊始 根据源码中RequestHandler类中发现__init__函数中会调用自身initialize函数,此函数中为pass,即可以围绕initialize开发一系列的组件 2.开发实现 ...
- 基于Vue的WebApp项目开发(五)
实现图片分享列表 步骤一:新增图片列表文件photolist.vue <template> <div id="tml"> 图片分享页面 </div&g ...
- asp.net微信开发第五篇----用户分组管理
上一篇已讲解到新建用户分组,移动用户到分组的功能,这一章主要讲解修改分组名称和删除分组 开发者可以使用接口,对公众平台的分组进行查询.创建.修改.删除等操作,也可以使用接口在需要时移动用户到某个分组. ...
随机推荐
- linux下so动态库一些不为人知的秘密(转)
linux 下有动态库和静态库,动态库以.so为扩展名,静态库以.a为扩展名.二者都使用广泛.本文主要讲动态库方面知识.基本上每一个linux 程序都至少会有一个动态库,查看某个程序使用了那些动态库, ...
- LVS+Keepalived+Nginx+Tomcat高可用负载均衡集群配置(DR模式,一个VIP,多个端口)
一.概述 LVS作用:实现负载均衡 Keepalived作用:监控集群系统中各个服务节点的状态,HA cluster. 配置LVS有两种方式: 1. 通过ipvsadm命令行方式配置 2. 通过Red ...
- HDOJ 2073 无限的路
Problem Description 甜甜从小就喜欢画图画,最近他买了一支智能画笔,由于刚刚接触,所以甜甜只会用它来画直线,于是他就在平面直角坐标系中画出如下的图形: 甜甜的好朋友蜜蜜发现上面的图还 ...
- 代码审查工具之PMD操作指南
上周客户要求对OA系统的代码质量进行了一个整体审查,并且要出一份报告给领导. 为此花了半天时间把代码审查工具PMD琢磨了下,现将具体操作步骤描述如下,以供大家参考! 1 前言 质量是衡量一个软件是否成 ...
- [Javascript] Promise
Promise 代表着一个异步操作,这个异步操作现在尚未完成,但在将来某刻会被完成. Promise 有三种状态 pending : 初始的状态,尚未知道结果 fulfilled : 代表操作成功 r ...
- Jenkins 五: 构建Ant项目
1. 点击“新建”,在“Item名称”栏输入要构建的项目名,比如“Ant_project”,选择“构建一个自由风格的软件项目”,点击“OK”按钮. 2. 找到“源码管理”-> “Subversi ...
- Java IO流以及装饰器模式在其上的运用
流概述 Java中,流是一种有序的字节序列,可以有任意的长度.从应用流向目的地称为输出流,从目的地流向应用称为输入流. Java的流族谱 Java的 java.io 包中囊括了整个流的家族,输出流和输 ...
- zznu 1068: 进制转换
进制应该属于程序员的看家本事了,也是大家水平告别菜鸟的一个转折,所以进制转换题目是很有意义的, 这个题目是最简单的把二进制数化简成十进制,因为输入有可能有31位,所以无法使用int或者long lon ...
- Node.js学习(7)----包
包是在模块的基础上更深一步的抽象,Node.js的包类似于C/C++函数库或者Java/.NET的类库.它将独立的功能封装起来用于发布.更新.依赖管理和版本控制. Node.js的包是一个目录,其中包 ...
- ActionScript 3.0日期与时间管理(Date类)
,6)); var now_1:Date=new Date(); trace(now_1.getHours()); /*输出结果会根据设置和测试时间不同而有 ...