GB/T28181平台C++实现学习笔记4: 线程中使用libeXosip库接收设备注册

GB/T28181平台C++实现学习笔记4: 线程中使用libeXosip库接收设备注册

GB28181 基本注册流程

在这里插入图片描述

开启线程池

关于线程池的实现是C++多线程开发的基础,不熟悉最好对相关内容着重学习一下,这里只粘接口,方便下面代码的阅读。

线程任务定义:

typedef std::function<int(void)> ThreadTask;

线程接口

        /**
         * @brief 开启线程池
         * 
         * @param num  线程池数,默认4个
         * @return int 
         */
        int start(int num = 4);

        /**
         * @brief 向线程池塞入执行体
         * 
         * @param task 执行体
         * @return int proc不允许堵塞,返回-1,表示从线程池pop
         */
        int pushTask(const ThreadTask &task);

SIPServer初始化

初始化上下文

void SIPServer::init()
{
    m_excontext = eXosip_malloc();
    int ret = eXosip_init(m_excontext);
    if (ret != OSIP_SUCCESS)
    {
        stl::error("eXosip_init failed, ret=%d", ret);
    }
    stl::info("eXosip_init successfully!");
}

开启监听

void SIPServer::start(const std::string &user_agent)
{
    int ret = eXosip_listen_addr(m_excontext, IPPROTO_UDP, m_sipHost.c_str(), m_sipPort, AF_INET, 0);
    if (ret != OSIP_SUCCESS)
    {
        eXosip_quit(m_excontext);
        stl::error("eXosip_listen_addr failed, ret: ", ret);
        return;
    }

    stl::debug("eXosip start listen[UDP] %s:%d", m_sipHost.c_str(), m_sipPort);

    if (user_agent.length() > 0)
        eXosip_set_user_agent(m_excontext, user_agent.c_str());

    stl::ThreadTask recvTask = std::bind(&SIPServer::DoReceiveEvents, this);
    stl::ThreadTask procTask = std::bind(&SIPServer::DoProcessEvents, this);

    stl::ThreadPool::instance()->pushTask(recvTask);
    stl::ThreadPool::instance()->pushTask(procTask);
}

其中会开启两个线程任务,一个接受sip包,一个处理sip包

接收SIP包生成事件

int SIPServer::DoReceiveEvents()
{
    eXosip_event_t *exosip_event;

    exosip_event = eXosip_event_wait(m_excontext, 0, 1);

    if (exosip_event == nullptr)
        return 0;
    
    sip_event_sptr sipg_event = new_event(m_excontext, exosip_event);
    if (nullptr == sipg_event)
        return 0;

    stl::debug("OnReceiveEvents[%s]", sipg_event->name);

    m_eventQueue.Push(sipg_event);
    stl::debug("Push event: %s, id=%d to queue successfully", sipg_event->name, sipg_event->id);
    return 0;
}

接收到的SIP包,封装成事件,push到事件队列中等待处理

SIP事件处理

int SIPServer::DoProcessEvents()
{
    const char *event_name;
    uint64_t event_id;

    sip_event_sptr sipg_event;
    if (!m_eventQueue.Pop(sipg_event))
        return 0;

    event_name = sipg_event->name;
    event_id = sipg_event->id;

    stl::debug("OnProcessEvents[%s]", sipg_event->name);

    sipg_event->proc(sipg_event);
    eXosip_event_free(sipg_event->exevent);
    return 0;
}

生成事件对象时,直接绑定了事件的处理,所以直接执行事件处理即可sipg_event->proc

事件生成

sip_event_sptr SIPServer::new_event(eXosip_t *exosip_context, eXosip_event_t *exosip_event)
{
    if (exosip_event == nullptr)
        return nullptr;

    if (exosip_event->type < EXOSIP_REGISTRATION_SUCCESS || exosip_event->type > EXOSIP_NOTIFICATION_GLOBALFAILURE)
        return nullptr;

    sip_event_sptr event(new sip_event_t); // = std::make_shared(SipEvent)();

    EventMgr::EventNameProcPair pair = m_eventHandle.getEventProc(exosip_event->type);
    if (pair.name == nullptr)
        return nullptr;

    event->name = pair.name;
    event->proc = pair.proc;
    event->excontext = exosip_context;
    event->exevent = exosip_event;
    event->id = m_eventId++;

    return event;
}

通过事件样式获得事件处理函数
EventMgr::EventNameProcPair pair = m_eventHandle.getEventProc(exosip_event->type);

事件处理函数

接收设备注册,我们仅需要处理一个type:EXOSIP_MESSAGE_NEW,
并且仅处理method:REGISTER的消息

int EventMgr::on_exosip_message_new(const sip_event_sptr &event)
{
    eXosip_event_t *exosip_event = event->exevent;

    if (!strncmp(exosip_event->request->sip_method, "REGISTER", strlen("REGISTER")))
    {
        m_RegisterHandler->HandleRegisterRequest(event);
    }
    else if (!strncmp(exosip_event->request->sip_method, "MESSAGE", strlen("MESSAGE")))
    {
        // m_msghandler.HandleIncomingReq(event);
    }

    return 0;
}

处理注册消息

int HandlerRegister::HandleRegisterRequest(const sip_event_sptr &e)
{
    const char *username = e->exevent->request->from->url->username;
    const char *host = e->exevent->request->from->url->host;
    uint16_t port = std::stoul(std::string(e->exevent->request->from->url->port));

    //鉴权信息
    osip_authorization_t *authentication = nullptr;
    {
        osip_message_get_authorization(e->exevent->request, 0, &authentication);
        if (nullptr == authentication)
        {
            sendWWWAuthenticateResponse(username, e->excontext, e->exevent->tid, SIP_UNAUTHORIZED);
            return 0;
        }
        // TODO digist 摘要认证
        bool isAuth = true;
        if (isAuth)
        {
            sendSimplyResp(username, e->excontext, e->exevent->tid, SIP_OK);
            DBMgr::Device device;
            device.username = std::string(username);
            device.host = std::string(host);
            device.port = port;
            int deviceId = DBMgr::instance()->addDevice(device);
            
            static bool once = false;//TODO 去除重复任务
            if(!once){
                once = true;
                DBMgr::instance()->addTask(deviceId);
            }
        }
        else
        {
            sendWWWAuthenticateResponse(username, e->excontext, e->exevent->tid, SIP_UNAUTHORIZED);
        }
    }
    authentication = nullptr;
    return 0;
}

Digist 摘要认证 省略了,网上能找到

回复401头域WWW-Authenticate

int HandlerRegister::sendWWWAuthenticateResponse(const char *uname, struct eXosip_t *excontext, int tid, int status)
{
    osip_www_authenticate_t *www_auth_test;

    int result = OSIP_SUCCESS;
    osip_message_t *answer = nullptr;

    eXosip_lock(excontext);
    result = eXosip_message_build_answer(excontext, tid, status, &answer);

    if (OSIP_SUCCESS == result)
    {
            osip_www_authenticate_t *www_auth;
            osip_www_authenticate_init(&www_auth);
            osip_www_authenticate_set_auth_type(www_auth, osip_strdup(AUTH_TYPE));
            osip_www_authenticate_set_realm(www_auth, osip_strdup(REALM));
            osip_www_authenticate_set_qop_options(www_auth, osip_strdup(QOP));
            osip_www_authenticate_set_nonce(www_auth, osip_strdup(NONCE));

            char *str_www_auth = NULL;
            osip_www_authenticate_to_str(www_auth, &str_www_auth);
            osip_message_set_www_authenticate(answer, str_www_auth);

            /* 注意释放空间 */
            osip_free(str_www_auth);
            osip_www_authenticate_free(www_auth);
            www_auth = nullptr;

        result = eXosip_message_send_answer(excontext, tid, status, answer);
    }
    else
    {
        result = eXosip_message_send_answer(excontext, tid, status, nullptr);
    }

    eXosip_unlock(excontext);
    return result;
}

测试

抓包测试服务基本注册流程,接下来查询设备信息。

微信号:yjkhtddx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值