SIP注冊流程eXosip2的實作過程分析
原創文章,引用請保證原文完整性,尊重作者勞動,原文位址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, ®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 }
3.測試效果
3.1 啟動後
3.2 輸入0 注冊
可以看到第一次收到了401封包,庫自動發送鑒權資訊,然後收到了200OK封包。
3.3 然後輸入1重新整理
可以看到收到200OK封包
3.4 輸入2登出後
收到200OK封包。并且可以看到expires為0了。
至此eXosip2庫實作注冊,全部功能完成。
歡迎技術交流溝通,轉載請注明出處并保持作品的完整性。
作者:程式人生 qq1269122125