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

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

上两章节简要的讲解了SIP组件开发接口和开发环境的搭建。在本节将实现Linux 32平台的UAS和UAC,当然该UAS和UAC只实现了注册功能,并且是基于自主开发SIP组件libGBT28181SipComponent.so的,没有这个组件是运行不了的。其他功能在后续章节中讲解。

首先简单讲解一下GBT28181关于注册描述

一. GBT28181注册的流程如下图

         基于GBT28181:SIP协议组件开发-----------第三篇SIP注册流程分析实现_第1张图片

          电力系统注册稍微复杂点,但原来基本相同。多了个刷新注册的过程。

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

 

 

二.GBT28181关于注册的解释如下

基于GBT28181:SIP协议组件开发-----------第三篇SIP注册流程分析实现_第3张图片

 

三.SIP协议简介

一个合法的SIP请求必须至少包含如下头域:TO,FROM,Cseq,Call-ID,Max-Forwards, Via;这些字段在所有SIP请求中必须包含。这6个字段是SIP消息的基本组成部分,他们提供了用于路由用的核心信息,包含了消息的地址,响应的路由,消息传递次数,详细的顺序,事务的唯一标志。 

这些头域字段是必须包含在请求行之后的,请求行包含了请求的方法,Request-URI,SIP的版本号码。请求行例子:REGISTER sip:192.168.10.177:5060 SIP/2.0

基于GBT28181:SIP协议组件开发-----------第三篇SIP注册流程分析实现_第4张图片

 

四.GBT28181注册流程如下

 1.UAC--->UAS 发送请求登录,传送未鉴权信息

 基于GBT28181:SIP协议组件开发-----------第三篇SIP注册流程分析实现_第5张图片

   

 From 字段,由UAS管理的UAC地址编码@UAS IP:UAS端口号。在实际过程中,UAS管理很多的UAC每个UAC都会保存一个地址编码(可以理解为用户名)和密码等UAC的信息,当UAC登录时,用于验证UAC身份的合法性。

 To字段,From相同

 Contact字段是通讯信息字段,保存是本地UAC地址编码@本地IP:UAC端口号

 Call-ID地段,对应用层是必须要的,一次成功登录完成后要保存这个Call_id值,因为这个ID是标志这次注册的唯一标志。在后续的注销登录及刷新登录都必须要这个ID.。

 Cseq值保证了REGISTER请求的正确顺序

 Expires字段:表示该登记生存期为3600s。

 Content-Length字段:表明此请求消息消息体的长度为空,即此消息不带会话描述

 

2.UAS--->UAC  exosip库在发送注册请求时,第一次发送未鉴权信息,UAS收到后回复401,并携带认证体制(如MD4)和认证参数(如nonce值)。

 

基于GBT28181:SIP协议组件开发-----------第三篇SIP注册流程分析实现_第6张图片

 

3.UAC--->UAS  UAC在收到401信息后,根据UAS发送的401信息中的认证体制和认证参数,结合用户名和密码,生成response值。发送鉴权消息。

 基于GBT28181:SIP协议组件开发-----------第三篇SIP注册流程分析实现_第7张图片

 

4.UAS--->UAC UAS收到鉴权信息后,根据自己自身的管理体制,找到UAC用户在服务器中的密码,根据UAC发送的认证体制和认证参数,结合用户名和密码,生成response值,在把response和UAC发送的response比较,相等则认证通过发送 200 ok。不等发送404验证失败。

 

基于GBT28181:SIP协议组件开发-----------第三篇SIP注册流程分析实现_第8张图片

 

五.源代码

这里注册功能虽然简单,但是为了后续其他功能的添加,这里还是根据功能划分了几个模块。后续添加功能,只是在这个框架中添加。

 

UAS_test部分代码:

  1. 主要功能文件method.h

/*
===============================================================
    GBT28181 SIP组件libGBT28181SipComponent.so注册实现
    作者:程序人生
    博客地址:http://blog.csdn.net/hiwubihe
    QQ:1269122125
    注:请尊重原作者劳动成果,仅供学习使用,请勿盗用,违者必究!
================================================================
*/
#ifndef METHOD_H_
#define METHOD_H_
#include <iostream>
#include <cstdlib>
#include <stdio.h>
#include "callback.h"
#include "IGBT28181Comm.h"
#include "sipserver.h"

using namespace GBT28181::Vsp;
using namespace std;
//启动UAS角色的服务器
int server_start(void*addr);
//停止UAS角色服务器
void server_stop();

#endif /* METHOD_H_ */

2.method.cpp 实现文件中,开启服务包括启动服务和设置回调函数。

 /*
===============================================================
    GBT28181 SIP组件libGBT28181SipComponent.so注册实现
    作者:程序人生
    博客地址:http://blog.csdn.net/hiwubihe
    QQ:1269122125
    注:请尊重原作者劳动成果,仅供学习使用,请勿盗用,违者必究!
================================================================
*/
#include "method.h"
#include <semaphore.h>
static IGBT28181Comm* g_SIPComm = NULL;
//启动SIP服务
int server_start(void*addr)
{
    COMM_PAIR *addr_entry = (COMM_PAIR *) addr;
    if (g_SIPComm != NULL)
    {
        delete g_SIPComm;
    }
    if (!(g_SIPComm = new IGBT28181Comm(true)))
    {
        return -1;
    }
    //回调函数
    g_SIPComm->SetResponseCallback(&server_callback, (void_t*) g_SIPComm);
    g_SIPComm->StartSip(addr_entry->local_addr, addr_entry->local_port);
    return 0;
}
//停止SIP服务
void server_stop()
{
    if (g_SIPComm != NULL)
    {
        g_SIPComm->StopSip();
        sleep(2);
        delete g_SIPComm;
    }
    g_SIPComm = NULL;
}

3.回调函数callback.h

/*
===============================================================
    GBT28181 SIP组件libGBT28181SipComponent.so注册实现
    作者:程序人生
    博客地址:http://blog.csdn.net/hiwubihe
    QQ:1269122125
    注:请尊重原作者劳动成果,仅供学习使用,请勿盗用,违者必究!
================================================================
*/
#ifndef CALLBACK_H_
#define CALLBACK_H_
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <stdlib.h>
#include "sipserver.h"
#include "IGBT28181Comm.h"
#include "method.h"
using namespace GBT28181::Vsp;
using namespace std;
//回调函数
void_t server_callback(const SipRequestInfo& info, void_t* user);

#endif /* LIBINTERFACE_H_ */

4.callback.cpp 实现文件

/*
===============================================================
    GBT28181 SIP组件libGBT28181SipComponent.so注册实现
    作者:程序人生
    博客地址:http://blog.csdn.net/hiwubihe
    QQ:1269122125
    注:请尊重原作者劳动成果,仅供学习使用,请勿盗用,违者必究!
================================================================
*/
#include "callback.h"
#include "algorithm.h"

//客户端主动请求,服务器端回调
const char * client_request_method[] =
{
        "GBT28181.Vsp.Sip.SipMethod.Register",
        "GBT28181.Vsp.Sip.SipMethod.Notify",
        "GBT28181.Vsp.Sip.SipMethod.Subscribenotify"
};
//打印SIP消息
static void SIP_PKG_Print(const SipRequestInfo& infomation)
{
    SipRegisterContextInfo* info = (SipRegisterContextInfo*) &infomation;
    cout << "\n"
            << "**************************************************"
            << "**************************"
            << endl;
    cout << "packet receive " << endl;
    cout << "status :" << info->status << endl;
    cout << "sipRequestId :" << info->sipRequestId << endl;
    cout << "requestId :" << info->requestId << endl;
    cout << "method :" << info->method << endl;
    cout << "from :" << info->from << endl;
    cout << "proxy :" << info->proxy << endl;
    cout << "contact :" << info->contact << endl;
    cout << "handle :" << info->handle << endl;
    cout << "sipIp :" << info->sipIp << endl;
    cout << "sipPort :" << info->sipPort << endl;
    cout << "subscribeEvent :" << info->subscribeEvent << endl;
    cout << "expires :" << info->expires << endl;
    cout << "content :" << info->content << endl;
    cout<<"Call ID:"<<info->callid<<endl;
    if (!info->registerInfo.userName.empty())
    {
        cout<<"********************************************"<<endl;
        cout<<"authendication infomation as follows:"<<endl;
        cout<<"username:"<<info->registerInfo.userName<<endl;
        cout<<"algorithm:"<<info->registerInfo.algorithm<<endl;
        cout<<"Realm:"<<info->registerInfo.digestRealm<<endl;
        cout<<"nonce:"<<info->registerInfo.nonce<<endl;
        cout<<"response:"<<info->registerInfo.response<<endl;
        cout<<"uri:"<<info->registerInfo.uri<<endl;
    }
    cout
            << "**************************************************"
            << "**************************"
            << endl;
}
static void_t register_response(const GBT28181::Vsp::SipRequestInfo& info,
        void_t* user)
{
    cout << "receive register request packet from client" << endl;
    SIP_PKG_Print(info);
    char temp[16];
    SipRegisterContextInfo* regInfo = (SipRegisterContextInfo*) &info;
    SipRegisterContextInfo repinfo;
    repinfo.sipRequestId = info.sipRequestId;
    repinfo.from = info.proxy;
    repinfo.proxy = info.from;
    repinfo.method = info.method;
    //repinfo.expires = 300;
    repinfo.registerInfo.nonce = "9bd055";
    sscanf(info.contact.c_str(), "%*[^@]@%[^:]", temp);
    repinfo.registerInfo.digestRealm = temp;
    sscanf(info.proxy.c_str(), "%*[^@]@%[^:]", temp);
    repinfo.sipIp = temp;
    sscanf(info.proxy.c_str(), "%*[^:]:%s", temp);
    repinfo.sipPort = atoi(temp);
    repinfo.registerInfo.userName = regInfo->registerInfo.userName;
    repinfo.content="sfsdfsdf";
    GBT28181::Vsp::IGBT28181Comm* p_this = (GBT28181::Vsp::IGBT28181Comm*) user;
    if (repinfo.registerInfo.userName.empty())
    {
        cout<<"this register packet is unauthendicatin"<<endl;
        cout<<"send 401"<<endl;
        repinfo.status = "401";
        p_this->Downcast(repinfo);
    }
    else
    {
        cout<<"this register packet is authendicatin"<<endl;
        //验证
        HASHHEX HA1;
        HASHHEX Response;
        DigestCalcHA1(regInfo->registerInfo.algorithm.c_str(),
                regInfo->registerInfo.userName.c_str(),
                regInfo->registerInfo.digestRealm.c_str(), UAC_PASSWD,
                regInfo->registerInfo.nonce.c_str(), NULL, HA1);
        DigestCalcResponse(HA1, regInfo->registerInfo.nonce.c_str(),
                NULL, NULL, NULL, 0, "REGISTER",
                regInfo->registerInfo.uri.c_str(),
                NULL, Response);
        if (!strcmp(Response, regInfo->registerInfo.response.c_str()))
        {
            cout<<"认证成功发送 200 OK!!!"<<endl;
            repinfo.expires = 5;
            repinfo.status = "200";
        }
        else
        {
            cout<<"认证失败发送 404 OK!!!"<<endl;
            repinfo.expires = 5;
            repinfo.status = "404";
        }
        p_this->Downcast(repinfo);
    }
}
//
void_t server_callback(const SipRequestInfo& info, void_t* user)
{
    //注册报文的情况,调用注册回调
    if (strncmp(info.method.c_str(), client_request_method[0], strlen(
            client_request_method[0])) == 0)
    {
        register_response(info, user);
    }
    //其他情况报文
    else
    {
        cout << "server receive wrong packer" << endl;
        SIP_PKG_Print(info);
        exit(1);
    }
}

5.sip认证 algorithm.h

/*
===============================================================
    GBT28181 SIP组件libGBT28181SipComponent.so注册实现
    作者:程序人生
    博客地址:http://blog.csdn.net/hiwubihe
    QQ:1269122125
    注:请尊重原作者劳动成果,仅供学习使用,请勿盗用,违者必究!
================================================================
*/
#ifndef B_REGISTER__ALGORITHM_H_
#define B_REGISTER__ALGORITHM_H_
#include <stdio.h>
#include "osip_md5.h"
#define HASHLEN 16
typedef char HASH[HASHLEN];
#define HASHHEXLEN 32
typedef char HASHHEX[HASHHEXLEN + 1];

void DigestCalcHA1(const char *pszAlg, const char *pszUserName,
        const char *pszRealm, const char *pszPassword,
        const char *pszNonce, const char *pszCNonce,
        HASHHEX SessionKey);
void DigestCalcResponse(HASHHEX HA1, const char *pszNonce,
        const char *pszNonceCount, const char *pszCNonce,
        const char *pszQop, int Aka, const char *pszMethod,
        const char *pszDigestUri, HASHHEX HEntity, HASHHEX Response);
#endif /* B_REGISTER__ALGORITHM_H_ */

5.sip认证 algorithm.cpp,这部分参考代码可以在exosip2源代码中找到。

/*
===============================================================
    GBT28181 SIP组件libGBT28181SipComponent.so注册实现
    作者:程序人生
    博客地址:http://blog.csdn.net/hiwubihe
    QQ:1269122125
    注:请尊重原作者劳动成果,仅供学习使用,请勿盗用,违者必究!
================================================================
*/
#include "algorithm.h"
#include "string.h"
static void CvtHex(HASH Bin, HASHHEX Hex)
{
    unsigned short i;
    unsigned char j;
    for (i = 0; i < HASHLEN; i++)
    {
        j = (Bin[i] >> 4) & 0xf;
        if (j <= 9)
            Hex[i * 2] = (j + '0');
        else
            Hex[i * 2] = (j + 'a' - 10);
        j = Bin[i] & 0xf;
        if (j <= 9)
            Hex[i * 2 + 1] = (j + '0');
        else
            Hex[i * 2 + 1] = (j + 'a' - 10);
    };
    Hex[HASHHEXLEN] = '\0';
}
void DigestCalcHA1(const char *pszAlg, const char *pszUserName,
        const char *pszRealm, const char *pszPassword,
        const char *pszNonce, const char *pszCNonce,
        HASHHEX SessionKey)
{
    osip_MD5_CTX Md5Ctx;
    HASH HA1;
    osip_MD5Init(&Md5Ctx);
    osip_MD5Update(&Md5Ctx, (unsigned char *) pszUserName, strlen(pszUserName));
    osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1);
    osip_MD5Update(&Md5Ctx, (unsigned char *) pszRealm, strlen(pszRealm));
    osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1);
    osip_MD5Update(&Md5Ctx, (unsigned char *) pszPassword, strlen(pszPassword));
    osip_MD5Final((unsigned char *) HA1, &Md5Ctx);
    if ((pszAlg != NULL) && strcmp(pszAlg, "md5-sess") == 0)
    {
        osip_MD5Init(&Md5Ctx);
        osip_MD5Update(&Md5Ctx, (unsigned char *) HA1, HASHLEN);
        osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1);
        osip_MD5Update(&Md5Ctx, (unsigned char *) pszNonce, strlen(pszNonce));
        osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1);
        osip_MD5Update(&Md5Ctx, (unsigned char *) pszCNonce, strlen(pszCNonce));
        osip_MD5Final((unsigned char *) HA1, &Md5Ctx);
    }
    CvtHex(HA1, SessionKey);
}
void DigestCalcResponse(HASHHEX HA1, const char *pszNonce,
        const char *pszNonceCount, const char *pszCNonce,
        const char *pszQop, int Aka, const char *pszMethod,
        const char *pszDigestUri, HASHHEX HEntity, HASHHEX Response)
{
    osip_MD5_CTX Md5Ctx;
    HASH HA2;
    HASH RespHash;
    HASHHEX HA2Hex;
    /* calculate H(A2) */
    osip_MD5Init(&Md5Ctx);
    osip_MD5Update(&Md5Ctx, (unsigned char *) pszMethod, strlen(pszMethod));
    osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1);
    osip_MD5Update(&Md5Ctx, (unsigned char *) pszDigestUri,
            strlen(pszDigestUri));
    if (pszQop == NULL)
    {
        goto auth_withoutqop;
    }
    else if (0 == strcmp(pszQop, "auth-int"))
    {
        goto auth_withauth_int;
    }
    else if (0 == strcmp(pszQop, "auth"))
    {
        goto auth_withauth;
    }
    auth_withoutqop: osip_MD5Final((unsigned char *) HA2, &Md5Ctx);
    CvtHex(HA2, HA2Hex);
    /* calculate response */
    osip_MD5Init(&Md5Ctx);
    osip_MD5Update(&Md5Ctx, (unsigned char *) HA1, HASHHEXLEN);
    osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1);
    osip_MD5Update(&Md5Ctx, (unsigned char *) pszNonce, strlen(pszNonce));
    osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1);
    goto end;
    auth_withauth_int:
    osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1);
    osip_MD5Update(&Md5Ctx, (unsigned char *) HEntity, HASHHEXLEN);
    auth_withauth: osip_MD5Final((unsigned char *) HA2, &Md5Ctx);
    CvtHex(HA2, HA2Hex);
    /* calculate response */
    osip_MD5Init(&Md5Ctx);
    osip_MD5Update(&Md5Ctx, (unsigned char *) HA1, HASHHEXLEN);
    osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1);
    osip_MD5Update(&Md5Ctx, (unsigned char *) pszNonce, strlen(pszNonce));
    osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1);
    if (Aka == 0)
    {
        osip_MD5Update(&Md5Ctx, (unsigned char *) pszNonceCount, strlen(
                pszNonceCount));
        osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1);
        osip_MD5Update(&Md5Ctx, (unsigned char *) pszCNonce, strlen(pszCNonce));
        osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1);
        osip_MD5Update(&Md5Ctx, (unsigned char *) pszQop, strlen(pszQop));
        osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1);
    }
    end: osip_MD5Update(&Md5Ctx, (unsigned char *) HA2Hex, HASHHEXLEN);
    osip_MD5Final((unsigned char *) RespHash, &Md5Ctx);
    CvtHex(RespHash, Response);
} 

 由于我采用的是MD5验证,所以还需要从exosip2代码中引用osip_md5.h 文件。也可以直接把osip_md5.h和osip_md5.cpp拷到你的工程中,我就采用这个方法。

 

6.主程序 sipserver.h,该测试程序中,UAS值管理一个UAC 地址编码为100110000201000000 密码123456,UAS端口写死5060,地址编码写死100110000000000000

  /*
===============================================================
    GBT28181 SIP组件libGBT28181SipComponent.so注册实现
    作者:程序人生
    博客地址:http://blog.csdn.net/hiwubihe
    QQ:1269122125
    注:请尊重原作者劳动成果,仅供学习使用,请勿盗用,违者必究!
================================================================
*/
#ifndef SIPCLIENT_H_
#define SIPCLIENT_H_
#include "IGBT28181Comm.h"
using namespace GBT28181::Vsp;

//默认端口
#define UASPORT (5060)
//#define UACPORT (5061)
//UAS自己地址编码 具体地址编码什么含义参考标准
#define UASADD_CODE ("100110000000000000")
//UAC地址编码 地址编码相当于用户名
//实际应用中每个UAS保存该UAS所管理的一大堆UAC地址编码以及用户密码等信息 用于注册验证
//当前测试UAS只管理一个UAC 地址编码如下
#define UACADD_CODE ("100110000201000000")
#define UAC_PASSWD ("123456")
//该枚举类型列举类UAS角色 所主动请求的方法
//
typedef enum
{
    INVITE, ACK, MESSAGE, BYE, SUBSCRIBE, CALLMESSAGE,
} METHOD;

//通信实体对 UAS服务器的IP与端口
typedef struct
{
    char local_addr[16];
    int local_port;
} COMM_PAIR;
 
#endif /* SIPCLIENT_H_ */

7.sipserver.cpp,当前只支持启动服务,退出服务功能。其他功能后续添加。

  /*
===============================================================
    GBT28181 SIP组件libGBT28181SipComponent.so注册实现
    作者:程序人生
    博客地址:http://blog.csdn.net/hiwubihe
    QQ:1269122125
    注:请尊重原作者劳动成果,仅供学习使用,请勿盗用,违者必究!
================================================================
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include "sipserver.h"
#include "method.h"
using namespace std;
static void usage()
{
    const char
            *b =        "-------------------------------------------------------------------------------\n"
                        "SIP Library test process - sipserver v 1.0 (June 13, 2014)\n\n"
                        "Author: 程序人生\n\n"
                        "博客地址:http://blog.csdn.net/hiwubihe QQ:1269122125\n\n"
                        "-------------------------------------------------------------------------------\n"
                        "\n"
                        "-s            local server ipv4 address\n"
                        "-h            Print this help and exit\n\n"
                        "-------------------------------------------------------------------------------\n"
                        "\n"
                        "example1: ./sipserver -s127.0.0.1\n"
                        "example2: ./sipserver -h\n"
                        "server default port|server address code|client default port|client address code\n"
                        "5060               |100110000000000000 |5061               |100110000201000000 \n"
                        "-------------------------------------------------------------------------------\n"
                        "\n";
    fprintf(stderr, b, strlen(b));
}
static void help()
{
    const char
            *b =        "-------------------------------------------------------------------------------\n"
                        "SIP Library test process - sipserver v 1.0 (June 13, 2014)\n\n"
                        "Current test Register method,only number 6 7 8 9 is useful\n\n"
                        "Author: 程序人生\n\n"
                        "博客地址:http://blog.csdn.net/hiwubihe QQ:1269122125\n\n"
                        "-------------------------------------------------------------------------------\n"
                        "\n"
                        "              0:Invite\n"
                        "              1:Ack\n"
                        "              2:Message\n"
                        "              3:Bye\n"
                        "              4:Subscribe\n"
                        "              5:CallMessage  unused\n"
                        "              6:start service\n"
                        "              7:stop service\n"
                        "              8:clear scream\n"
                        "              9:exit\n"
                        "-------------------------------------------------------------------------------\n"
                        "\n";
    fprintf(stderr, b, strlen(b));
    cout << "please select method :";
}
int main(int argc, char*argv[])
{
    int ch;
    /*METHOD meth;*/
    COMM_PAIR comm_entry;
    comm_entry.local_port = UASPORT;
    opterr = 0;
    if (argc == 2)
    {
        while ((ch = getopt(argc, argv, "s:h:")) != -1)
        {
            switch (ch)
            {
            case 's':
            {
                strcpy(comm_entry.local_addr, optarg);
                break;
            }
            case 'h':
            {
                usage();
                return EXIT_SUCCESS;
                break;
            }
            default:
            {
                fprintf(stderr, "Illegal argument \n");
                usage();
                return EXIT_FAILURE;
            }
            }
        }
    }
    else
    {
        fprintf(stderr, "Illegal argument \n");
        usage();
        return EXIT_FAILURE;
    }
    if (system("clear") < 0)
    {
        cout << "clear scream error" << endl;
        exit(0);
    }
    help();
    ch = getchar();
    getchar();
    while (1)
    {
        switch (ch)
        {
        case '6':
            //启动服务
            if (server_start(&comm_entry) < 0)
            {
                cout << "service start failure" << endl;
                break;
            }
            cout << "service start success ......" << endl;
            cout << "uas address :" << comm_entry.local_addr << "    uas port :"
                    << comm_entry.local_port << "    address code :" << UASADD_CODE
                    << endl;
            break;
        case '7':
            cout << "stop service......" << endl;
            server_stop();
            break;
        case '8':
            if (system("clear") < 0)
            {
                cout << "clear scream error" << endl;
                exit(1);
            }
            break;
        case '9':
            cout << "exit sipserver......" << endl;
            getchar();
            exit(0);
        default:
            cout << "select error" << endl;
            break;
        }
        cout << "press any key to continue......" << endl;
        getchar();
        help();
        ch = getchar();
        getchar();
    }
    return 0;
}

 

UAC_test 代码如下:

这部分实现简单,只是演示,包含一个文件UAC_test.cpp,UAC地址编码100110000201000000,端口5061写死。UAS端口写死5060。

  /*
===============================================================
    GBT28181 SIP组件libGBT28181SipComponent.so注册实现
    作者:程序人生
    博客地址:http://blog.csdn.net/hiwubihe
    QQ:1269122125
    注:请尊重原作者劳动成果,仅供学习使用,请勿盗用,违者必究!
================================================================
*/
#include <iostream>
#include <string.h>
#include <stdio.h>
#include "IGBT28181Comm.h"
using namespace GBT28181::Vsp;
using namespace std;
//默认端口
#define UASPORT (5060)
#define UACPORT (5061)
static char pwd[20];
//通信实体对
typedef struct
{
    char local_addr[16];
    char remote_addr[16];
    int local_port;
    int remote_port;
} COMM_PAIR;
static void_t SIP_PKG_Print(const SipRequestInfo& info)
{
    cout << "packet receive :" << endl;
    cout
            << "****************************************************************************"
            << endl;
    cout << "status :" << info.status << endl;
    cout << "sipRequestId :" << info.sipRequestId << endl;
    cout << "requestId :" << info.requestId << endl;
    cout << "method :" << info.method << endl;
    cout << "from :" << info.from << endl;
    cout << "proxy :" << info.proxy << endl;
    cout << "contact :" << info.contact << endl;
    cout << "content :" << info.content << endl;
    cout << "status :" << info.status << endl;
    cout << "handle :" << info.handle << endl;
    cout << "sipIp :" << info.sipIp << endl;
    cout << "sipPort :" << info.sipPort << endl;
    cout << "subscribeEvent :" << info.subscribeEvent << endl;
    cout << "expires :" << info.expires << endl;
    cout
            << "****************************************************************************"
            << endl;
}
//消息处理回调函数
void_t SIP_PKG_Receive(const SipRequestInfo& info, void_t* user)
{
    //打印从服务器接收的消息
    SIP_PKG_Print(info);
    //UAC向服务器发送响应报文
    char buf[1024];
    const char*mthd=info.method.data();
    snprintf(buf, 1024, "response from client,for test method:%s", mthd);
    if(memcmp(mthd,"Nari.Vsp.Sip.SipMethod.Register",strlen("Nari.Vsp.Sip.SipMethod.Register"))==0)
    {
        return ;
    }
}

void Communicator_init(void*comm,IGBT28181Comm *SIPComm)
{
    if (NULL != SIPComm)
    {
        delete SIPComm;
    }
    SIPComm = new IGBT28181Comm(false);
    //回调函数
    SIPComm->SetResponseCallback(SIP_PKG_Receive, (void_t*) SIPComm);
    //启动SIP协议
    COMM_PAIR *comm_entry = (COMM_PAIR *) comm;
    SIPComm->StartSip(comm_entry->local_addr, comm_entry->local_port);
}

//客户端主动发送向服务器发送注册报文
void sip_register(IGBT28181Comm* uac, COMM_PAIR *entry)
{
    cout << "enter" << endl;
    SipRegisterContextInfo info;
    char temp[128];
    // 失效时间
    info.expires = 300;
    // 用户100110000201000000 在 entry->remote_addr 远程IP上
    snprintf(temp, 128, "100110000201000000@%s:%d", entry->remote_addr,
            entry->remote_port);
    info.from = temp;
    info.proxy = temp;
    // 发起会话的方式
    info.method = SipMethod::METHOD_REGISTER;
    // 本地ip
    snprintf(temp, 128, "100110000201000000@%s:%d", entry->local_addr,
            entry->local_port);
    //info.proxy = temp;
    // contact
    //info.contact = info.from;
    info.contact = temp;
    // 端口
    info.sipPort = entry->local_port;
    snprintf(temp, 128, "100110000201000000@%s", entry->local_addr);
    info.sipIp = temp;
    info.requestId = "0";
    info.sipRequestId = "0";
    info.registerInfo.userName = "100110000201000000";
    info.registerInfo.response = pwd;
    if (NULL != uac)
    {
        uac->Downcast(info);
        cout << "downcast success" << endl;
    }
    else
    {
        cout << "downcast failure" << endl;
    }
}
 
int main(int argc, char*argv[])
{
    if(argc!=4)
    {
        cout<<"usage: ./UAC_test 127.0.0.1 127.0.0.1 123456"<<endl;
        return 1;
    }
    COMM_PAIR comm_entry;
    comm_entry.local_port = UACPORT;
    comm_entry.remote_port = UASPORT;
    strcpy(comm_entry.local_addr, argv[1]);
    strcpy(comm_entry.remote_addr, argv[2]);
    strcpy(pwd, argv[3]);
    IGBT28181Comm *SIPComm=NULL;
    SIPComm = new IGBT28181Comm(false);
    //回调函数
    SIPComm->SetResponseCallback(SIP_PKG_Receive, (void_t*) SIPComm);
    //启动SIP协议
    SIPComm->StartSip(comm_entry.local_addr, comm_entry.local_port);
    //向服务器发送注册报文
    sip_register(SIPComm, &comm_entry);
    while(1)
    {
        sleep(5);
    }
}

 

六.测试

笔者用的环境为centos 6.0 32bit系统。成功启动的前提是上一节所讲的libGBT28181SipComponent.so必须拷到系统库目录下,或者设置LD_LIBRARY_PATH环境变量。同时安装libGBT28181SipComponent.so库所依赖的库。可以用ldd UAS_test 查看程序依赖的库以及哪些库找不到。

1.启动UAS

 

2.启动后效果

基于GBT28181:SIP协议组件开发-----------第三篇SIP注册流程分析实现_第9张图片

3.填写6 start service后效果

此时,UAS注册服务程序已经成功启动。等待UAC的注册。

4.笔者UAS_test和UAC_test是在一台机器上测试的。格式UAC_test 本机IP UASIP 密码

5.启动后UAS_test收到未鉴权报文

基于GBT28181:SIP协议组件开发-----------第三篇SIP注册流程分析实现_第10张图片

从打印的报文看,没有用户名等信息,同时提示发送了401回复报文

6.UAC_test收到401报文如下

基于GBT28181:SIP协议组件开发-----------第三篇SIP注册流程分析实现_第11张图片

可以看到UAC_test已经成功接受401报文,准备发送具有鉴权信息给UAS_test

7.UAS_test收到鉴权信息

基于GBT28181:SIP协议组件开发-----------第三篇SIP注册流程分析实现_第12张图片

可以看到有用户名等鉴权信息。UAS_test鉴权后,发现用户合法,给与回复200ok!

8.UAC_test收到200OK

基于GBT28181:SIP协议组件开发-----------第三篇SIP注册流程分析实现_第13张图片

整个验证过程结束。

9.其实验证还用很多情况,标准中都有定义,发送什么响应码。如密码错误响应404错误码。上例中

./UAC_test 192.168.50.57 192.168.50.57 12345,密码不正确。将会收到404报文。

基于GBT28181:SIP协议组件开发-----------第三篇SIP注册流程分析实现_第14张图片

关于SIP注册怎么调用exosip2的接口实现,有空再整理。

本文源码下载,正在整理中。。。。。。。。。。。。。。。。

 

你可能感兴趣的:(linux,c/c++,SIP,so库文件,eXosip2,GBT28181,Osip2)