云计算百科
云计算领域专业知识百科平台

【GB28181】 SIP信令服务器

概述

本文仅总结关于GB28181下的注册、心跳维持等与推流拉流相配合的SIP信令,主要基于eXosip库实现;其中搭建信令服务器参考了开源代码以及B站up北小菜,文章结尾有链接

主要逻辑梳理

  • 配置自身SIP服务器,同时配置自己想要访问的SIP服务器
  • 主事件循环逻辑
    • 设计一个循环,不断的接收请求的事件
    • 根据不同请求,进行不同的处理
  • 事件处理逻辑
    • REGISTER 请求处理
    • MESSAGE 请求处理
    • INVITE 请求处理
    • INVITE 响应处理
    • BYE 请求处理

框架分析

事件循环的基本框架

class SipServer {
public:
void loop(); // 服务器主循环
private:
int sip_event_handle(eXosip_event_t *evtp); // SIP 事件处理函数
struct eXosip_t *mSipCtx; // eXosip 上下文
bool mQuit; // 退出标志
};

void SipServer::loop() {
if (this->init_sip_server() != 0) {
return;
}
while (!mQuit) {
eXosip_event_t *evtp = eXosip_event_wait(mSipCtx, 0, 20); // 等待 SIP 事件
if (!evtp) {
eXosip_automatic_action(mSipCtx); // 处理超时
osip_usleep(100000);
continue;
}
eXosip_automatic_action(mSipCtx); // 执行 eXosip 自动操作
this->sip_event_handle(evtp); // 分发和处理事件
eXosip_event_free(evtp); // 释放事件内存
}
}

int SipServer::sip_event_handle(eXosip_event_t *evtp) {
switch (evtp->type) {
case EXOSIP_CALL_MESSAGE_NEW: // 14
// 处理 EXOSIP_CALL_MESSAGE_NEW 事件
LOGI("EXOSIP_CALL_MESSAGE_NEW type=%d", evtp->type);
this->dump_request(evtp);
this->dump_response(evtp);
break;

case EXOSIP_CALL_CLOSED: // 21
// 处理 EXOSIP_CALL_CLOSED 事件
LOGI("EXOSIP_CALL_CLOSED type=%d", evtp->type);
this->dump_request(evtp);
this->dump_response(evtp);
break;

case EXOSIP_CALL_RELEASED: // 22
// 处理 EXOSIP_CALL_RELEASED 事件
LOGI("EXOSIP_CALL_RELEASED type=%d", evtp->type);
this->dump_request(evtp);
this->dump_response(evtp);
this->clearClientMap();
break;

case EXOSIP_MESSAGE_NEW: // 23
// 处理 EXOSIP_MESSAGE_NEW 事件
LOGI("EXOSIP_MESSAGE_NEW type=%d", evtp->type);
if (MSG_IS_REGISTER(evtp->request)) {
this->response_register(evtp);
} else if (MSG_IS_MESSAGE(evtp->request)) {
this->response_message(evtp);
} else if (strncmp(evtp->request->sip_method, "BYE", 3) != 0) {
LOGE("unknown1");
} else {
LOGE("unknown2");
}
break;

case EXOSIP_MESSAGE_ANSWERED:
// 处理 EXOSIP_MESSAGE_ANSWERED 事件
this->dump_request(evtp);
break;

case EXOSIP_MESSAGE_REQUESTFAILURE:
// 处理 EXOSIP_MESSAGE_REQUESTFAILURE 事件
LOGI("EXOSIP_MESSAGE_REQUESTFAILURE type=%d: Receive feedback on sending failure after actively sending a message", evtp->type);
this->dump_request(evtp);
this->dump_response(evtp);
break;

case EXOSIP_CALL_INVITE:
// 处理 EXOSIP_CALL_INVITE 事件
LOGI("EXOSIP_CALL_INVITE type=%d: The server receives the Invite request actively sent by the client", evtp->type);
break;

case EXOSIP_CALL_PROCEEDING: // 5
// 处理 EXOSIP_CALL_PROCEEDING 事件
LOGI("EXOSIP_CALL_PROCEEDING type=%d: When the server receives the Invite (SDP) confirmation reply from the client", evtp->type);
this->dump_request(evtp);
this->dump_response(evtp);
break;

case EXOSIP_CALL_ANSWERED: // 7
// 处理 EXOSIP_CALL_ANSWERED 事件
LOGI("EXOSIP_CALL_ANSWERED type=%d: The server receives an invite (SDP) confirmation reply from the client", evtp->type);
this->dump_request(evtp);
this->dump_response(evtp);
this->response_invite_ack(evtp);
break;

case EXOSIP_CALL_SERVERFAILURE:
// 处理 EXOSIP_CALL_SERVERFAILURE 事件
LOGI("EXOSIP_CALL_SERVERFAILURE type=%d", evtp->type);
break;

case EXOSIP_IN_SUBSCRIPTION_NEW:
// 处理 EXOSIP_IN_SUBSCRIPTION_NEW 事件
LOGI("EXOSIP_IN_SUBSCRIPTION_NEW type=%d", evtp->type);
break;

default:
// 处理未知事件
LOGI("type=%d unknown", evtp->type);
break;
}
return 0;
}

基本逻辑分析

该处仅仅说明事件循环,在事件处理逻辑对于事件的处理逻辑再进行详细说明

  • 事件循环(loop)
    • 负责初始化服务器 (init_sip_server())
    • 在一个 while 循环中不断等待事件
    • 如果超时没有事件,执行 eXosip_automatic_action() 和休眠
    • 如果接收到了事件evtp,那么就调用 sip_event_handle(evtp) 分发处理
    • 最后释放该事件即可

事件处理逻辑

 事件处理逻辑的总体框架

int SipServer::sip_event_handle(eXosip_event_t *evtp) {
switch (evtp->type) {
case EXOSIP_CALL_MESSAGE_NEW: // 14
// 处理 EXOSIP_CALL_MESSAGE_NEW 事件
LOGI("EXOSIP_CALL_MESSAGE_NEW type=%d", evtp->type);
this->dump_request(evtp);
this->dump_response(evtp);
break;

case EXOSIP_CALL_CLOSED: // 21
// 处理 EXOSIP_CALL_CLOSED 事件
LOGI("EXOSIP_CALL_CLOSED type=%d", evtp->type);
this->dump_request(evtp);
this->dump_response(evtp);
break;

case EXOSIP_CALL_RELEASED: // 22
// 处理 EXOSIP_CALL_RELEASED 事件
LOGI("EXOSIP_CALL_RELEASED type=%d", evtp->type);
this->dump_request(evtp);
this->dump_response(evtp);
this->clearClientMap();
break;

case EXOSIP_MESSAGE_NEW: // 23
// 处理 EXOSIP_MESSAGE_NEW 事件
LOGI("EXOSIP_MESSAGE_NEW type=%d", evtp->type);
if (MSG_IS_REGISTER(evtp->request)) {
this->response_register(evtp);
} else if (MSG_IS_MESSAGE(evtp->request)) {
this->response_message(evtp);
} else if (strncmp(evtp->request->sip_method, "BYE", 3) != 0) {
LOGE("unknown1");
} else {
LOGE("unknown2");
}
break;

case EXOSIP_MESSAGE_ANSWERED:
// 处理 EXOSIP_MESSAGE_ANSWERED 事件
this->dump_request(evtp);
break;

case EXOSIP_MESSAGE_REQUESTFAILURE:
// 处理 EXOSIP_MESSAGE_REQUESTFAILURE 事件
LOGI("EXOSIP_MESSAGE_REQUESTFAILURE type=%d: Receive feedback on sending failure after actively sending a message", evtp->type);
this->dump_request(evtp);
this->dump_response(evtp);
break;

case EXOSIP_CALL_INVITE:
// 处理 EXOSIP_CALL_INVITE 事件
LOGI("EXOSIP_CALL_INVITE type=%d: The server receives the Invite request actively sent by the client", evtp->type);
break;

case EXOSIP_CALL_PROCEEDING: // 5
// 处理 EXOSIP_CALL_PROCEEDING 事件
LOGI("EXOSIP_CALL_PROCEEDING type=%d: When the server receives the Invite (SDP) confirmation reply from the client", evtp->type);
this->dump_request(evtp);
this->dump_response(evtp);
break;

case EXOSIP_CALL_ANSWERED: // 7
// 处理 EXOSIP_CALL_ANSWERED 事件
LOGI("EXOSIP_CALL_ANSWERED type=%d: The server receives an invite (SDP) confirmation reply from the client", evtp->type);
this->dump_request(evtp);
this->dump_response(evtp);
this->response_invite_ack(evtp);
break;

case EXOSIP_CALL_SERVERFAILURE:
// 处理 EXOSIP_CALL_SERVERFAILURE 事件
LOGI("EXOSIP_CALL_SERVERFAILURE type=%d", evtp->type);
break;

case EXOSIP_IN_SUBSCRIPTION_NEW:
// 处理 EXOSIP_IN_SUBSCRIPTION_NEW 事件
LOGI("EXOSIP_IN_SUBSCRIPTION_NEW type=%d", evtp->type);
break;

default:
// 处理未知事件
LOGI("type=%d unknown", evtp->type);
break;
}
return 0;
}

新呼叫

打印消息到日志上

代码实现

case EXOSIP_CALL_CLOSED://21
LOGI("EXOSIP_CALL_CLOSED type=%d",evtp->type); // 代码行 109
this->dump_request(evtp); // 代码行 110
this->dump_response(evtp); // 代码行 111
break;

// 写入日志函数实现
void SipServer::dump_request(eXosip_event_t *evtp) {
char *s;
size_t len;
osip_message_to_str(evtp->request, &s, &len);
LOGI("\\nprint request start\\ntype=%d\\n%s\\nprint request end\\n",evtp->type,s);
}
void SipServer::dump_response(eXosip_event_t *evtp) {
char *s;
size_t len;
osip_message_to_str(evtp->response, &s, &len);
LOGI("\\nprint response start\\ntype=%d\\n%s\\nprint response end\\n",evtp->type,s);
}

呼叫关闭

实现同新呼叫,仅仅是将sip信息输入到到日志中

case EXOSIP_CALL_CLOSED://21
LOGI("EXOSIP_CALL_CLOSED type=%d",evtp->type); // 代码行 109
this->dump_request(evtp); // 代码行 110
this->dump_response(evtp); // 代码行 111
break;

呼叫释放事件

  • 将消息内容记录到日志
  • 调用 clearClientMap() 函数,清空客户端列表 mClientMap

新的消息请求事件

  • 判断新消息的类型
    • 如果是注册消息,则调用注册逻辑
    • 如果是即时消息,那么解析该消息,然后回复200OK

case EXOSIP_MESSAGE_NEW://23
LOGI("EXOSIP_MESSAGE_NEW type=%d",evtp->type); // 代码行 121

if (MSG_IS_REGISTER(evtp->request)) { // 代码行 123
this->response_register(evtp); // 代码行 124
}
else if (MSG_IS_MESSAGE(evtp->request)) { // 代码行 126
this->response_message(evtp); // 代码行 127
}
else if(strncmp(evtp->request->sip_method, "BYE", 3) != 0){ // 代码行 129
LOGE("unknown1"); // 代码行 130
}
else{
LOGE("unknown2"); // 代码行 132
}
break;

注册消息处理

  • 客户端发送一个注册请求,其中会包含授权信息(Authorization头部)
  • 服务器提取请求中的认证信息
  • 服务器计算认证信息是否正确
  • 如果结果一致,那么此时就可以创建一个新的Client对象,然后发送200响应
  • 如果结果不一致,那么就返回401 Unauthorized响应

void SipServer::response_register(eXosip_event_t *evtp) {

// 1. 获取授权信息
osip_authorization_t * auth = nullptr;
osip_message_get_authorization(evtp->request, 0, &auth);

// 2. 获取SIP协议中身份认证信息
if(auth && auth->username){

char *method = NULL, // REGISTER
*algorithm = NULL, // MD5
*username = NULL,// 340200000013200000024
*realm = NULL, // sip服务器传给客户端,客户端携带并提交上来的sip服务域
*nonce = NULL, //sip服务器传给客户端,客户端携带并提交上来的nonce
*nonce_count = NULL,
*uri = NULL; // sip:34020000002000000001@3402000000

osip_contact_t *contact = nullptr;
osip_message_get_contact (evtp->request, 0, &contact);

method = evtp->request->sip_method;
char calc_response[HASHHEXLEN];
HASHHEX HA1, HA2 = "", Response;

#define SIP_STRDUP(field) if (auth->field) (field) = osip_strdup_without_quote(auth->field)

SIP_STRDUP(algorithm);
SIP_STRDUP(username);
SIP_STRDUP(realm);
SIP_STRDUP(nonce);
SIP_STRDUP(nonce_count);
SIP_STRDUP(uri);

DigestCalcHA1(algorithm, username, realm, mInfo->getSipPass(), nonce, nonce_count, HA1);
DigestCalcResponse(HA1, nonce, nonce_count, auth->cnonce, auth->message_qop, 0, method, uri, HA2, Response);

HASHHEX temp_HA1;
HASHHEX temp_response;
DigestCalcHA1("REGISTER", username, mInfo->getSipRealm(), mInfo->getSipPass(), mInfo->getNonce(), NULL, temp_HA1);
DigestCalcResponse(temp_HA1, mInfo->getNonce(), NULL, NULL, NULL, 0, method, uri, NULL, temp_response);
memcpy(calc_response, temp_response, HASHHEXLEN);

Client *client = new Client(strdup(contact->url->host),
atoi(contact->url->port),
strdup(username));

if (!memcmp(calc_response, Response, HASHHEXLEN)) {
this->response_message_answer(evtp,200);
LOGI("Camera registration succee,ip=%s,port=%d,device=%s",client->getIp(),client->getPort(),client->getDevice());

mClientMap.insert(std::make_pair(client->getDevice(),client));

this->request_invite(client->getDevice(),client->getIp(),client->getPort());

}
else {
this->response_message_answer(evtp,401);
LOGI("Camera registration error, p=%s,port=%d,device=%s",client->getIp(),client->getPort(),client->getDevice());

delete client;
}

osip_free(algorithm);
osip_free(username);
osip_free(realm);
osip_free(nonce);
osip_free(nonce_count);
osip_free(uri);
} else {
response_register_401unauthorized(evtp);
}

}

未授权响应处理逻辑(也就是401响应的处理逻辑)

  •  401响应的生成
    • osip_www_authenticate_set_auth_type设置认证类型为Digest
    • osip_www_authenticate_set_realm设置服务域名
    • osip_www_authenticate_set_nonce设置为none防止重放攻击的随机值

void SipServer::response_register_401unauthorized(eXosip_event_t *evtp) {

char *dest = nullptr;
osip_message_t * reg = nullptr;
osip_www_authenticate_t * header = nullptr;

osip_www_authenticate_init(&header);
osip_www_authenticate_set_auth_type (header, osip_strdup("Digest"));
osip_www_authenticate_set_realm(header,osip_enquote(mInfo->getSipRealm()));
osip_www_authenticate_set_nonce(header,osip_enquote(mInfo->getNonce()));
osip_www_authenticate_to_str(header, &dest);
int ret = eXosip_message_build_answer (mSipCtx, evtp->tid, 401, &reg);
if ( ret == 0 && reg != nullptr ) {
osip_message_set_www_authenticate(reg, dest);
osip_message_set_content_type(reg, "Application/MANSCDP+xml");
eXosip_lock(mSipCtx);
eXosip_message_send_answer (mSipCtx, evtp->tid,401, reg);
eXosip_unlock(mSipCtx);
LOGI("response_register_401unauthorized success");
}else {
LOGI("response_register_401unauthorized error");
}

osip_www_authenticate_free(header);
osip_free(dest);

}

MESSAGE 请求的应答事件

仅输出到日志或者客户端

case EXOSIP_MESSAGE_ANSWERED:
this->dump_request(evtp); // 代码行 135
break;

MESSAGE 请求发送失败事件

case EXOSIP_MESSAGE_REQUESTFAILURE:
LOGI("EXOSIP_MESSAGE_REQUESTFAILURE type=%d: Receive feedback on sending failure after actively sending a message", evtp->type); // 代码行 138
this->dump_request(evtp); // 代码行 139
this->dump_response(evtp); // 代码行 140
break;

呼叫应答事件 (2xx 响应)

  • 记录日志的同时,发送响应信息

case EXOSIP_CALL_ANSWERED:// 7
LOGI("EXOSIP_CALL_ANSWERED type=%d: The server receives an invite (SDP) confirmation reply from the client", evtp->type); // 代码行 151
this->dump_request(evtp); // 代码行 152
this->dump_response(evtp); // 代码行 153
this->response_invite_ack(evtp); // 代码行 155
break;

void SipServer::response_invite_ack(eXosip_event_t *evtp){

osip_message_t* msg = nullptr;
int ret = eXosip_call_build_ack(mSipCtx, evtp->did, &msg);
if (!ret && msg) {
eXosip_call_send_ack(mSipCtx, evtp->did, msg);
} else {
LOGE("eXosip_call_send_ack error=%d", ret);
}

}

参考资料:BXC_SipServer: C++开发的国标GB28181流媒体Sip信令服务器

赞(0)
未经允许不得转载:网硕互联帮助中心 » 【GB28181】 SIP信令服务器
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!