程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 基於GBT28181:SIP協議組件開發-----------第五篇SIP注冊流程eXosip2實現(二),gbt28181exosip2

基於GBT28181:SIP協議組件開發-----------第五篇SIP注冊流程eXosip2實現(二),gbt28181exosip2

編輯:C++入門知識

基於GBT28181:SIP協議組件開發-----------第五篇SIP注冊流程eXosip2實現(二),gbt28181exosip2


原創文章,引用請保證原文完整性,尊重作者勞動,原文地址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庫去發送。

1.eXosip2 API介紹

本章中要用的eXosip2庫的API做個簡單的介紹和使用方法。

1.1 初始化庫

在使用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事件了。

 

1.2 接受事件

初始化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;                    

實際在應用的時候為了提高服務的並發性,一般並不用上面這種服務方式,因為上面這樣,如果接受到某個事件,而這個事件的處理事件非常長的話,會影響效率,導致其他事件阻塞等待,得不到即使處理。所以一般采用並發服務器的設計思想,即在接受到一個事件後,分配一個線程來處理。當然如果想效率更高,想節省創建線程的時間,可以在系統一起動後,分配一定大小的線程池,有事件到來的話,直接從線程池中獲取線程處理。

 

1.3 消息分析

每個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     

 

2.UAC代碼實例 

  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, &regMsg);
164         if (registerId <= 0)
165         {
166             return -1;
167         }
168     }
169     else
170     {
171         // 構建注冊消息
172         ret = ::eXosip_register_build_register(registerId, expires, &regMsg);
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(&regMsg->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 }

 

3.測試效果

 3.1 啟動後

3.2 輸入0 注冊

可以看到第一次收到了401報文,庫自動發送鑒權信息,然後收到了200OK報文。

3.3 然後輸入1刷新

可以看到收到200OK報文

3.4 輸入2注銷後

收到200OK報文。並且可以看到expires為0了。

至此eXosip2庫實現注冊,全部功能完成。

 




  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved