EasyGBS國標(GB28181)流媒體服務軟件提供用戶管理及Web可視化頁面管理; 提供設備狀態管理,可實時查看設備是否掉線等信息; 實時流媒體處理,PS(TS)轉ES; 設備狀態監測、雲臺控制、錄像檢索、回放; 提供RTSP、RTMP、HTTP-FLV、HLS等多種協議流輸出; 對外提供服務器獲取狀態、信息,控制等HTTP API接口。c++
今天咱們就來簡單介紹下注冊、心跳和註銷這幾個協議。註冊和註銷很好理解,就是Expires: 0的時候爲註銷,心跳也很簡單就是一個簡單的無應答message。安全
下面將註冊的流程圖簡單摘出來一下:服務器
根據流程圖仍是比較容易讀懂,下面粘貼一下抓包信息:session
REGISTER sip:34020000002000000001@192.168.1.81:5060 SIP/2.0 Via: SIP/2.0/UDP 192.168.1.93:5060;rport;branch=z9hG4bK-3d09000-23b5dfc-TFyCL5ME From: <sip:64000000002000000001@192.168.1.93:5060>;tag=pck2i7pI To: <sip:64000000002000000001@192.168.1.93:5060> Call-ID: b4359b71-eb2f-b6f4-2fce-8fd47e5888ff@192.168.1.93 CSeq: 5 REGISTER Contact: <sip:64000000002000000001@192.168.1.93:5060> Authorization: Digest username="34020000002000000001", realm="3402000000", nonce="bd2e4df9e3d9b280", uri="sip:64000000002000000001@192.168.1.93:5060", response="9c8411f2b96c5aef55eb136ba3f34655", algorithm=MD5 Max-Forwards: 70 User-Agent: iVMS 1.0 Expires: 200 Content-Length: 0 SIP/2.0 401 Unauthorized To: <sip:64000000002000000001@192.168.1.93:5060>;tag=67239569_53173353_7ce4590f-587d-4d44-9690-96498367c675 Via: SIP/2.0/UDP 192.168.1.93:5060;rport=5060;branch=z9hG4bK-3d09000-23b5dfc-TFyCL5ME;received=192.168.1.93 CSeq: 5 REGISTER Call-ID: b4359b71-eb2f-b6f4-2fce-8fd47e5888ff@192.168.1.93 From: <sip:64000000002000000001@192.168.1.93:5060>;tag=pck2i7pI WWW-Authenticate: Digest realm="3402000000",nonce="31ada55697307236" Content-Length: 0 REGISTER sip:34020000002000000001@192.168.1.81:5060 SIP/2.0 Via: SIP/2.0/UDP 192.168.1.93:5060;rport;branch=z9hG4bK-3d09000-3068be5-lnl05y0C From: <sip:64000000002000000001@192.168.1.93:5060>;tag=pck2i7pI To: <sip:64000000002000000001@192.168.1.93:5060> Call-ID: b4359b71-eb2f-b6f4-2fce-8fd47e5888ff@192.168.1.93 CSeq: 6 REGISTER Contact: <sip:64000000002000000001@192.168.1.93:5060> Authorization: Digest username="34020000002000000001", realm="3402000000", nonce="31ada55697307236", uri="sip:64000000002000000001@192.168.1.93:5060", response="3526a5a5dd91a45e19d1e6a65704457f", algorithm=MD5 Max-Forwards: 70 User-Agent: iVMS 1.0 Expires: 200 Content-Length: 0 SIP/2.0 200 OK To: <sip:64000000002000000001@192.168.1.93:5060>;tag=86156704_53173353_d7fb589f-4357-4fae-ae32-47c38d81fff1 Via: SIP/2.0/UDP 192.168.1.93:5060;rport=5060;branch=z9hG4bK-3d09000-3068be5-lnl05y0C;received=192.168.1.93 CSeq: 6 REGISTER Call-ID: b4359b71-eb2f-b6f4-2fce-8fd47e5888ff@192.168.1.93 From: <sip:64000000002000000001@192.168.1.93:5060>;tag=pck2i7pI Contact: <sip:64000000002000000001@192.168.1.93:5060> Expires: 200 Date: 2017-05-19T18:09:17.033 Content-Length:
其中註冊要注意的是,註冊的時候有個回覆時間格式須要進行上下級和設備之間校時功能:架構
MESSAGE sip:34020000002000000001@192.168.1.81:5060 SIP/2.0 Via: SIP/2.0/UDP 192.168.1.93:5060;rport;branch=z9hG4bK-3d09000-2a2b21b-nddru7fy From: <sip:64000000002000000001@192.168.1.93:5060>;tag=UL61Qycy To: <sip:34020000002000000001@192.168.1.81:5060> Call-ID: 5be796e4-b5ab-592e-cc13-64f34ea57528@192.168.1.93 CSeq: 35 MESSAGE Contact: <sip:192.168.1.93:5060> Content-Type: Application/MANSCDP+xml Max-Forwards: 70 User-Agent: iVMS 1.0 Content-Length: 151 <?xml version="1.0"?> <Notify> <CmdType>Keepalive</CmdType> <SN>26</SN> <DeviceID>64000000002000000001</DeviceID> <Status>OK</Status> </Notify> SIP/2.0 200 OK To: <sip:34020000002000000001@192.168.1.81:5060>;tag=69112542_53173353_89bb4bc2-7f74-42a8-a102-6b6490b6daa2 Via: SIP/2.0/UDP 192.168.1.93:5060;rport=5060;branch=z9hG4bK-3d09000-2a2b21b-nddru7fy;received=192.168.1.93 CSeq: 35 MESSAGE Call-ID: 5be796e4-b5ab-592e-cc13-64f34ea57528@192.168.1.93 From: <sip:64000000002000000001@192.168.1.93:5060>;tag=UL61Qycy Content-Length: 0
REGISTER sip:34020000002000000001@192.168.1.81:5060 SIP/2.0 Via: SIP/2.0/UDP 192.168.1.93:5060;rport;branch=z9hG4bK-3d09000-23b5dfc-TFyCL5ME From: <sip:64000000002000000001@192.168.1.93:5060>;tag=pck2i7pI To: <sip:64000000002000000001@192.168.1.93:5060> Call-ID: b4359b71-eb2f-b6f4-2fce-8fd47e5888ff@192.168.1.93 CSeq: 5 REGISTER Contact: <sip:64000000002000000001@192.168.1.93:5060> Authorization: Digest username="34020000002000000001", realm="3402000000", nonce="bd2e4df9e3d9b280", uri="sip:64000000002000000001@192.168.1.93:5060", response="9c8411f2b96c5aef55eb136ba3f34655", algorithm=MD5 Max-Forwards: 70 User-Agent: iVMS 1.0 Expires: 0 Content-Length: 0 SIP/2.0 401 Unauthorized To: <sip:64000000002000000001@192.168.1.93:5060>;tag=67239569_53173353_7ce4590f-587d-4d44-9690-96498367c675 Via: SIP/2.0/UDP 192.168.1.93:5060;rport=5060;branch=z9hG4bK-3d09000-23b5dfc-TFyCL5ME;received=192.168.1.93 CSeq: 5 REGISTER Call-ID: b4359b71-eb2f-b6f4-2fce-8fd47e5888ff@192.168.1.93 From: <sip:64000000002000000001@192.168.1.93:5060>;tag=pck2i7pI WWW-Authenticate: Digest realm="3402000000",nonce="31ada55697307236" Content-Length: 0 REGISTER sip:34020000002000000001@192.168.1.81:5060 SIP/2.0 Via: SIP/2.0/UDP 192.168.1.93:5060;rport;branch=z9hG4bK-3d09000-3068be5-lnl05y0C From: <sip:64000000002000000001@192.168.1.93:5060>;tag=pck2i7pI To: <sip:64000000002000000001@192.168.1.93:5060> Call-ID: b4359b71-eb2f-b6f4-2fce-8fd47e5888ff@192.168.1.93 CSeq: 6 REGISTER Contact: <sip:64000000002000000001@192.168.1.93:5060> Authorization: Digest username="34020000002000000001", realm="3402000000", nonce="31ada55697307236", uri="sip:64000000002000000001@192.168.1.93:5060", response="3526a5a5dd91a45e19d1e6a65704457f", algorithm=MD5 Max-Forwards: 70 User-Agent: iVMS 1.0 Expires: 0 Content-Length: 0 SIP/2.0 200 OK To: <sip:64000000002000000001@192.168.1.93:5060>;tag=86156704_53173353_d7fb589f-4357-4fae-ae32-47c38d81fff1 Via: SIP/2.0/UDP 192.168.1.93:5060;rport=5060;branch=z9hG4bK-3d09000-3068be5-lnl05y0C;received=192.168.1.93 CSeq: 6 REGISTER Call-ID: b4359b71-eb2f-b6f4-2fce-8fd47e5888ff@192.168.1.93 From: <sip:64000000002000000001@192.168.1.93:5060>;tag=pck2i7pI Contact: <sip:64000000002000000001@192.168.1.93:5060> Expires: 0 Date: 2017-05-19T18:09:17.033 Content-Length: 0
下面粘貼一下部分註冊的代碼,因爲使用c++寫的代碼,全部只能裁剪一部分出來:dom
#include <stdlib.h> #include <stdio.h> #include "eXosip2/eXosip.h" #include "osipparser2/osip_uri.h" #include "osipparser2/osip_message.h" #include "eXosip2.h" //初始化 監聽端口 int Init(int listenport) { int iRet = eXosip_init(); if (iRet != OSIP_SUCCESS) { //DBGPrint(M_SipUA, ERROR_LEVEL, "eXosip2 fail!"); //return -1; printf("eXosip2 fail! \n"); exit(1); } iRet = eXosip_listen_addr(IPPROTO_UDP, NULL, listenport, AF_INET, 0); if (iRet != OSIP_SUCCESS) { eXosip_quit(); //DBGPrint(M_SipUA, ERROR_LEVEL, "eXosip2 could not initialize transport layer!"); //return -1; DBGPrint(M_SipUA, BREAK_LEVEL, "eXosip2 could not initialize transport layer! \n"); exit(1); } DBGPrint(M_SipUA, BREAK_LEVEL, "%s: SipSvr Listen Port:%d Sucess!!!!", `ls_function` , listenport); return 0; } /******************************************************************************* * Function: * Digest MD5 authorization validate process. *******************************************************************************/ //MD5鑑權 bool MD5AuthValidate(char *pMethod, osip_authorization_t *pAuthEcho) { if (NULL == pAuthEcho) { DBGPrint(M_SipUA, ERROR_LEVEL, "pAuthField is null pointer!"); return false; } char *pAlgorithm = NULL; char *pUsername = NULL; char *pRealm = NULL; char *pPasswd = NULL; char *pNonce = NULL; char *pNonceCount = NULL; char *pCNonce = NULL; char *pQop = NULL; char *pCMethod = NULL; char *pUri = NULL; char sNullAlg[] = ""; //"MD5"; char sNullQop[] = ""; //"auth"; char SessionKey[MD5_SESSION_KEY_LEN+1] = {0}; char Response[MD5_RESPONSE_LEN] = {0}; char *pResponse2 = NULL; if (NULL == pAuthEcho->algorithm) pAlgorithm = unquote(sNullAlg); else pAlgorithm = unquote(pAuthEcho->algorithm); pUsername = unquote(pAuthEcho->username); pRealm = unquote(pAuthEcho->realm); pPasswd = unquote(CSBase::m_SipRegPasswd); pNonce = unquote(pAuthEcho->nonce); pCNonce = unquote(pAuthEcho->cnonce); if (NULL == pUsername) { DBGPrint(M_SipUA, ERROR_LEVEL, "%s Get username is null error!",__FUNCTION__); return false; } if(strcmp(pUsername, CSBase::m_SipRegUserName) != 0) { // DBGPrint(M_SipUA, ERROR_LEVEL, "%s %s username error!",__FUNCTION__,pAuthEcho->username); // return false; } //calculate session key DigestCalcHA1(pAlgorithm, pUsername, pRealm, pPasswd, pNonce, pCNonce, SessionKey); pNonceCount = unquote(pAuthEcho->nonce_count); if (NULL == pAuthEcho->message_qop) pQop = unquote(sNullQop); else pQop = unquote(pAuthEcho->message_qop); pCMethod = unquote(pMethod); pUri = unquote(pAuthEcho->uri); //calculate response DigestCalcResponse(SessionKey, pNonce, pNonceCount, pCNonce, pQop, pCMethod, pUri, (char*)"", Response); pResponse2 = unquote(pAuthEcho->response); if (strcmp(Response, pResponse2) != 0) { DBGPrint(M_SipUA, ERROR_LEVEL, "Authorization failed <%s, %s>!", Response, pResponse2); return false; } return true; } //回覆沒有包體的響應 int SendRegisterAnswer(int Tid, int Code, bool bSetTime) { osip_message_t * pMsgAnswer = NULL; int ret = eXosip_message_build_answer(Tid, Code,&pMsgAnswer); if(ret == OSIP_SUCCESS && pMsgAnswer != NULL ) { if (bSetTime == true) { osip_header_t* pHeader = NULL; char TmpBuf[CLIP_BUFFER_SIZE+1]; //initialize Date header int iRet = osip_header_init(&pHeader); if (OSIP_SUCCESS == iRet) { time_t now; struct timeval tv; //Get current time gettimeofday(&tv,NULL); now = tv.tv_sec; // time(&now); //struct tm* pTmVal = localtime(&now); struct tm STm = {0}; struct tm* pTmVal = localtime_r(&now, &STm); //使用線程安全函數 if (pTmVal != NULL) { //系統啓用夏令時,須要減去一個小時。 if (1 == pTmVal->tm_isdst) { now -= 3600; //pTmVal = localtime(&now); pTmVal = localtime_r(&now, &STm); //使用線程安全函數 if (NULL == pTmVal) return -1; } //2010-07-26 16:05:10 snprintf(TmpBuf, CLIP_BUFFER_SIZE, "%4d-%02d-%02dT%02d:%02d:%02d.%03d", pTmVal->tm_year+1900, pTmVal->tm_mon+1, pTmVal->tm_mday, pTmVal->tm_hour, \ pTmVal->tm_min, pTmVal->tm_sec,(int)(tv.tv_usec/1000)); //set Date field osip_header_set_name(pHeader, osip_strdup("Date") ); osip_header_set_value(pHeader, osip_strdup(TmpBuf) ); osip_list_add(&pMsgAnswer->headers, pHeader, -1); } } } eXosip_message_send_answer(Tid, Code, pMsgAnswer); } else { DBGPrint(M_SipUA, ERROR_LEVEL, "%s:Build Message Answer Failed Tid<%d> Code<%d>!", `ls_function` , Tid, Code); } return 0; } //初始接收的註冊信息 int ProceRegister(const eXosip_event_t* pSipEvt) { int iReturnCode = -1; int iRet = -1; osip_authorization_t *pWWWAuEcho = NULL; iRet = osip_message_get_authorization(pSipEvt->request, 0, &pWWWAuEcho); if (iRet != -1) { //鑑權, 鑑權成功回覆200OK if (MD5AuthValidate(pSipEvt->request->sip_method, pWWWAuEcho)) { //鑑權成功,回覆200成功 //添加用戶信息 SendRegisterAnswer(pSipEvt->tid, SIP_OK, true); } else { //鑑權失敗,回覆403 SendRegisterAnswer(pSipEvt->tid, SIP_FORBIDDEN, false); } } else { //回覆401 , 待認證 osip_www_authenticate_t *pWWWAuth = NULL; iRet = osip_www_authenticate_init(&pWWWAuth); if (-1 == iRet) { DBGPrint(M_SipUA, ERROR_LEVEL, "Failed to init www-authenticate header"); return -1; } char RTag[RANDOM_TAG_LEN+1] = {0}; char Nonce[DIGEST_NONCE_LEN+1] = {0}; char TmpBuf[CLIP_BUFFER_SIZE/2+1] = {0}; SIPGenerateNonce(Nonce, 0, GenerateRandomTag(RTag) ); osip_www_authenticate_set_auth_type(pWWWAuth, osip_strdup("Digest") ); memcpy(TmpBuf, CSBase::m_SipSvrPubID, 10); char realm[CLIP_BUFFER_SIZE]; snprintf(realm, CLIP_BUFFER_SIZE/2, "\"%s\"", TmpBuf); osip_www_authenticate_set_realm(pWWWAuth, osip_strdup(realm) ); snprintf(TmpBuf, CLIP_BUFFER_SIZE/2, "\"%s\"", Nonce); osip_www_authenticate_set_nonce(pWWWAuth, osip_strdup(TmpBuf) ); char *pDest = NULL; osip_www_authenticate_to_str(pWWWAuth, &pDest); osip_message_t * pSRegister = NULL; iReturnCode = eXosip_message_build_answer(pSipEvt->tid,SIP_UNAUTHORIZED,&pSRegister); if ( iReturnCode == 0 && pSRegister != NULL ) { osip_message_set_www_authenticate(pSRegister,pDest); osip_message_set_content_type(pSRegister,"Application/MANSCDP+xml"); eXosip_message_send_answer(pSipEvt->tid,SIP_UNAUTHORIZED,pSRegister); } osip_www_authenticate_free(pWWWAuth); osip_free(pDest); } return 0; } int ProceXsipEvt(eXosip_event_t* pSipEvt) { //Check input parameter exception if (NULL == pSipEvt) { DBGPrint(M_SipUA, ERROR_LEVEL, "%s: pSipEvt is null pointer!", `ls_function` ); return -1; } //Check eXosip event type switch (pSipEvt->type) { case EXOSIP_MESSAGE_NEW: if (MSG_IS_NOTIFY(pSipEvt->request)) { //ProceNotify(pSipEvt); } else if(MSG_IS_MESSAGE(pSipEvt->request)) { //ProceMessage(pSipEvt); } else if(MSG_IS_REGISTER(pSipEvt->request)) { ProceRegister(pSipEvt); } break; case EXOSIP_SUBSCRIPTION_ANSWERED: case EXOSIP_SUBSCRIPTION_REQUESTFAILURE: { //ProcSubScribleAnswer(pSipEvt); } break; case EXOSIP_SUBSCRIPTION_NOTIFY: { //ProceNotify(pSipEvt); } break; case EXOSIP_CALL_PROCEEDING: case EXOSIP_CALL_RINGING: break; case EXOSIP_CALL_ANSWERED: { eXosip_call_t* pJCall = NULL; if (OSIP_SUCCESS == eXosip_call_find(pSipEvt->cid, &pJCall) ) { //ProcCallAnswer(pSipEvt); } } break; case EXOSIP_CALL_NOANSWER: case EXOSIP_CALL_REDIRECTED: case EXOSIP_CALL_REQUESTFAILURE: case EXOSIP_CALL_SERVERFAILURE: case EXOSIP_CALL_GLOBALFAILURE: case EXOSIP_CALL_TIMEOUT: { if (pSipEvt->request == NULL || pSipEvt->response == NULL) { DBGPrint(M_SipUA, ERROR_LEVEL, "%s: Recv Sip Type<%d:%s> Error!", `ls_function` , pSipEvt->type, GetRecvSipType(pSipEvt->type)); break; } DBGPrint(M_SipUA, ERROR_LEVEL, "%s: ******** receive device:<%s> error !response status code:<%d> username:<%s>!", `ls_function` , pSipEvt->request->to->url->username, pSipEvt->response->status_code, pSipEvt->request->req_uri->username ); eXosip_call_t* pJCall = NULL; if (OSIP_SUCCESS == eXosip_call_find(pSipEvt->cid, &pJCall) ) { //ProcCallAnswer(pSipEvt); } } break; case EXOSIP_CALL_MESSAGE_NEW: if (MSG_IS_BYE(pSipEvt->request) ) { DBGPrint(M_SipUA, ERROR_LEVEL, "%s: ******** imcoming BYE for DevAor<%s>!", `ls_function` , CSBase::URIToAOR(pSipEvt->request->from->url) ); } break; case EXOSIP_CALL_MESSAGE_ANSWERED: //BYE response break; case EXOSIP_CALL_CLOSED: break; case EXOSIP_CALL_RELEASED: break; case EXOSIP_MESSAGE_REQUESTFAILURE: break; case EXOSIP_MESSAGE_ANSWERED: break; default: DBGPrint(M_SipUA, ERROR_LEVEL, "%s: not handle <%d:%s> event type!", `ls_function` , pSipEvt->type, GetRecvSipType(pSipEvt->type)); return -1; } } int main() { //初始化exosip協議棧端口 Init(5060); //等待接收sip數據 while(1) { //---------------- eXosip running process ----------------// eXosip_execute(); eXosip_event_t* pSipEvt = eXosip_event_wait(0, 50); while (pSipEvt != NULL) { eXosip_lock(); ProceXsipEvt(pSipEvt); eXosip_default_action(pSipEvt); eXosip_unlock(); eXosip_event_free(pSipEvt); pSipEvt = eXosip_event_wait(0, 50); } usleep(50000); } }
GB/T28181視頻流媒體解決方案播放效果可見下圖:
函數