原創文章,引用請保證原文完整性,尊重作者勞動,原文地址http://www.cnblogs.com/qq1269122125/p/3966794.html。
上章節講解了講解一個用eXosip2庫實現的Demo 程序。Demo講的是注冊的過程,因為篇幅比較長,再分一節寫。本節是上一節的繼續,主要實現UAC用eXosip2庫實現的Demo 程序。本節講的比較全面,處理實現注冊問題還添加了注銷和刷新注冊的過程。刷新相當於心跳的功能。注意這個函數eXosip_default_action()實現在sip中401和407錯誤類型的eXosip2庫的自動處理。網上有的人問注冊報文發送後,只收到401返回碼。這是對SIP注冊不了解造成的。至於這個過程在前面注冊理論部分已經講解。我也嘗試不用eXosip_default_action()這個函數,我自己發送鑒權信息,可惜沒成功,不知道什麼原因,有時返回200OK,有時不返回,所以還是用了eXosip_default_action()這個函數,讓401的響應報文由eXosip2庫去發送。
本章中要用的eXosip2庫的API做個簡單的介紹和使用方法。
在使用eXosip2前你需要初始化eXosip環境和libeXosip2.so庫,這一步必須在所有使用之前完成。
1 #include <eXosip2/eXosip.h> 2 //庫處理結果 3 int result = OSIP_SUCCESS; 4 //初始化庫 5 if (OSIP_SUCCESS != (result = eXosip_init())) 6 { 7 printf("eXosip_init failure.\n"); 8 return 1; 9 } 10 cout << "eXosip_init success." << endl; 11 //監聽 12 if (OSIP_SUCCESS != eXosip_listen_addr(IPPROTO_UDP, NULL, UACPORTINT, 13 AF_INET, 0)) 14 { 15 printf("eXosip_listen_addr failure.\n"); 16 eXosip_quit (); 17 return 1; 18 }
初始化完成之後,用戶就可以發送SIP消息和等待接受SIP事件了。
初始化eXosip2庫後,主服務程序就可以接受事件並處理事件了,下面是一些從eXosip2協議棧接受處理事件的實例代碼。
1 //開啟循環消息,實際應用中可以開啟多線程同時接收信號 2 eXosip_event_t* osipEventPtr = NULL; 3 4 while (true) 5 { 6 // Wait the osip event. 7 osipEventPtr = ::eXosip_event_wait(0, 200); 8 eXosip_lock(); 9 //一般處理401/407采用庫默認處理 10 eXosip_default_action(osipEventPtr); 11 eXosip_unlock(); 12 // If get nothing osip event,then continue the loop. 13 if (NULL == osipEventPtr) 14 { 15 continue; 16 } 17 // 事件處理 18 19 switch (osipEventPtr->type) 20 { 21 //需要繼續驗證REGISTER是什麼類型 22 case EXOSIP_REGISTRATION_NEW: 23 { 24 //注冊事件處理 25 } 26 break; 27 case EXOSIP_MESSAGE_NEW: 28 { 29 //消息事件處理 30 } 31 break; 32 case XXXXX: 33 { 34 //事件處理 35 } 36 break; 37 case XXXXX: 38 { 39 //事件處理 40 } 41 break; 42 default: 43 cout << "未處理消息 : " << osipEventPtr->type<<endl; 44 break; 45 } 46 eXosip_event_free(osipEventPtr); 47 osipEventPtr = NULL;
實際在應用的時候為了提高服務的並發性,一般並不用上面這種服務方式,因為上面這樣,如果接受到某個事件,而這個事件的處理事件非常長的話,會影響效率,導致其他事件阻塞等待,得不到即使處理。所以一般采用並發服務器的設計思想,即在接受到一個事件後,分配一個線程來處理。當然如果想效率更高,想節省創建線程的時間,可以在系統一起動後,分配一定大小的線程池,有事件到來的話,直接從線程池中獲取線程處理。
每個UAC或者UAS發送一個SIP信息後,相對應的UAS或者UAC都會接受到響應的,即事件。每個事件包含兩個部分,一個是request和response。request即是這個事件的請求報文, 而response是本次會話建立過程中,最後一次響應請求的報文。用戶可以從獲取的消息中,分析出message並且獲取SIP頭部,保存成用戶自己的數據形式。下面實例獲取expires頭部內容。
1 osip_header_t* header = NULL; 2 osip_message_header_get_byname(request, "expires", 0, &header); 3 if (NULL != header && NULL != header->hvalue) 4 { 5 ...... 6 } 7
1 /* 2 =============================================================== 3 GBT28181 基於eXosip2,osip庫實現注冊UAC功能 4 作者:程序人生 5 博客地址:http://blog.csdn.net/hiwubihe 6 QQ:1269122125 7 注:請尊重原作者勞動成果,僅供學習使用,請勿盜用,違者必究! 8 ================================================================ 9 */ 10 11 #include <iostream> 12 #include <string> 13 #include <sstream> 14 #include <osipparser2/osip_message.h> 15 #include <osipparser2/osip_parser.h> 16 #include <osipparser2/osip_port.h> 17 18 #include <eXosip2/eXosip.h> 19 #include <eXosip2/eX_setup.h> 20 #include <eXosip2/eX_register.h> 21 #include <eXosip2/eX_options.h> 22 #include <eXosip2/eX_message.h> 23 #include <arpa/inet.h> 24 #include <sys/types.h> 25 #include <sys/socket.h> 26 27 using namespace std; 28 29 //本地監聽IP 30 #define LISTEN_ADDR ("192.168.50.57") 31 //本地監聽端口 32 #define UACPORT ("5061") 33 #define UACPORTINT (5061) 34 //本UAC地址編碼 35 #define UACCODE ("100110000201000000") 36 //本地UAC密碼 37 #define UACPWD ("12345") 38 //遠程UAS IP 39 #define UAS_ADDR ("192.168.50.57") 40 //遠程UAS 端口 41 #define UAS_PORT ("5060") 42 //超時 43 #define EXPIS 300 44 45 //當前服務狀態 1 已經注冊 0 未注冊 46 static int iCurrentStatus; 47 //注冊成功HANDLE 48 static int iHandle = -1; 49 50 //SIP From/To 頭部 51 class CSipFromToHeader 52 { 53 public: 54 CSipFromToHeader() 55 { 56 } 57 ~CSipFromToHeader() 58 { 59 } 60 void SetHeader(string addrCod, string addrI, string addrPor) 61 { 62 addrCode = addrCod; 63 addrIp = addrI; 64 addrPort = addrPor; 65 } 66 string GetFormatHeader() 67 { 68 std::stringstream stream; 69 stream << "sip: " << addrCode << "@" << addrIp << ":" << addrPort; 70 return stream.str(); 71 } 72 //主機名稱 73 string GetCode() 74 { 75 std::stringstream stream; 76 stream << addrCode; 77 return stream.str(); 78 } 79 //主機地址 80 string GetAddr() 81 { 82 std::stringstream stream; 83 stream << addrIp; 84 return stream.str(); 85 } 86 //端口 87 string GetPort() 88 { 89 std::stringstream stream; 90 stream << addrPort; 91 return stream.str(); 92 } 93 94 private: 95 string addrCode; 96 string addrIp; 97 string addrPort; 98 }; 99 100 //SIP Contract頭部 101 class CContractHeader: public CSipFromToHeader 102 { 103 public: 104 CContractHeader() 105 { 106 } 107 ~CContractHeader() 108 { 109 } 110 void SetContractHeader(string addrCod, string addrI, string addrPor) 111 { 112 SetHeader(addrCod, addrI, addrPor); 113 } 114 string GetContractFormatHeader() 115 { 116 117 std::stringstream stream; 118 stream << "<sip:" << GetCode() << "@" << GetAddr() << ":" << GetPort() 119 << ">"; 120 return stream.str(); 121 } 122 }; 123 124 //發送注冊信息 125 int SendRegister(int& registerId, CSipFromToHeader &from, CSipFromToHeader &to, 126 CContractHeader &contact, const string& userName, const string& pwd, 127 const int expires, int iType) 128 { 129 cout << "=============================================" << endl; 130 if (iType == 0) 131 { 132 cout << "注冊請求信息:" << endl; 133 } 134 else if (iType == 1) 135 { 136 cout << "刷新注冊信息:" << endl; 137 } 138 else 139 { 140 cout << "注銷信息:" << endl; 141 } 142 cout << "registerId " << registerId << endl; 143 cout << "from " << from.GetFormatHeader() << endl; 144 cout << "to " << to.GetFormatHeader() << endl; 145 cout << "contact" << contact.GetContractFormatHeader() << endl; 146 cout << "userName" << userName << endl; 147 cout << "pwd" << pwd << endl; 148 cout << "expires" << expires << endl; 149 cout << "=============================================" << endl; 150 //服務器注冊 151 static osip_message_t *regMsg = 0; 152 int ret; 153 154 ::eXosip_add_authentication_info(userName.c_str(), userName.c_str(), 155 pwd.c_str(), "MD5", NULL); 156 eXosip_lock(); 157 //發送注冊信息 401響應由eXosip2庫自動發送 158 if (0 == registerId) 159 { 160 // 注冊消息的初始化 161 registerId = ::eXosip_register_build_initial_register( 162 from.GetFormatHeader().c_str(), to.GetFormatHeader().c_str(), 163 contact.GetContractFormatHeader().c_str(), expires, ®Msg); 164 if (registerId <= 0) 165 { 166 return -1; 167 } 168 } 169 else 170 { 171 // 構建注冊消息 172 ret = ::eXosip_register_build_register(registerId, expires, ®Msg); 173 if (ret != OSIP_SUCCESS) 174 { 175 return ret; 176 } 177 //添加注銷原因 178 if (expires == 0) 179 { 180 osip_contact_t *contact = NULL; 181 char tmp[128]; 182 183 osip_message_get_contact(regMsg, 0, &contact); 184 { 185 sprintf(tmp, "<sip:%s@%s:%s>;expires=0", 186 contact->url->username, contact->url->host, 187 contact->url->port); 188 } 189 //osip_contact_free(contact); 190 //reset contact header 191 osip_list_remove(®Msg->contacts, 0); 192 osip_message_set_contact(regMsg, tmp); 193 osip_message_set_header(regMsg, "Logout-Reason", "logout"); 194 } 195 } 196 // 發送注冊消息 197 ret = ::eXosip_register_send_register(registerId, regMsg); 198 if (ret != OSIP_SUCCESS) 199 { 200 registerId = 0; 201 }eXosip_unlock(); 202 203 return ret; 204 } 205 206 //注冊 207 void Register() 208 { 209 if (iCurrentStatus == 1) 210 { 211 cout << "當前已經注冊" << endl; 212 return; 213 } 214 CSipFromToHeader stFrom; 215 stFrom.SetHeader(UACCODE, UAS_ADDR, UAS_PORT); 216 CSipFromToHeader stTo; 217 stTo.SetHeader(UACCODE, UAS_ADDR, UAS_PORT); 218 CContractHeader stContract; 219 stContract.SetContractHeader(UACCODE, LISTEN_ADDR, UACPORT); 220 //發送注冊信息 221 int registerId = 0; 222 if (0 > SendRegister(registerId, stFrom, stTo, stContract, UACCODE, UACPWD, 223 3000, 0)) 224 { 225 cout << "發送注冊失敗" << endl; 226 return; 227 } 228 iCurrentStatus = 1; 229 iHandle = registerId; 230 } 231 //刷新注冊 232 void RefreshRegister() 233 { 234 if (iCurrentStatus == 0) 235 { 236 cout << "當前未注冊,不允許刷新" << endl; 237 return; 238 } 239 CSipFromToHeader stFrom; 240 stFrom.SetHeader(UACCODE, UAS_ADDR, UAS_PORT); 241 CSipFromToHeader stTo; 242 stTo.SetHeader(UACCODE, UAS_ADDR, UAS_PORT); 243 CContractHeader stContract; 244 stContract.SetContractHeader(UACCODE, LISTEN_ADDR, UACPORT); 245 //發送注冊信息 246 if (0 > SendRegister(iHandle, stFrom, stTo, stContract, UACCODE, UACPWD, 247 3000, 1)) 248 { 249 cout << "發送刷新注冊失敗" << endl; 250 return; 251 } 252 } 253 //注銷 254 void UnRegister() 255 { 256 if (iCurrentStatus == 0) 257 { 258 cout << "當前未注冊,不允許注銷" << endl; 259 return; 260 } 261 CSipFromToHeader stFrom; 262 stFrom.SetHeader(UACCODE, UAS_ADDR, UAS_PORT); 263 CSipFromToHeader stTo; 264 stTo.SetHeader(UACCODE, UAS_ADDR, UAS_PORT); 265 CContractHeader stContract; 266 stContract.SetContractHeader(UACCODE, LISTEN_ADDR, UACPORT); 267 //發送注冊信息 268269 if (0 > SendRegister( iHandle, stFrom, stTo, stContract, UACCODE, UACPWD, 270 0, 2)) 271 { 272 cout << "發送注銷失敗" << endl; 273 return; 274 } 275 iCurrentStatus = 0; 276 iHandle = -1; 277 } 278 static void help() 279 { 280 const char 281 *b = 282 "-------------------------------------------------------------------------------\n" 283 "SIP Library test process - uac v 1.0 (June 13, 2014)\n\n" 284 "SIP UAC端 注冊,刷新注冊,注銷實現\n\n" 285 "Author: 程序人生\n\n" 286 "博客地址:http://blog.csdn.net/hiwubihe QQ:1269122125\n\n" 287 "-------------------------------------------------------------------------------\n" 288 "\n" 289 " 0:Register\n" 290 " 1:RefreshRegister\n" 291 " 2:UnRegister\n" 292 " 3:clear scream\n" 293 " 4:exit\n" 294 "-------------------------------------------------------------------------------\n" 295 "\n"; 296 fprintf(stderr, b, strlen(b)); 297 cout << "please select method :"; 298 } 299 //服務處理線程 300 void *serverHandle(void *pUser) 301 { 302 sleep(3); 303 help(); 304 char ch = getchar(); 305 getchar(); 306 while (1) 307 { 308 switch (ch) 309 { 310 case '0': 311 //注冊 312 Register(); 313 break; 314 case '1': 315 //刷新注冊 316 RefreshRegister(); 317 break; 318 case '2': 319 //注銷 320 UnRegister(); 321 break; 322 case '3': 323 if (system("clear") < 0) 324 { 325 cout << "clear scream error" << endl; 326 exit(1); 327 } 328 break; 329 case '4': 330 cout << "exit sipserver......" << endl; 331 getchar(); 332 exit(0); 333 default: 334 cout << "select error" << endl; 335 break; 336 } 337 cout << "press any key to continue......" << endl; 338 getchar(); 339 help(); 340 ch = getchar(); 341 getchar(); 342 } 343 return NULL; 344 } 345 346 //事件處理線程 347 void *eventHandle(void *pUser) 348 { 349 eXosip_event_t* osipEventPtr = (eXosip_event_t*) pUser; 350 switch (osipEventPtr->type) 351 { 352 //需要繼續驗證REGISTER是什麼類型 353 case EXOSIP_REGISTRATION_SUCCESS: 354 case EXOSIP_REGISTRATION_FAILURE: 355 { 356 cout<<"收到狀態碼:"<<osipEventPtr->response->status_code<<"報文"<<endl; 357 if(osipEventPtr->response->status_code == 401) 358 { 359 cout<<"發送鑒權報文"<<endl; 360 } 361 else if(osipEventPtr->response->status_code == 200) 362 { 363 cout<<"接收成功"<<endl; 364 } 365 else 366 {} 367 } 368 break; 369 default: 370 cout << "The sip event type that not be precessed.the event " 371 "type is : " << osipEventPtr->type << endl; 372 break; 373 } 374 eXosip_event_free(osipEventPtr); 375 return NULL; 376 } 377 378 int main() 379 { 380 iCurrentStatus = 0; 381 //庫處理結果 382 int result = OSIP_SUCCESS; 383 //初始化庫 384 if (OSIP_SUCCESS != (result = eXosip_init())) 385 { 386 printf("eXosip_init failure.\n"); 387 return 1; 388 } 389 cout << "eXosip_init success." << endl; 390 eXosip_set_user_agent(NULL); 391 //監聽 392 if (OSIP_SUCCESS != eXosip_listen_addr(IPPROTO_UDP, NULL, UACPORTINT, 393 AF_INET, 0)) 394 { 395 printf("eXosip_listen_addr failure.\n"); 396 return 1; 397 } 398 //設置監聽網卡 399 if (OSIP_SUCCESS != eXosip_set_option( 400 EXOSIP_OPT_SET_IPV4_FOR_GATEWAY, 401 LISTEN_ADDR)) 402 { 403 return -1; 404 } 405 //開啟服務線程 406 pthread_t pthser; 407 if (0 != pthread_create(&pthser, NULL, serverHandle, NULL)) 408 { 409 printf("創建主服務失敗\n"); 410 return -1; 411 } 412 //事件用於等待 413 eXosip_event_t* osipEventPtr = NULL; 414 //開啟事件循環 415 while (true) 416 { 417 //等待事件 0的單位是秒,500是毫秒 418 osipEventPtr = ::eXosip_event_wait(0, 200); 419 //處理eXosip庫默認處理 420 { 421 usleep(500 * 1000); 422 eXosip_lock(); 423 //一般處理401/407采用庫默認處理 424 eXosip_default_action(osipEventPtr); 425 eXosip_unlock(); 426 } 427 //事件空繼續等待 428 if (NULL == osipEventPtr) 429 { 430 continue; 431 } 432 //開啟線程處理事件並在事件處理完畢將事件指針釋放 433 pthread_t pth; 434 if (0 != pthread_create(&pth, NULL, eventHandle, (void*) osipEventPtr)) 435 { 436 printf("創建線程處理事件失敗\n"); 437 continue; 438 } 439 osipEventPtr = NULL; 440 } 441 }
可以看到第一次收到了401報文,庫自動發送鑒權信息,然後收到了200OK報文。
可以看到收到200OK報文
收到200OK報文。並且可以看到expires為0了。
至此eXosip2庫實現注冊,全部功能完成。