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