1. 程式人生 > >國標GBT28181協議,註冊功能服務端與客戶端實現程式碼

國標GBT28181協議,註冊功能服務端與客戶端實現程式碼

國標GBT28181協議的使用者註冊時候,需要使用者名稱密碼認證,其本質是使用 http digest的演算法,

http digest演算法,在RFC2617 [HTTP Authentication: Basic and Digest Access Authentication]文件裡面有詳細的描述,並且有實現程式碼,

國標GBT28181註冊流程如下,[GBT 28181-2011安全防範視訊監控聯網系統資訊傳輸、交換、控制技術要求,第9.1.2.1 基本註冊]


  1. 客戶端(SIP代理),向伺服器傳送註冊請求1,不帶口令(密碼),伺服器返回401,其中攜帶一些欄位值,要求客戶端驗證;
  2. 客戶端接收到401未認證訊息之後,攜帶口令已經401訊息中攜帶的欄位,進行 http digest密碼數字摘要計算之後,傳回給伺服器,服務端對客戶端的裝置編碼,裝置口令進行驗證,驗證通過則傳送200 OK訊息。

我使用  eXosip2 庫,實現了國標的註冊程式碼的客戶端與服務端,程式碼如下,請參考,

伺服器端註冊程式碼,

#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>    //INADDR_ANY

#include <eXosip2/eXosip.h>

//MD5
#include "HTTPDigest.h"

char *pcPassword = "12345";


char *pcRealm = "640001";
char *pcNonce = "6fe9ba44a76be22a";
static void Register401Unauthorized(struct eXosip_t * peCtx,eXosip_event_t *je)
{
    int iReturnCode = 0;
    osip_message_t * pSRegister = NULL;

    osip_www_authenticate_t * header = NULL;
    osip_www_authenticate_init(&header);

    osip_www_authenticate_set_auth_type (header,osip_strdup("Digest"));
    osip_www_authenticate_set_realm(header,osip_enquote(pcRealm));
    osip_www_authenticate_set_nonce(header,osip_enquote(pcNonce));

    char *pDest = NULL;
    osip_www_authenticate_to_str(header,&pDest);

    iReturnCode = eXosip_message_build_answer (peCtx,je->tid,401,&pSRegister);
    if ( iReturnCode == 0 && pSRegister != NULL )
    {
        osip_message_set_www_authenticate(pSRegister,pDest);
        osip_message_set_content_type(pSRegister,"Application/MANSCDP+xml");
        eXosip_lock(peCtx);
        eXosip_message_send_answer (peCtx,je->tid,401,pSRegister);
        eXosip_unlock(peCtx);
    }

    osip_www_authenticate_free(header);
    osip_free(pDest);
}

static void SelfCalculateResponse(char *pcUsername,char *pcURI,char *pcMethod,HASHHEX pcResponse)
{
    //MD5 計算
    HASHHEX HA1;
    DigestCalcHA1("REGISTER",pcUsername,pcRealm,pcPassword,pcNonce,NULL,HA1);

    HASHHEX Response;
    //在下面這個函式裡面,已經計算了 H(A2),所以不需要自己計算 H(A2)
    DigestCalcResponse(HA1,pcNonce,NULL,NULL,NULL,0,pcMethod,pcURI,NULL,Response);

    memcpy(pcResponse,Response,HASHHEXLEN+1);
}

static void RegisterSuccess(struct eXosip_t * peCtx,eXosip_event_t *je)
{
    int iReturnCode = 0 ;
    osip_message_t * pSRegister = NULL;
    iReturnCode = eXosip_message_build_answer (peCtx,je->tid,200,&pSRegister);
    if ( iReturnCode == 0 && pSRegister != NULL )
    {
        eXosip_lock(peCtx);
        eXosip_message_send_answer (peCtx,je->tid,200,pSRegister);
        eXosip_unlock(peCtx);
    }
}

static void RegisterFailed(struct eXosip_t * peCtx,eXosip_event_t *je)
{
    int iReturnCode = 0 ;
    osip_message_t * pSRegister = NULL;
    iReturnCode = eXosip_message_build_answer (peCtx,je->tid,401,&pSRegister);
    if ( iReturnCode == 0 && pSRegister != NULL )
    {
        eXosip_lock(peCtx);
        eXosip_message_send_answer (peCtx,je->tid,401,pSRegister);
        eXosip_unlock(peCtx);
    }
}

static void *MainProcess(void * pvSClientGB)
{
    struct eXosip_t * peCtx = (struct eXosip_t *)pvSClientGB;
    for(;;)
    {
        eXosip_event_t *je = NULL;

        je = eXosip_event_wait (peCtx,0,4);

        if (je == NULL)
        {
            osip_usleep(10000);
            continue;
        }

        switch (je->type)
        {
            case EXOSIP_MESSAGE_NEW:
                {
                    //處理註冊訊息
                    if ( MSG_IS_REGISTER(je->request) )
                    {
                        //提取出各個欄位值,進行 MD5 計算
                        osip_authorization_t * Sdest = NULL;
                        osip_message_get_authorization(je->request,0,&Sdest);
                        if ( Sdest != NULL )
                        {

                            char *pcMethod = je->request->sip_method;
                            char *pAlgorithm = osip_strdup_without_quote(Sdest->algorithm);
                            char *pUsername = NULL;
                            if ( Sdest->username != NULL )
                            {
                                pUsername = osip_strdup_without_quote(Sdest->username);
                            }
                            char *pRealm = NULL;
                            if ( Sdest->realm != NULL )
                            {
                                pRealm = osip_strdup_without_quote(Sdest->realm);
                            }
                            char *pNonce = NULL;
                            if ( Sdest->nonce != NULL )
                            {
                                pNonce = osip_strdup_without_quote(Sdest->nonce);
                            }
                            char *pNonce_count = NULL;
                            if ( Sdest->nonce_count != NULL)
                            {
                                pNonce_count = osip_strdup_without_quote(Sdest->nonce_count);
                            }

                            char *pUri = NULL;
                            if ( Sdest->uri != NULL )
                            {
                                pUri = osip_strdup_without_quote(Sdest->uri);
                            }

                            //需要去掉兩端多餘的引號
                            HASHHEX HA1;
                            DigestCalcHA1(pAlgorithm,pUsername,pRealm,pcPassword,pNonce,
                                          pNonce_count, HA1);

                            HASHHEX Response;
                            HASHHEX HA2="";
                            //在下面這個函式裡面,已經計算了 H(A2),所以不需要自己計算 H(A2)
                            DigestCalcResponse(HA1,pNonce,pNonce_count,Sdest->cnonce,Sdest->message_qop,0,
                                               pcMethod,pUri,HA2,Response);


                            //Authenticate
                            char acResponse[HASHHEXLEN];
                            SelfCalculateResponse(pUsername,pUri,pcMethod,acResponse);
                            if ( memcmp(acResponse,Response,HASHHEXLEN) == 0 )
                            {
                                //傳送註冊成功的訊息
                                RegisterSuccess(peCtx,je);

                                fprintf(stderr,"Register Success!!\n");
                            }
                            else  //認證失敗
                            {
                                RegisterFailed(peCtx,je);
                            }

                            osip_free(pAlgorithm);
                            osip_free(pUsername);
                            osip_free(pRealm);
                            osip_free(pNonce);
                            osip_free(pNonce_count);
                            osip_free(pUri);

                        }
                        else   //認證失敗
                        {
                            Register401Unauthorized(peCtx,je);
                        }
                    }
                }
                break;
            default:
                {
                }
                break;
        }

        eXosip_event_free(je);
    }

    return NULL;
}


int main()
{
    struct eXosip_t *eCtx;
    eCtx = eXosip_malloc();

    int iReturnCode = 0;
    iReturnCode = eXosip_init (eCtx);
    if (iReturnCode != OSIP_SUCCESS )
    {
        printf ("Can't initialize eXosip!\n");
        return -1;
    }
    else
    {
        printf ("eXosip_init successfully!\n");
    }

    iReturnCode = eXosip_listen_addr (eCtx,IPPROTO_UDP, NULL,5060, AF_INET, 0);
    if ( iReturnCode !=  OSIP_SUCCESS )
    {
        printf ("eXosip_listen_addr error!\n");
        return -1;
    }

    MainProcess(eCtx);

    eXosip_quit(eCtx);
    osip_free(eCtx);
    eCtx = NULL;

    return 0;
}

客戶端註冊程式碼,

#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>    //INADDR_ANY

#include <eXosip2/eXosip.h>


char *pcServerID = "34010000002000000001";
char *pcServerIP = "192.168.120.107";
int iServerPort = 5060;
char *pcClientID = "34010000001310000001";
int iClientPort = 5062;
char *pcPassword = "12345";


int g_Rid = 0;

/**
 * @brief 註冊 向上級伺服器註冊
 * @param pSClientGB
 * @return
 */
#define GBT28181_REGISTER_SUCCESS 0
#define GBT28181_REGISTER_FAILURE -1
#define GBT28181_REGISTER_TIMEOUT -2
int GBT28181_Register(struct eXosip_t * peCtx)
{
    int i = 0;

    int iRid = 0;

    osip_message_t * reg = NULL;

    char acClientURI[128];
    char acServerURI[128];

    char acDeiveID10Bit[11];
    memset(acDeiveID10Bit,0,11);
    strncpy(acDeiveID10Bit,pcClientID,10);

    sprintf(acClientURI,"sip:%
[email protected]
%s:%d",pcClientID,acDeiveID10Bit,iClientPort); sprintf(acServerURI,"sip:%[email protected]%s:%d",pcServerID,pcServerIP,iServerPort); iRid = eXosip_register_build_initial_register(peCtx,acClientURI,acServerURI,NULL,3600,&reg); if (iRid < 0) { return -1; } g_Rid = iRid; i = eXosip_register_send_register (peCtx,iRid,reg); if ( i != OSIP_SUCCESS ) { return GBT28181_REGISTER_TIMEOUT; } else { } return GBT28181_REGISTER_SUCCESS; } static void *MainProcess(void * pvSClientGB) { struct eXosip_t * peCtx = (struct eXosip_t *)pvSClientGB; int iHasStartFlag = 0; for(;;) { eXosip_event_t *je = NULL; je = eXosip_event_wait (peCtx,0,4); if ( iHasStartFlag == 0 ) { iHasStartFlag = 1; } if ( iHasStartFlag == 1 ) { //Send Register to Server GBT28181_Register(peCtx); iHasStartFlag = 2; } if (je == NULL) { osip_usleep(10000); continue; } switch (je->type) { case EXOSIP_REGISTRATION_SUCCESS: { if ( je->response != NULL ) { osip_header_t *Date = NULL; osip_message_header_get_byname (je->response,"Date",0,&Date); if ( (Date != NULL) && (Date->hvalue != NULL) ) { char *pcDate = Date->hvalue; //HandleTiming(pSClientGB,pcDate); //處理校時指令 fprintf(stderr,"Register Success!!\n"); } } } break; case EXOSIP_REGISTRATION_FAILURE: { //傳送帶使用者名稱密碼的註冊訊息 if ( je->response != NULL ) { if(je->response->status_code == 401 || je->response->status_code == 407) { eXosip_lock(peCtx); eXosip_add_authentication_info (peCtx,pcClientID,pcClientID,pcPassword,"MD5",NULL);//向eXosip提供本機的認證資訊 eXosip_unlock(peCtx); osip_message_t * reg = NULL; eXosip_lock(peCtx); eXosip_register_build_register (peCtx,g_Rid,3600,&reg); eXosip_register_send_register (peCtx,g_Rid,reg); eXosip_unlock(peCtx); } } } } eXosip_event_free(je); } return NULL; } int main() { struct eXosip_t *eCtx; eCtx = eXosip_malloc(); int iReturnCode = 0; iReturnCode = eXosip_init (eCtx); if (iReturnCode != OSIP_SUCCESS ) { printf ("Can't initialize eXosip!\n"); return -1; } else { printf ("eXosip_init successfully!\n"); } iReturnCode = eXosip_listen_addr (eCtx,IPPROTO_UDP, NULL,iClientPort, AF_INET, 0); if ( iReturnCode != OSIP_SUCCESS ) { printf ("eXosip_listen_addr error!\n"); return -1; } MainProcess(eCtx); eXosip_quit(eCtx); osip_free(eCtx); eCtx = NULL; return 0; }


非常需要注意的是,記憶體洩漏,osip2 庫與 eXosip2 庫使用時,有很多地方都需要注意記憶體是否自動回收,若沒有回收,需要自己手動回收記憶體。

比如上面程式碼中的函式,

osip_strdup_without_quote()

osip_www_authenticate_to_str()

庫內部都沒有回收記憶體,需要手動釋放記憶體。記憶體洩漏檢測,有一個 valgrind 工具,感覺挺好用,推薦!!

我在資源裡面,上傳了本程式碼的工程,編寫好了makefile,可在 linux 下可以直接編譯執行。請參考。

相關推薦

國標GBT28181協議註冊功能服務客戶實現程式碼

國標GBT28181協議的使用者註冊時候,需要使用者名稱密碼認證,其本質是使用 http digest的演算法, http digest演算法,在RFC2617 [HTTP Authentication: Basic and Digest Access Authentica

nagios 服務客戶監控安裝詳細配置各配置文件詳解

this sql 引入 apache2 cpu load fine 宕機 pri require nagios 安裝與部署—————— 1、安裝前準備(1)創建nagios用戶和用戶組 [root@localhost ~]#groupadd nagios

【node】node的核心模塊---http模塊http的服務器和客戶

tel lis and 是否 使用 啟動 進入 path 入隊 http服務器和客戶端 node.js標準庫提供了http模塊,其中封裝了一個高效的http服務器和一個簡易的http客戶端,http.Server是一個基於事件的HTTP服務器,他的核心由Node.js下層的

WebService服務客戶得簡單應用效能測試

用途: 比如需要從其他系統獲取資訊,但是不能暴露自己得實現方式的時候。 比較常用得有: 1.HttpClient 2.WebService (推薦使用WebService) 一.簡單介紹 服務端:  暴露出一個URL地址即可,列入     可以發現訪問進去就是一

java 服務實現服務客戶之間的通訊以及客戶之間的通訊

此服務端是利用socket進行通訊 ServerSocket serversocket = new ServerSocket(12333); 使用本機的12333埠進行通訊,可以自己選擇 一般選擇10000以後的埠不會與其他服務衝突 import j

RTSP協議分析標準RTSP服務客戶互動流程

1.1.   RTSP協議簡介 一種應用層協議,可基於tcp或udp協議。 RTSP(Real Time StreamingProtocol,實時流媒體協議)是由Real Network和Netscape共同提出的一種應用層協議,它定義瞭如何在IP網路上有效地傳輸流媒

新手學習-Tcp的服務客戶的登入註冊系統

客戶端 最近臨近考試 還有好多科目需要預習 這個只能等到考試後再改一下了 現在這個程式問題很多 import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.I

SeesionId ,Token以及公私鑰服務客戶自己的互動

一、登入機制 粗略地分析, 登入機制主要分為登入驗證、登入保持、登出三個部分。登入驗證是指客戶端提供使用者名稱和密碼,向伺服器提出登入請求,伺服器判斷客戶端是否可以登入並向客戶端確認。 登入認保持是指客戶端登入後, 伺服器能夠分辨出已登入的客戶端,併為其持續提供登入許可

一文徹底理解Redis序列化協議你也可以編寫Redis客戶

前提 最近學習Netty的時候想做一個基於Redis服務協議的編碼解碼模組,過程中順便閱讀了Redis服務序列化協議RESP,結合自己的理解對文件進行了翻譯並且簡單實現了RESP基於Java語言的解析。編寫本文的使用使用的JDK版本為[8+]。 RESP簡介 Redis客戶端與Redis服務端基於一個稱作RE

oracle服務客戶字符集不同導致中文亂碼解決方案

use 修改環境變量 描述 image nls_lang oracle服務 環境 分析 導致 1.問題描述 用pl/sql登錄時,會提示“數據庫字符集(ZHS16GBK)和客戶端字符集(2%)是不同的,字符集轉化可能會造成不可預期的後果”,具體問題是中文亂碼,如下圖 2.

C# Socket簡單例子(服務客戶通信)

項目 回車 pop ace log () client protocol comm 這個例子只是簡單實現了如何使用 Socket 類實現面向連接的通信。 註意:此例子的目的只是為了說明用套接字寫程序的大概思路,而不是實際項目中的使用程序。在這個例子中,實際上還有很多問題

TCP網絡程序實例——服務客戶交互

href tcpclient 端口號 信息 try 本機ip 發送數據 定義 .cn ◆ 服務器端 創建服務器端項目Server,在Main方法中創建TCP連接對象;然後監聽客戶端接入,並讀取接入的客戶端IP地址和傳入的消息;最後向接入的客戶端發送一條信息。代碼如下:

NFS文件系統、服務客戶安裝、exportfs命令

NFS exportfs命令 NFS服務端安裝 NFS客戶端安裝 NFS介紹 NFS是Network File system的縮寫,也就是網絡文件系統;基於RPC協議進行傳輸; 服務端安裝 yum install -y nfs-utils rpcbind //安裝rpcbind包

html原理簡介、第一個網頁服務客戶

直接 ack 字符 time() true nec utf-8 RM 成了 #coding=utf-8 """ HTML: 20個標簽 一套瀏覽器認識的規則 學習規則。開發後臺程序:寫html文件 本地測試:找到文件直接雙擊打

使用Python的socket模塊搭建tcp服務客戶

有客 cep 服務器 send msg AC 鏈接 upper 可能 # __author__ = ‘ZSnail‘ # socket就是一個網絡通訊協議 # 服務器端 import socket server = socket.socket() server.b

ROS之服務客戶簡單程序遇到的錯誤

執行 AI gist 命令 鏈接 ret 教程 format fail 在按ROS入門教程(點擊打開鏈接)行進過程中遇到的錯誤 1、exec_depend與run_depend一樣 在按ROS入門教程(點擊打開鏈接)行進過程中到了執行 rosmsg show beg

基於Netty的一個WeoSocket通信服務客戶代碼(非JS代碼)

soc AS hub 應用服務 str ram 當前 AR 如果 基於Netty的一個WeoSocket通信服務器與客戶端代碼(非JS代碼) 咳咳,在這裏呢,小軒就不多說什麽是WebSocket的,還有呢,小軒為什麽不給出JS-Client代碼?網上太多代碼可以用了。小軒這

java 界面編程用socket實現服務客戶的循環通信。

accept star return IE while fde trac AS inf 服務端: package 實驗五聊天; import java.awt.BorderLayout; import java.awt.EventQueue; import

Python 絕技 —— UDP 服務客戶

建模 又一 https lib python3 -a 數據 Coding doc i春秋作家:wasrehpic 0x00 前言 在上一篇文章「Python 絕技 —— TCP 服務器與客戶端」中,介紹了傳輸層的核心協議 TCP ,並運用 Python 腳本的 socket

Untiy中用C#實現TCP通訊(Socket通訊)服務客戶皆可

簡而言之,TCP通訊原理大家可以從各種網路文獻上找到,這裡不做贅述。 只提出C#實現TCP通訊的一般方法和常用程式碼工具供第一次接觸TCP通訊的玩家參考,老玩家繞道。。。 為了方便大家理解我的程式碼,會適當提及通訊遠離。 1、建立服務端,TCP連線的基本: using U