原創文章,引用請保證原文完整性,尊重作者勞動,原文地址http://www.cnblogs.com/qq1269122125/p/3941172.html,qq:1269122125。
上兩章節簡要的講解了SIP組件開發接口和開發環境的搭建。在本節將實現Linux 32平台的UAS和UAC,當然該UAS和UAC只實現了注冊功能,並且是基於自主開發SIP組件libGBT28181SipComponent.so的,沒有這個組件是運行不了的。其他功能在後續章節中講解。
首先簡單講解一下GBT28181關於注冊描述
一. GBT28181注冊的流程如下圖
電力系統注冊稍微復雜點,但原來基本相同。多了個刷新注冊的過程。
二.GBT28181關於注冊的解釋如下
三.SIP協議簡介
一個合法的SIP請求必須至少包含如下頭域:TO,FROM,Cseq,Call-ID,Max-Forwards, Via;這些字段在所有SIP請求中必須包含。這6個字段是SIP消息的基本組成部分,他們提供了用於路由用的核心信息,包含了消息的地址,響應的路由,消息傳遞次數,詳細的順序,事務的唯一標志。
這些頭域字段是必須包含在請求行之後的,請求行包含了請求的方法,Request-URI,SIP的版本號碼。請求行例子:REGISTER sip:192.168.10.177:5060 SIP/2.0
四.GBT28181注冊流程如下
1.UAC--->UAS 發送請求登錄,傳送未鑒權信息
From 字段,由UAS管理的UAC地址編碼@UAS IP:UAS端口號。在實際過程中,UAS管理很多的UAC每個UAC都會保存一個地址編碼(可以理解為用戶名)和密碼等UAC的信息,當UAC登錄時,用於驗證UAC身份的合法性。
To字段,From相同
Contact字段是通訊信息字段,保存是本地UAC地址編碼@本地IP:UAC端口號
Call-ID地段,對應用層是必須要的,一次成功登錄完成後要保存這個Call_id值,因為這個ID是標志這次注冊的唯一標志。在後續的注銷登錄及刷新登錄都必須要這個ID.。
Cseq值保證了REGISTER請求的正確順序
Expires字段:表示該登記生存期為3600s。
Content-Length字段:表明此請求消息消息體的長度為空,即此消息不帶會話描述
2.UAS--->UAC exosip庫在發送注冊請求時,第一次發送未鑒權信息,UAS收到後回復401,並攜帶認證體制(如MD4)和認證參數(如nonce值)。
3.UAC--->UAS UAC在收到401信息後,根據UAS發送的401信息中的認證體制和認證參數,結合用戶名和密碼,生成response值。發送鑒權消息。
4.UAS--->UAC UAS收到鑒權信息後,根據自己自身的管理體制,找到UAC用戶在服務器中的密碼,根據UAC發送的認證體制和認證參數,結合用戶名和密碼,生成response值,在把response和UAC發送的response比較,相等則認證通過發送 200 ok。不等發送404驗證失敗。
五.源代碼
這裡注冊功能雖然簡單,但是為了後續其他功能的添加,這裡還是根據功能劃分了幾個模塊。後續添加功能,只是在這個框架中添加。
UAS_test部分代碼:
1.主要功能文件method.h
1 /* 2 =============================================================== 3 GBT28181 SIP組件libGBT28181SipComponent.so注冊實現 4 作者:程序人生 5 博客地址:http://blog.csdn.net/hiwubihe 6 QQ:1269122125 7 注:請尊重原作者勞動成果,僅供學習使用,請勿盜用,違者必究! 8 ================================================================ 9 */ 10 11 #ifndef METHOD_H_ 12 #define METHOD_H_ 13 #include <iostream> 14 #include <cstdlib> 15 #include <stdio.h> 16 #include "callback.h" 17 #include "IGBT28181Comm.h" 18 #include "sipserver.h" 19 20 21 using namespace GBT28181::Vsp; 22 using namespace std; 23 24 //啟動UAS角色的服務器 25 int server_start(void*addr); 26 //停止UAS角色服務器 27 void server_stop(); 28 29 30 #endif /* METHOD_H_ */
2.method.cpp 實現文件中,開啟服務包括啟動服務和設置回調函數。
1 /* 2 =============================================================== 3 GBT28181 SIP組件libGBT28181SipComponent.so注冊實現 4 作者:程序人生 5 博客地址:http://blog.csdn.net/hiwubihe 6 QQ:1269122125 7 注:請尊重原作者勞動成果,僅供學習使用,請勿盜用,違者必究! 8 ================================================================ 9 */ 10 11 #include "method.h" 12 #include <semaphore.h> 13 14 static IGBT28181Comm* g_SIPComm = NULL; 15 16 //啟動SIP服務 17 int server_start(void*addr) 18 { 19 COMM_PAIR *addr_entry = (COMM_PAIR *) addr; 20 if (g_SIPComm != NULL) 21 { 22 delete g_SIPComm; 23 } 24 if (!(g_SIPComm = new IGBT28181Comm(true))) 25 { 26 return -1; 27 } 28 //回調函數 29 g_SIPComm->SetResponseCallback(&server_callback, (void_t*) g_SIPComm); 30 g_SIPComm->StartSip(addr_entry->local_addr, addr_entry->local_port); 31 return 0; 32 } 33 34 //停止SIP服務 35 void server_stop() 36 { 37 38 if (g_SIPComm != NULL) 39 { 40 g_SIPComm->StopSip(); 41 sleep(2); 42 delete g_SIPComm; 43 } 44 g_SIPComm = NULL; 45 }
3.回調函數callback.h
1 /* 2 =============================================================== 3 GBT28181 SIP組件libGBT28181SipComponent.so注冊實現 4 作者:程序人生 5 博客地址:http://blog.csdn.net/hiwubihe 6 QQ:1269122125 7 注:請尊重原作者勞動成果,僅供學習使用,請勿盜用,違者必究! 8 ================================================================ 9 */ 10 11 #ifndef CALLBACK_H_ 12 #define CALLBACK_H_ 13 #include <stdio.h> 14 #include <string.h> 15 #include <iostream> 16 #include <stdlib.h> 17 #include "sipserver.h" 18 #include "IGBT28181Comm.h" 19 #include "method.h" 20 21 using namespace GBT28181::Vsp; 22 using namespace std; 23 24 //回調函數 25 void_t server_callback(const SipRequestInfo& info, void_t* user); 26 27 28 #endif /* LIBINTERFACE_H_ */
4.callback.cpp 實現文件
1 /* 2 =============================================================== 3 GBT28181 SIP組件libGBT28181SipComponent.so注冊實現 4 作者:程序人生 5 博客地址:http://blog.csdn.net/hiwubihe 6 QQ:1269122125 7 注:請尊重原作者勞動成果,僅供學習使用,請勿盜用,違者必究! 8 ================================================================ 9 */ 10 11 #include "callback.h" 12 #include "algorithm.h" 13 14 15 //客戶端主動請求,服務器端回調 16 const char * client_request_method[] = 17 { 18 "GBT28181.Vsp.Sip.SipMethod.Register", 19 "GBT28181.Vsp.Sip.SipMethod.Notify", 20 "GBT28181.Vsp.Sip.SipMethod.Subscribenotify" 21 }; 22 23 //打印SIP消息 24 static void SIP_PKG_Print(const SipRequestInfo& infomation) 25 { 26 SipRegisterContextInfo* info = (SipRegisterContextInfo*) &infomation; 27 cout << "\n" 28 << "**************************************************" 29 << "**************************" 30 << endl; 31 cout << "packet receive " << endl; 32 cout << "status :" << info->status << endl; 33 cout << "sipRequestId :" << info->sipRequestId << endl; 34 cout << "requestId :" << info->requestId << endl; 35 cout << "method :" << info->method << endl; 36 cout << "from :" << info->from << endl; 37 cout << "proxy :" << info->proxy << endl; 38 cout << "contact :" << info->contact << endl; 39 cout << "handle :" << info->handle << endl; 40 cout << "sipIp :" << info->sipIp << endl; 41 cout << "sipPort :" << info->sipPort << endl; 42 cout << "subscribeEvent :" << info->subscribeEvent << endl; 43 cout << "expires :" << info->expires << endl; 44 cout << "content :" << info->content << endl; 45 cout<<"Call ID:"<<info->callid<<endl; 46 if (!info->registerInfo.userName.empty()) 47 { 48 cout<<"********************************************"<<endl; 49 cout<<"authendication infomation as follows:"<<endl; 50 cout<<"username:"<<info->registerInfo.userName<<endl; 51 cout<<"algorithm:"<<info->registerInfo.algorithm<<endl; 52 cout<<"Realm:"<<info->registerInfo.digestRealm<<endl; 53 cout<<"nonce:"<<info->registerInfo.nonce<<endl; 54 cout<<"response:"<<info->registerInfo.response<<endl; 55 cout<<"uri:"<<info->registerInfo.uri<<endl; 56 57 } 58 cout 59 << "**************************************************" 60 << "**************************" 61 << endl; 62 } 63 64 static void_t register_response(const GBT28181::Vsp::SipRequestInfo& info, 65 void_t* user) 66 { 67 cout << "receive register request packet from client" << endl; 68 SIP_PKG_Print(info); 69 char temp[16]; 70 SipRegisterContextInfo* regInfo = (SipRegisterContextInfo*) &info; 71 SipRegisterContextInfo repinfo; 72 repinfo.sipRequestId = info.sipRequestId; 73 repinfo.from = info.proxy; 74 repinfo.proxy = info.from; 75 repinfo.method = info.method; 76 //repinfo.expires = 300; 77 repinfo.registerInfo.nonce = "9bd055"; 78 sscanf(info.contact.c_str(), "%*[^@]@%[^:]", temp); 79 repinfo.registerInfo.digestRealm = temp; 80 sscanf(info.proxy.c_str(), "%*[^@]@%[^:]", temp); 81 repinfo.sipIp = temp; 82 sscanf(info.proxy.c_str(), "%*[^:]:%s", temp); 83 repinfo.sipPort = atoi(temp); 84 repinfo.registerInfo.userName = regInfo->registerInfo.userName; 85 repinfo.content="sfsdfsdf"; 86 GBT28181::Vsp::IGBT28181Comm* p_this = (GBT28181::Vsp::IGBT28181Comm*) user; 87 88 if (repinfo.registerInfo.userName.empty()) 89 { 90 cout<<"this register packet is unauthendicatin"<<endl; 91 cout<<"send 401"<<endl; 92 repinfo.status = "401"; 93 p_this->Downcast(repinfo); 94 } 95 else 96 { 97 cout<<"this register packet is authendicatin"<<endl; 98 //驗證 99 HASHHEX HA1; 100 HASHHEX Response; 101 DigestCalcHA1(regInfo->registerInfo.algorithm.c_str(), 102 regInfo->registerInfo.userName.c_str(), 103 regInfo->registerInfo.digestRealm.c_str(), UAC_PASSWD, 104 regInfo->registerInfo.nonce.c_str(), NULL, HA1); 105 DigestCalcResponse(HA1, regInfo->registerInfo.nonce.c_str(), 106 NULL, NULL, NULL, 0, "REGISTER", 107 regInfo->registerInfo.uri.c_str(), 108 NULL, Response); 109 if (!strcmp(Response, regInfo->registerInfo.response.c_str())) 110 { 111 cout<<"認證成功發送 200 OK!!!"<<endl; 112 repinfo.expires = 5; 113 repinfo.status = "200"; 114 } 115 else 116 { 117 cout<<"認證失敗發送 404 OK!!!"<<endl; 118 repinfo.expires = 5; 119 repinfo.status = "404"; 120 } 121 122 p_this->Downcast(repinfo); 123 124 } 125 } 126 127 // 128 void_t server_callback(const SipRequestInfo& info, void_t* user) 129 { 130 //注冊報文的情況,調用注冊回調 131 if (strncmp(info.method.c_str(), client_request_method[0], strlen( 132 client_request_method[0])) == 0) 133 { 134 register_response(info, user); 135 } 136 //其他情況報文 137 else 138 { 139 cout << "server receive wrong packer" << endl; 140 SIP_PKG_Print(info); 141 exit(1); 142 } 143 }
5.sip認證 algorithm.h
1 /* 2 =============================================================== 3 GBT28181 SIP組件libGBT28181SipComponent.so注冊實現 4 作者:程序人生 5 博客地址:http://blog.csdn.net/hiwubihe 6 QQ:1269122125 7 注:請尊重原作者勞動成果,僅供學習使用,請勿盜用,違者必究! 8 ================================================================ 9 */ 10 #ifndef B_REGISTER__ALGORITHM_H_ 11 #define B_REGISTER__ALGORITHM_H_ 12 13 #include <stdio.h> 14 #include "osip_md5.h" 15 16 #define HASHLEN 16 17 typedef char HASH[HASHLEN]; 18 19 #define HASHHEXLEN 32 20 typedef char HASHHEX[HASHHEXLEN + 1]; 21 22 23 void DigestCalcHA1(const char *pszAlg, const char *pszUserName, 24 const char *pszRealm, const char *pszPassword, 25 const char *pszNonce, const char *pszCNonce, 26 HASHHEX SessionKey); 27 28 void DigestCalcResponse(HASHHEX HA1, const char *pszNonce, 29 const char *pszNonceCount, const char *pszCNonce, 30 const char *pszQop, int Aka, const char *pszMethod, 31 const char *pszDigestUri, HASHHEX HEntity, HASHHEX Response); 32 33 #endif /* B_REGISTER__ALGORITHM_H_ */
5.sip認證 algorithm.cpp,這部分參考代碼可以在exosip2源代碼中找到。
1 /* 2 =============================================================== 3 GBT28181 SIP組件libGBT28181SipComponent.so注冊實現 4 作者:程序人生 5 博客地址:http://blog.csdn.net/hiwubihe 6 QQ:1269122125 7 注:請尊重原作者勞動成果,僅供學習使用,請勿盜用,違者必究! 8 ================================================================ 9 */ 10 11 #include "algorithm.h" 12 #include "string.h" 13 14 static void CvtHex(HASH Bin, HASHHEX Hex) 15 { 16 unsigned short i; 17 unsigned char j; 18 19 for (i = 0; i < HASHLEN; i++) 20 { 21 j = (Bin[i] >> 4) & 0xf; 22 if (j <= 9) 23 Hex[i * 2] = (j + '0'); 24 else 25 Hex[i * 2] = (j + 'a' - 10); 26 j = Bin[i] & 0xf; 27 if (j <= 9) 28 Hex[i * 2 + 1] = (j + '0'); 29 else 30 Hex[i * 2 + 1] = (j + 'a' - 10); 31 }; 32 Hex[HASHHEXLEN] = '\0'; 33 } 34 35 void DigestCalcHA1(const char *pszAlg, const char *pszUserName, 36 const char *pszRealm, const char *pszPassword, 37 const char *pszNonce, const char *pszCNonce, 38 HASHHEX SessionKey) 39 { 40 osip_MD5_CTX Md5Ctx; 41 HASH HA1; 42 43 osip_MD5Init(&Md5Ctx); 44 osip_MD5Update(&Md5Ctx, (unsigned char *) pszUserName, strlen(pszUserName)); 45 osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1); 46 osip_MD5Update(&Md5Ctx, (unsigned char *) pszRealm, strlen(pszRealm)); 47 osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1); 48 osip_MD5Update(&Md5Ctx, (unsigned char *) pszPassword, strlen(pszPassword)); 49 osip_MD5Final((unsigned char *) HA1, &Md5Ctx); 50 if ((pszAlg != NULL) && strcmp(pszAlg, "md5-sess") == 0) 51 { 52 osip_MD5Init(&Md5Ctx); 53 osip_MD5Update(&Md5Ctx, (unsigned char *) HA1, HASHLEN); 54 osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1); 55 osip_MD5Update(&Md5Ctx, (unsigned char *) pszNonce, strlen(pszNonce)); 56 osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1); 57 osip_MD5Update(&Md5Ctx, (unsigned char *) pszCNonce, strlen(pszCNonce)); 58 osip_MD5Final((unsigned char *) HA1, &Md5Ctx); 59 } 60 CvtHex(HA1, SessionKey); 61 } 62 63 void DigestCalcResponse(HASHHEX HA1, const char *pszNonce, 64 const char *pszNonceCount, const char *pszCNonce, 65 const char *pszQop, int Aka, const char *pszMethod, 66 const char *pszDigestUri, HASHHEX HEntity, HASHHEX Response) 67 { 68 osip_MD5_CTX Md5Ctx; 69 HASH HA2; 70 HASH RespHash; 71 HASHHEX HA2Hex; 72 73 /* calculate H(A2) */ 74 osip_MD5Init(&Md5Ctx); 75 osip_MD5Update(&Md5Ctx, (unsigned char *) pszMethod, strlen(pszMethod)); 76 osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1); 77 osip_MD5Update(&Md5Ctx, (unsigned char *) pszDigestUri, 78 strlen(pszDigestUri)); 79 80 if (pszQop == NULL) 81 { 82 goto auth_withoutqop; 83 } 84 else if (0 == strcmp(pszQop, "auth-int")) 85 { 86 goto auth_withauth_int; 87 } 88 else if (0 == strcmp(pszQop, "auth")) 89 { 90 goto auth_withauth; 91 } 92 93 auth_withoutqop: osip_MD5Final((unsigned char *) HA2, &Md5Ctx); 94 CvtHex(HA2, HA2Hex); 95 96 /* calculate response */ 97 osip_MD5Init(&Md5Ctx); 98 osip_MD5Update(&Md5Ctx, (unsigned char *) HA1, HASHHEXLEN); 99 osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1); 100 osip_MD5Update(&Md5Ctx, (unsigned char *) pszNonce, strlen(pszNonce)); 101 osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1); 102 103 goto end; 104 105 auth_withauth_int: 106 107 osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1); 108 osip_MD5Update(&Md5Ctx, (unsigned char *) HEntity, HASHHEXLEN); 109 110 auth_withauth: osip_MD5Final((unsigned char *) HA2, &Md5Ctx); 111 CvtHex(HA2, HA2Hex); 112 113 /* calculate response */ 114 osip_MD5Init(&Md5Ctx); 115 osip_MD5Update(&Md5Ctx, (unsigned char *) HA1, HASHHEXLEN); 116 osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1); 117 osip_MD5Update(&Md5Ctx, (unsigned char *) pszNonce, strlen(pszNonce)); 118 osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1); 119 if (Aka == 0) 120 { 121 osip_MD5Update(&Md5Ctx, (unsigned char *) pszNonceCount, strlen( 122 pszNonceCount)); 123 osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1); 124 osip_MD5Update(&Md5Ctx, (unsigned char *) pszCNonce, strlen(pszCNonce)); 125 osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1); 126 osip_MD5Update(&Md5Ctx, (unsigned char *) pszQop, strlen(pszQop)); 127 osip_MD5Update(&Md5Ctx, (unsigned char *) ":", 1); 128 } 129 end: osip_MD5Update(&Md5Ctx, (unsigned char *) HA2Hex, HASHHEXLEN); 130 osip_MD5Final((unsigned char *) RespHash, &Md5Ctx); 131 CvtHex(RespHash, Response); 132 }
由於我采用的是MD5驗證,所以還需要從exosip2代碼中引用osip_md5.h 文件。也可以直接把osip_md5.h和osip_md5.cpp拷到你的工程中,我就采用這個方法。
6.主程序 sipserver.h,該測試程序中,UAS值管理一個UAC 地址編碼為100110000201000000 密碼123456,UAS端口寫死5060,地址編碼寫死100110000000000000
1 /* 2 =============================================================== 3 GBT28181 SIP組件libGBT28181SipComponent.so注冊實現 4 作者:程序人生 5 博客地址:http://blog.csdn.net/hiwubihe 6 QQ:1269122125 7 注:請尊重原作者勞動成果,僅供學習使用,請勿盜用,違者必究! 8 ================================================================ 9 */ 10 11 #ifndef SIPCLIENT_H_ 12 #define SIPCLIENT_H_ 13 14 #include "IGBT28181Comm.h" 15 16 using namespace GBT28181::Vsp; 17 18 19 //默認端口 20 #define UASPORT (5060) 21 //#define UACPORT (5061) 22 //UAS自己地址編碼 具體地址編碼什麼含義參考標准 23 #define UASADD_CODE ("100110000000000000") 24 //UAC地址編碼 地址編碼相當於用戶名 25 //實際應用中每個UAS保存該UAS所管理的一大堆UAC地址編碼以及用戶密碼等信息 用於注冊驗證 26 //當前測試UAS只管理一個UAC 地址編碼如下 27 #define UACADD_CODE ("100110000201000000") 28 #define UAC_PASSWD ("123456") 29 30 //該枚舉類型列舉類UAS角色 所主動請求的方法 31 // 32 typedef enum 33 { 34 INVITE, ACK, MESSAGE, BYE, SUBSCRIBE, CALLMESSAGE, 35 } METHOD; 36 37 38 //通信實體對 UAS服務器的IP與端口 39 typedef struct 40 { 41 char local_addr[16]; 42 int local_port; 43 } COMM_PAIR; 44 45 46 47 #endif /* SIPCLIENT_H_ */
7.sipserver.cpp,當前只支持啟動服務,退出服務功能。其他功能後續添加。
1 /* 2 =============================================================== 3 GBT28181 SIP組件libGBT28181SipComponent.so注冊實現 4 作者:程序人生 5 博客地址:http://blog.csdn.net/hiwubihe 6 QQ:1269122125 7 注:請尊重原作者勞動成果,僅供學習使用,請勿盜用,違者必究! 8 ================================================================ 9 */ 10 11 #include <unistd.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <iostream> 16 #include "sipserver.h" 17 #include "method.h" 18 19 using namespace std; 20 21 static void usage() 22 { 23 const char 24 *b = "-------------------------------------------------------------------------------\n" 25 "SIP Library test process - sipserver v 1.0 (June 13, 2014)\n\n" 26 "Author: 程序人生\n\n" 27 "博客地址:http://blog.csdn.net/hiwubihe QQ:1269122125\n\n" 28 "-------------------------------------------------------------------------------\n" 29 "\n" 30 "-s local server ipv4 address\n" 31 "-h Print this help and exit\n\n" 32 "-------------------------------------------------------------------------------\n" 33 "\n" 34 "example1: ./sipserver -s127.0.0.1\n" 35 "example2: ./sipserver -h\n" 36 "server default port|server address code|client default port|client address code\n" 37 "5060 |100110000000000000 |5061 |100110000201000000 \n" 38 "-------------------------------------------------------------------------------\n" 39 "\n"; 40 fprintf(stderr, b, strlen(b)); 41 } 42 43 static void help() 44 { 45 const char 46 *b = "-------------------------------------------------------------------------------\n" 47 "SIP Library test process - sipserver v 1.0 (June 13, 2014)\n\n" 48 "Current test Register method,only number 6 7 8 9 is useful\n\n" 49 "Author: 程序人生\n\n" 50 "博客地址:http://blog.csdn.net/hiwubihe QQ:1269122125\n\n" 51 "-------------------------------------------------------------------------------\n" 52 "\n" 53 " 0:Invite\n" 54 " 1:Ack\n" 55 " 2:Message\n" 56 " 3:Bye\n" 57 " 4:Subscribe\n" 58 " 5:CallMessage unused\n" 59 " 6:start service\n" 60 " 7:stop service\n" 61 " 8:clear scream\n" 62 " 9:exit\n" 63 "-------------------------------------------------------------------------------\n" 64 "\n"; 65 fprintf(stderr, b, strlen(b)); 66 cout << "please select method :"; 67 } 68 69 int main(int argc, char*argv[]) 70 { 71 int ch; 72 /*METHOD meth;*/ 73 COMM_PAIR comm_entry; 74 comm_entry.local_port = UASPORT; 75 opterr = 0; 76 if (argc == 2) 77 { 78 while ((ch = getopt(argc, argv, "s:h:")) != -1) 79 { 80 switch (ch) 81 { 82 case 's': 83 { 84 strcpy(comm_entry.local_addr, optarg); 85 break; 86 } 87 case 'h': 88 { 89 usage(); 90 return EXIT_SUCCESS; 91 break; 92 } 93 default: 94 { 95 fprintf(stderr, "Illegal argument \n"); 96 usage(); 97 return EXIT_FAILURE; 98 } 99 } 100 } 101 } 102 else 103 { 104 fprintf(stderr, "Illegal argument \n"); 105 usage(); 106 return EXIT_FAILURE; 107 } 108 if (system("clear") < 0) 109 { 110 cout << "clear scream error" << endl; 111 exit(0); 112 } 113 help(); 114 ch = getchar(); 115 getchar(); 116 while (1) 117 { 118 switch (ch) 119 { 120 case '6': 121 //啟動服務 122 if (server_start(&comm_entry) < 0) 123 { 124 cout << "service start failure" << endl; 125 break; 126 } 127 cout << "service start success ......" << endl; 128 cout << "uas address :" << comm_entry.local_addr << " uas port :" 129 << comm_entry.local_port << " address code :" << UASADD_CODE 130 << endl; 131 break; 132 case '7': 133 cout << "stop service......" << endl; 134 server_stop(); 135 break; 136 case '8': 137 if (system("clear") < 0) 138 { 139 cout << "clear scream error" << endl; 140 exit(1); 141 } 142 break; 143 case '9': 144 cout << "exit sipserver......" << endl; 145 getchar(); 146 exit(0); 147 default: 148 cout << "select error" << endl; 149 break; 150 } 151 cout << "press any key to continue......" << endl; 152 getchar(); 153 help(); 154 ch = getchar(); 155 getchar(); 156 } 157 return 0; 158 }
UAC_test 代碼如下:
這部分實現簡單,只是演示,包含一個文件UAC_test.cpp,UAC地址編碼100110000201000000,端口5061寫死。UAS端口寫死5060。
1 /* 2 =============================================================== 3 GBT28181 SIP組件libGBT28181SipComponent.so注冊實現 4 作者:程序人生 5 博客地址:http://blog.csdn.net/hiwubihe 6 QQ:1269122125 7 注:請尊重原作者勞動成果,僅供學習使用,請勿盜用,違者必究! 8 ================================================================ 9 */ 10 11 #include <iostream> 12 #include <string.h> 13 #include <stdio.h> 14 15 #include "IGBT28181Comm.h" 16 using namespace GBT28181::Vsp; 17 using namespace std; 18 19 //默認端口 20 #define UASPORT (5060) 21 #define UACPORT (5061) 22 23 static char pwd[20]; 24 25 //通信實體對 26 typedef struct 27 { 28 char local_addr[16]; 29 char remote_addr[16]; 30 int local_port; 31 int remote_port; 32 } COMM_PAIR; 33 34 static void_t SIP_PKG_Print(const SipRequestInfo& info) 35 { 36 cout << "packet receive :" << endl; 37 cout 38 << "****************************************************************************" 39 << endl; 40 cout << "status :" << info.status << endl; 41 cout << "sipRequestId :" << info.sipRequestId << endl; 42 cout << "requestId :" << info.requestId << endl; 43 cout << "method :" << info.method << endl; 44 cout << "from :" << info.from << endl; 45 cout << "proxy :" << info.proxy << endl; 46 cout << "contact :" << info.contact << endl; 47 cout << "content :" << info.content << endl; 48 cout << "status :" << info.status << endl; 49 cout << "handle :" << info.handle << endl; 50 cout << "sipIp :" << info.sipIp << endl; 51 cout << "sipPort :" << info.sipPort << endl; 52 cout << "subscribeEvent :" << info.subscribeEvent << endl; 53 cout << "expires :" << info.expires << endl; 54 cout 55 << "****************************************************************************" 56 << endl; 57 } 58 59 //消息處理回調函數 60 void_t SIP_PKG_Receive(const SipRequestInfo& info, void_t* user) 61 { 62 //打印從服務器接收的消息 63 SIP_PKG_Print(info); 64 //UAC向服務器發送響應報文 65 char buf[1024]; 66 const char*mthd=info.method.data(); 67 snprintf(buf, 1024, "response from client,for test method:%s", mthd); 68 if(memcmp(mthd,"Nari.Vsp.Sip.SipMethod.Register",strlen("Nari.Vsp.Sip.SipMethod.Register"))==0) 69 { 70 return ; 71 } 72 } 73 74 75 void Communicator_init(void*comm,IGBT28181Comm *SIPComm) 76 { 77 if (NULL != SIPComm) 78 { 79 delete SIPComm; 80 } 81 SIPComm = new IGBT28181Comm(false); 82 //回調函數 83 SIPComm->SetResponseCallback(SIP_PKG_Receive, (void_t*) SIPComm); 84 //啟動SIP協議 85 COMM_PAIR *comm_entry = (COMM_PAIR *) comm; 86 SIPComm->StartSip(comm_entry->local_addr, comm_entry->local_port); 87 } 88 89 90 //客戶端主動發送向服務器發送注冊報文 91 void sip_register(IGBT28181Comm* uac, COMM_PAIR *entry) 92 { 93 cout << "enter" << endl; 94 SipRegisterContextInfo info; 95 char temp[128]; 96 // 失效時間 97 info.expires = 300; 98 // 用戶100110000201000000 在 entry->remote_addr 遠程IP上 99 snprintf(temp, 128, "100110000201000000@%s:%d", entry->remote_addr, 100 entry->remote_port); 101 info.from = temp; 102 info.proxy = temp; 103 // 發起會話的方式 104 info.method = SipMethod::METHOD_REGISTER; 105 // 本地ip 106 snprintf(temp, 128, "100110000201000000@%s:%d", entry->local_addr, 107 entry->local_port); 108 //info.proxy = temp; 109 // contact 110 //info.contact = info.from; 111 info.contact = temp; 112 // 端口 113 info.sipPort = entry->local_port; 114 snprintf(temp, 128, "100110000201000000@%s", entry->local_addr); 115 info.sipIp = temp; 116 info.requestId = "0"; 117 info.sipRequestId = "0"; 118 info.registerInfo.userName = "100110000201000000"; 119 info.registerInfo.response = pwd; 120 if (NULL != uac) 121 { 122 uac->Downcast(info); 123 cout << "downcast success" << endl; 124 } 125 else 126 { 127 cout << "downcast failure" << endl; 128 } 129 } 130 131 132 133 int main(int argc, char*argv[]) 134 { 135 if(argc!=4) 136 { 137 cout<<"usage: ./UAC_test 127.0.0.1 127.0.0.1 123456"<<endl; 138 return 1; 139 } 140 COMM_PAIR comm_entry; 141 comm_entry.local_port = UACPORT; 142 comm_entry.remote_port = UASPORT; 143 strcpy(comm_entry.local_addr, argv[1]); 144 strcpy(comm_entry.remote_addr, argv[2]); 145 strcpy(pwd, argv[3]); 146 147 IGBT28181Comm *SIPComm=NULL; 148 SIPComm = new IGBT28181Comm(false); 149 //回調函數 150 SIPComm->SetResponseCallback(SIP_PKG_Receive, (void_t*) SIPComm); 151 //啟動SIP協議 152 SIPComm->StartSip(comm_entry.local_addr, comm_entry.local_port); 153 //向服務器發送注冊報文 154 sip_register(SIPComm, &comm_entry); 155 while(1) 156 { 157 sleep(5); 158 } 159 }
六.測試
筆者用的環境為centos 6.0 32bit系統。成功啟動的前提是上一節所講的libGBT28181SipComponent.so必須拷到系統庫目錄下,或者設置LD_LIBRARY_PATH環境變量。同時安裝libGBT28181SipComponent.so庫所依賴的庫。可以用ldd UAS_test 查看程序依賴的庫以及哪些庫找不到。
1.啟動UAS
2.啟動後效果
3.填寫6 start service後效果
此時,UAS注冊服務程序已經成功啟動。等待UAC的注冊。
4.筆者UAS_test和UAC_test是在一台機器上測試的。格式UAC_test 本機IP UASIP 密碼
5.啟動後UAS_test收到未鑒權報文
從打印的報文看,沒有用戶名等信息,同時提示發送了401回復報文
6.UAC_test收到401報文如下
可以看到UAC_test已經成功接受401報文,准備發送具有鑒權信息給UAS_test
7.UAS_test收到鑒權信息
可以看到有用戶名等鑒權信息。UAS_test鑒權後,發現用戶合法,給與回復200ok!
8.UAC_test收到200OK
整個驗證過程結束。
9.其實驗證還用很多情況,標准中都有定義,發送什麼響應碼。如密碼錯誤響應404錯誤碼。上例中
./UAC_test 192.168.50.57 192.168.50.57 12345,密碼不正確。將會收到404報文。
關於SIP注冊怎麼調用exosip2的接口實現,有空再整理。
本文源碼下載,正在整理中。。。。。。。。。。。。。。。。
TCP/IP協議 (傳輸控制協議/網間協議)
TCP/IP 協議集確立了 Internet 的技術基礎。TCP/IP 的發展始於美國 DOD (國防部)方案。 IAB (Internet 架構委員會)的下屬工作組 IETF (Internet 工程任務組)研發了其中多數協議。 IAB 最初由美國政府發起,如今轉變為公開而自治的機構。IAB 協同研究和開發 TCP/IP 協議集的底層結構,並引導著 Internet 的發展。TCP/IP 協議集記錄在請求注解(RFC)文件中,RFC 文件均由 IETF 委員會起草、討論、傳閱及核准。所有這些文件都是公開且免費的,且能在 IETF 網站上列出的參考文獻中找到。
TCP/IP 協議覆蓋了 OSI 網絡結構七層模型中的六層,並支持從交換(第二層)諸如多協議標記交換,到應用程序諸如郵件服務方面的功能。TCP/IP 的核心功能是尋址和路由選擇(網絡層的 IP/IPV6 )以及傳輸控制(傳輸層的 TCP、UDP)。
IP (網際協議)
在網絡通信中,網絡組件的尋址對信息的路由選擇和傳輸來說是相當關鍵的。相同網絡中的兩台機器間的消息傳輸有各自的技術協定。LAN 是通過提供6字節的唯一標識符(“MAC”地址)在機器間發送消息的。SNA 網絡中的每台機器都有一個邏輯單元及與其相應的網絡地址。DECNET、AppleTalk 和 Novell IPX 均有一個用來分配編號到各個本地網和工作站的配置。
除了本地或特定提供商的網絡地址,IP 為世界范圍內的各個網絡設備都分配了一個唯一編號,即 IP 地址。IPV4 的 IP 地址為4字節,按照慣例,將每個字節轉化成十進制(0-255)並以點分隔各字節。IPV6 的 IP 地址已經增加到16字節。關於 IP 和 IPV6 協議的詳細說明,在相關文件中再另作介紹。
TCP (傳輸控制協議)
通過序列化應答和必要時重發數據包,TCP 為應用程序提供了可靠的傳輸流和虛擬連接服務。TCP 主要提供數據流轉送,可靠傳輸,有效流控制,全雙工操作和多路傳輸技術。可查閱 TCP 部分獲取更多詳細資料。
在下面的 TCP/IP 協議表格中,我們根據協議功能和其在 OSI 七層網絡通信參考模型的映射關系將其全部列出。然而,TCP/IP 並不完全遵循 OSI 模型,例如:大多數 TCP/IP 應用程序是直接在傳輸層協議 TCP 和 UDP 上運行,而不涉及其中的表示層和會話層。
主要協議表
IP TCP UDP IPsec HTTP POP3 SNMP MPLS DNS SMTP
應用層(Application Layer)
--------------------------------------------------------------------------------
BOOTP:引導協議 (BOOTP:Bootstrap Protocol)
DCAP:數據轉接客戶訪問協議 (DCAP:Data Link Switching Client Access Protocol)
DHCP:動態主機配置協議 (DHCP:Dynamic Host Configuration Protocol)
DNS:域名系統(服務)系統 (DNS:Domain Name Systems)
Finger:用戶信息協議 (Finger:U......余下全文>>
SIP 協議概述
摘 要:SIP協議是NGN中的重要協議,越來越得到業界的重視。本文通過SIP協議的背景、功能、網絡元素、實現機制、以及SIP消息的組成等幾個方面對SIP協議做了全方位的概要性介紹,以使讀者對SIP有初步的概念和認識。
關鍵字:SIP NGN 代理服務器
一、SIP協議的背景和功能
SIP( 會話初始協議)的開發目的是用來幫助提供跨越因特網的高級電話業務。因特網電話(IP電話)正在向一種正式的商業電話模式演進,SIP就是用來確保這種演進實現而需要的NGN(下一代網絡)系列協議中重要的一員。
SIP是IETF標准進程的一部分,它是在諸如SMTP(簡單郵件傳送協議)和HTTP(超文本傳送協議)基礎之上建立起來的。它用來建立,改變和終止基於IP網絡的用戶間的呼叫。為了提供電話業務它還需要結合不同的標准和協議:特別是需要確保傳輸(RTP),與當前電話網絡的信令互連,能夠確保語音質量(RSVP),能夠提供目錄(LDAP),能夠鑒權用戶(RADIUS)等等。
SIP被描述為用來生成,修改和終結一個或多個參與者之間的會話。這些會話包括因特網多媒體會議,因特網(或任何IP網絡)電話呼叫和多媒體發布。會話中的成員能夠通過多播或單播聯系的網絡來通信。SIP支持會話描述,它允許參與者在一組兼容媒體類型上達成一致。它同時通過代理和重定向請求到用戶當前位置來支持用戶移動性。SIP不與任何特定的會議控制協議捆綁。
本質上,SIP提供以下功能:
名字翻譯和用戶定位:無論被呼叫方在哪裡都確保呼叫達到被叫方。執行任何描述信息到定位信息的映射。確保呼叫(會話)的本質細節被支持。
特征協商:它允許與呼叫有關的組(這可以是多方呼叫)在支持的特征上達成一致(注意:不是所有方都能夠支持相同級別的特征)。例如視頻可以或不可以被支持。總之,存在很多需要協商的范圍。
呼叫參與者管理:呼叫中參與者能夠引入其它用戶加入呼叫或取消到其它用戶的連接。此外,用戶可以被轉移或置為呼叫保持。
呼叫特征改變:用戶應該能夠改變呼叫過程中的呼叫特征。例如,一呼叫可以被設置為“voice-only”,但是在呼叫過程中,用戶可以需要開啟視頻功能。也就是說一個加入呼叫的第三方為了加入該呼叫可以開啟不同的特征。
二、SIP網絡元素
SIP中有兩個要素。SIP用戶代理和SIP網絡服務器。用戶代理是呼叫的終端系統元素,而SIP服務器是處理與多個呼叫相關聯信令的網絡設備。
用戶代理本身具有一客戶機元素(用戶代理客戶機UAC)和一服務器元素(用戶代理服務器UAS)。客戶機元素初始呼叫而服務器元素應答呼叫。這允許點到點的呼叫通過客戶機-服務器協議來完成。
SIP服務器元素提供多種類型的服務器。有三種服務器形式存在於網絡中--SIP有狀態代理服務器,SIP無狀態代理服務器和SIP重定向服務器。由於呼叫者未必知道被呼叫方的IP地址或主機名,SIP服務器的主要功能是提供名字解析和用戶定位。可以獲得的是email形式的地址或與被呼叫方關聯的電話號碼。使用該信息,呼叫者的用戶代理能夠確定特定服務器來解析地址信息--這可能涉及網絡中很多服務器。
SIP代理服務器接收請求,決定將這些請求傳送到何處,並且將它們傳送到下一服務器(使用下一跳路由原理)。在網絡中可以有多跳。
有狀態和無狀態代理服務器的區別是有狀態代理服務器記住它接收的入請求,以及回送的響應和它轉送的出請求。無狀態代理服務器一旦轉送請求後就忘記所有的信息。這允許有狀態代理服務器生成請求以並行地嘗試多個可能的用戶位置並且送回最好的響應。無狀態代理服務器可能是最快的,並且......余下全文>>