國標GBT28181協議,註冊功能服務端與客戶端實現程式碼
國標GBT28181協議的使用者註冊時候,需要使用者名稱密碼認證,其本質是使用 http digest的演算法,
http digest演算法,在RFC2617 [HTTP Authentication: Basic and Digest Access Authentication]文件裡面有詳細的描述,並且有實現程式碼,
國標GBT28181註冊流程如下,[GBT 28181-2011安全防範視訊監控聯網系統資訊傳輸、交換、控制技術要求,第9.1.2.1 基本註冊]
- 客戶端(SIP代理),向伺服器傳送註冊請求1,不帶口令(密碼),伺服器返回401,其中攜帶一些欄位值,要求客戶端驗證;
- 客戶端接收到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,®);
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,®);
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