概述
本文仅总结关于GB28181下的注册、心跳维持等与推流拉流相配合的SIP信令,主要基于eXosip库实现;其中搭建信令服务器参考了开源代码以及B站up北小菜,文章结尾有链接
主要逻辑梳理
- 配置自身SIP服务器,同时配置自己想要访问的SIP服务器
- 主事件循环逻辑
- 设计一个循环,不断的接收请求的事件
- 根据不同请求,进行不同的处理
- 事件处理逻辑
- REGISTER 请求处理
- MESSAGE 请求处理
- INVITE 请求处理
- INVITE 响应处理
- BYE 请求处理
框架分析
事件循环的基本框架
cpp
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) 分发处理
- 最后释放该事件即可
事件处理逻辑
事件处理逻辑的总体框架
cpp
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;
}
新呼叫
打印消息到日志上
代码实现
cpp
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信息输入到到日志中
cpp
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
cpp
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响应
cpp
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防止重放攻击的随机值
cpp
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, ®);
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 请求的应答事件
仅输出到日志或者客户端
cpp
case EXOSIP_MESSAGE_ANSWERED:
this->dump_request(evtp); // 代码行 135
break;
MESSAGE 请求发送失败事件
cpp
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 响应)
- 记录日志的同时,发送响应信息
cpp
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);
}
}