【GBT28181開發:SIP協議實踐】之Windows下編譯eXosip、osip
今天開始了SIP開源庫的學習,我選擇了osip和eXosip,但是這兩個庫的編譯使用有些麻煩,原始碼下來之後編譯會出現很多問題,網上也沒有找到完整的編譯介紹,只能一步一步的找辦法解決,最後終於編譯成功!先大概記錄下編譯過程,後面還要再整理下。
期間還向CSDN論壇求助了下,但是還沒有反應:http://bbs.csdn.net/topics/390499635?page=1#post-394866714
描述:error LNK2019: 無法解析的外部符號 _osip_transaction_set_naptr_record,該符號在函式 __eXosip_transaction_init 中被引用
原因:開原始碼的庫有些函式沒有匯出,需要手動在def檔案中新增
解決方法:第二步的2,3兩小步就是為了解決這個問題。
第一步,下載osip和eXosip
osip: http://ftp.twaren.net/Unix/NonGNU//osip/libosip2-3.6.0.tar.gzeXosip: http://download.savannah.gnu.org/releases/exosip/libeXosip2-3.6.0.tar.gz
第二步,解壓,編譯osip:
1.進入libosip2-3.6.0\platform\vsnet目錄,用VS2010直接開啟osip.sln檔案,專案自動轉換
2.更改libosip2-3.6.0\platform\vsnet\osip2.def 檔案,在檔案末尾追加
osip_transaction_set_naptr_record @138
3.更改libosip2-3.6.0\platform\vsnet\osipparser2.def 檔案,在檔案末尾追加
osip_strcasestr @417
__osip_uri_escape_userinfo @418
4.先編譯osipparser2,再編譯osip2,最後在libosip2-3.6.0\platform\vsnet\Debug DLL下生成庫檔案:
osip2.lib
osip2.dllosipparser2.lib
osipparser2.dll
第三步,解壓,編譯eXosip
進入libeXosip2-3.6.0\platform\vsnet目錄,用VS2010直接開啟eXosip.sln檔案,專案自動轉換:
1.將osip2.lib,osip2.dll,osipparser2.lib,osipparser2.dll拷貝到Debug目錄下
2.C/C++ --> 前處理器 --> 前處理器定義: 刪除HAVE_OPENSSL_SSL_H
3.C/C++ --> 常規 --> 附加包含目錄: 將osip的標頭檔案libosip2-3.6.0\include包含進來
4.編譯,生成eXosip.lib
第四步,新建UAC例子專案
1.連結器 --> 輸入 --> 附加依賴項:增加靜態庫引用:Dnsapi.lib;Iphlpapi.lib;Ws2_32.lib;osip2.lib;osipparser2.lib;exosip.lib;
2.C/C++ -->常規 -->附加包含目錄: 將osip和eXosip的標頭檔案libosip2-3.6.0\include、libeXosip2-3.6.0\include包含進來
3.連結器 --> 常規 --> 附加庫目錄:將osip和eXosip的庫包含進來,libeXosip2-3.6.0\platform\vsnet\Debug
4.編譯UAC程式碼:
<span style="font-family: SimSun;">#include <eXosip2/eXosip.h>
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
int main(int argc,char *argv[])
{
struct eXosip_t *context_eXosip;
eXosip_event_t *je;
osip_message_t *reg=NULL;
osip_message_t *invite=NULL;
osip_message_t *ack=NULL;
osip_message_t *info=NULL;
osip_message_t *message=NULL;
int call_id,dialog_id;
int i,flag;
int flag1=1;
char *identity="sip:140@127.0.0.1"; //UAC1,埠是15060
char *registar="sip:133@127.0.0.1:15061"; //UAS,埠是15061
char *source_call="sip:140@127.0.0.1";
char *dest_call="sip:133@127.0.0.1:15061";
//identify和register這一組地址是和source和destination地址相同的
//在這個例子中,uac和uas通訊,則source就是自己的地址,而目的地址就是uac1的地址
char command;
char tmp[4096];
printf("r 向伺服器註冊\n\n");
printf("c 取消註冊\n\n");
printf("i 發起呼叫請求\n\n");
printf("h 結束通話\n\n");
printf("q 推出程式\n\n");
printf("s 執行方法INFO\n\n");
printf("m 執行方法MESSAGE\n\n");
//初始化
i=eXosip_init();
if(i!=0)
{
printf("Couldn't initialize eXosip!\n");
return -1;
}
else
{
printf("eXosip_init successfully!\n");
}
//繫結uac自己的埠15060,並進行埠監聽
i=eXosip_listen_addr(IPPROTO_UDP,NULL,15060,AF_INET,0);
if(i!=0)
{
eXosip_quit();
fprintf(stderr,"Couldn't initialize transport layer!\n");
return -1;
}
flag=1;
while(flag)
{
//輸入命令
printf("Please input the command:\n");
scanf("%c",&command);
getchar();
switch(command)
{
case 'r':
printf("This modal is not completed!\n");
break;
case 'i'://INVITE,發起呼叫請求
i=eXosip_call_build_initial_invite(&invite,dest_call,source_call,NULL,"This is a call for conversation");
if(i!=0)
{
printf("Initial INVITE failed!\n");
break;
}
//符合SDP格式,其中屬性a是自定義格式,也就是說可以存放自己的資訊,
//但是隻能有兩列,比如帳戶資訊
//但是經過測試,格式vot必不可少,原因未知,估計是協議棧在傳輸時需要檢查的
_snprintf(tmp,4096,
"v=0\r\n"
"o=anonymous 0 0 IN IP4 0.0.0.0\r\n"
"t=1 10\r\n"
"a=username:rainfish\r\n"
"a=password:123\r\n");
osip_message_set_body(invite,tmp,strlen(tmp));
osip_message_set_content_type(invite,"application/sdp");
eXosip_lock();
i=eXosip_call_send_initial_invite(invite); //invite SIP INVITE message to send
eXosip_unlock();
//傳送了INVITE訊息,等待應答
flag1=1;
while(flag1)
{
je=eXosip_event_wait(0,200); //Wait for an eXosip event
//(超時時間秒,超時時間毫秒)
if(je==NULL)
{
printf("No response or the time is over!\n");
break;
}
switch(je->type) //可能會到來的事件型別
{
case EXOSIP_CALL_INVITE: //收到一個INVITE請求
printf("a new invite received!\n");
break;
case EXOSIP_CALL_PROCEEDING: //收到100 trying訊息,表示請求正在處理中
printf("proceeding!\n");
break;
case EXOSIP_CALL_RINGING: //收到180 Ringing應答,表示接收到INVITE請求的UAS正在向被叫使用者振鈴
printf("ringing!\n");
printf("call_id is %d,dialog_id is %d \n",je->cid,je->did);
break;
case EXOSIP_CALL_ANSWERED: //收到200 OK,表示請求已經被成功接受,使用者應答
printf("ok!connected!\n");
call_id=je->cid;
dialog_id=je->did;
printf("call_id is %d,dialog_id is %d \n",je->cid,je->did);
//回送ack應答訊息
eXosip_call_build_ack(je->did,&ack);
eXosip_call_send_ack(je->did,ack);
flag1=0; //推出While迴圈
break;
case EXOSIP_CALL_CLOSED: //a BYE was received for this call
printf("the other sid closed!\n");
break;
case EXOSIP_CALL_ACK: //ACK received for 200ok to INVITE
printf("ACK received!\n");
break;
default: //收到其他應答
printf("other response!\n");
break;
}
eXosip_event_free(je); //Free ressource in an eXosip event
}
break;
case 'h': //結束通話
printf("Holded!\n");
eXosip_lock();
eXosip_call_terminate(call_id,dialog_id);
eXosip_unlock();
break;
case 'c':
printf("This modal is not commpleted!\n");
break;
case 's': //傳輸INFO方法
eXosip_call_build_info(dialog_id,&info);
_snprintf(tmp,4096,"\nThis is a sip message(Method:INFO)");
osip_message_set_body(info,tmp,strlen(tmp));
//格式可以任意設定,text/plain代表文字資訊;
osip_message_set_content_type(info,"text/plain");
eXosip_call_send_request(dialog_id,info);
break;
case 'm':
//傳輸MESSAGE方法,也就是即時訊息,和INFO方法相比,我認為主要區別是:
//MESSAGE不用建立連線,直接傳輸資訊,而INFO訊息必須在建立INVITE的基礎上傳輸
printf("the method : MESSAGE\n");
eXosip_message_build_request(&message,"MESSAGE",dest_call,source_call,NULL);
//內容,方法, to ,from ,route
_snprintf(tmp,4096,"This is a sip message(Method:MESSAGE)");
osip_message_set_body(message,tmp,strlen(tmp));
//假設格式是xml
osip_message_set_content_type(message,"text/xml");
eXosip_message_send_request(message);
break;
case 'q':
eXosip_quit();
printf("Exit the setup!\n");
flag=0;
break;
}
}
return(0);
}</span>
第五步,編譯UAS專案:
與UAC同樣的設定後,編譯
<span style="font-family: SimSun;"># include <eXosip2/eXosip.h>
# include <stdio.h>
# include <stdlib.h>
# include <Winsock2.h>
int main (int argc, char *argv[])
{
eXosip_event_t *je = NULL;
osip_message_t *ack = NULL;
osip_message_t *invite = NULL;
osip_message_t *answer = NULL;
sdp_message_t *remote_sdp = NULL;
int call_id, dialog_id;
int i,j;
int id;
char *sour_call = "sip:140@127.0.0.1";
char *dest_call = "sip:133@127.0.0.1:15060";//client ip
char command;
char tmp[4096];
char localip[128];
int pos = 0;
//初始化sip
i = eXosip_init ();
if (i != 0)
{
printf ("Can't initialize eXosip!\n");
return -1;
}
else
{
printf ("eXosip_init successfully!\n");
}
i = eXosip_listen_addr (IPPROTO_UDP, NULL, 15061, AF_INET, 0);
if (i != 0)
{
eXosip_quit ();
fprintf (stderr, "eXosip_listen_addr error!\nCouldn't initialize transport layer!\n");
}
for(;;)
{
//偵聽是否有訊息到來
je = eXosip_event_wait (0,50);
//協議棧帶有此語句,具體作用未知
eXosip_lock ();
eXosip_default_action (je);
eXosip_automatic_refresh ();
eXosip_unlock ();
if (je == NULL)//沒有接收到訊息
continue;
// printf ("the cid is %s, did is %s/n", je->did, je->cid);
switch (je->type)
{
case EXOSIP_MESSAGE_NEW://新的訊息到來
printf (" EXOSIP_MESSAGE_NEW!\n");
if (MSG_IS_MESSAGE (je->request))//如果接受到的訊息型別是MESSAGE
{
{
osip_body_t *body;
osip_message_get_body (je->request, 0, &body);
printf ("I get the msg is: %s\n", body->body);
//printf ("the cid is %s, did is %s/n", je->did, je->cid);
}
//按照規則,需要回復OK資訊
eXosip_message_build_answer (je->tid, 200,&answer);
eXosip_message_send_answer (je->tid, 200,answer);
}
break;
case EXOSIP_CALL_INVITE:
//得到接收到訊息的具體資訊
printf ("Received a INVITE msg from %s:%s, UserName is %s, password is %s\n",je->request->req_uri->host,
je->request->req_uri->port, je->request->req_uri->username, je->request->req_uri->password);
//得到訊息體,認為該訊息就是SDP格式.
remote_sdp = eXosip_get_remote_sdp (je->did);
call_id = je->cid;
dialog_id = je->did;
eXosip_lock ();
eXosip_call_send_answer (je->tid, 180, NULL);
i = eXosip_call_build_answer (je->tid, 200, &answer);
if (i != 0)
{
printf ("This request msg is invalid!Cann't response!\n");
eXosip_call_send_answer (je->tid, 400, NULL);
}
else
{
snprintf (tmp, 4096,
"v=0\r\n"
"o=anonymous 0 0 IN IP4 0.0.0.0\r\n"
"t=1 10\r\n"
"a=username:rainfish\r\n"
"a=password:123\r\n");
//設定回覆的SDP訊息體,下一步計劃分析訊息體
//沒有分析訊息體,直接回復原來的訊息,這一塊做的不好。
osip_message_set_body (answer, tmp, strlen(tmp));
osip_message_set_content_type (answer, "application/sdp");
eXosip_call_send_answer (je->tid, 200, answer);
printf ("send 200 over!\n");
}
eXosip_unlock ();
//顯示出在sdp訊息體中的attribute 的內容,裡面計劃存放我們的資訊
printf ("the INFO is :\n");
while (!osip_list_eol ( &(remote_sdp->a_attributes), pos))
{
sdp_attribute_t *at;
at = (sdp_attribute_t *) osip_list_get ( &remote_sdp->a_attributes, pos);
printf ("%s : %s\n", at->a_att_field, at->a_att_value);//這裡解釋了為什麼在SDP訊息體中屬性a裡面存放必須是兩列
pos ++;
}
break;
case EXOSIP_CALL_ACK:
printf ("ACK recieved!\n");
// printf ("the cid is %s, did is %s/n", je->did, je->cid);
break;
case EXOSIP_CALL_CLOSED:
printf ("the remote hold the session!\n");
// eXosip_call_build_ack(dialog_id, &ack);
//eXosip_call_send_ack(dialog_id, ack);
i = eXosip_call_build_answer (je->tid, 200, &answer);
if (i != 0)
{
printf ("This request msg is invalid!Cann't response!\n");
eXosip_call_send_answer (je->tid, 400, NULL);
}
else
{
eXosip_call_send_answer (je->tid, 200, answer);
printf ("bye send 200 over!\n");
}
break;
case EXOSIP_CALL_MESSAGE_NEW://至於該型別和EXOSIP_MESSAGE_NEW的區別,原始碼這麼解釋的
/*
// request related events within calls (except INVITE)
EXOSIP_CALL_MESSAGE_NEW, < announce new incoming request.
// response received for request outside calls
EXOSIP_MESSAGE_NEW, < announce new incoming request.
我也不是很明白,理解是:EXOSIP_CALL_MESSAGE_NEW是一個呼叫中的新的訊息到來,比如ring trying都算,所以在接受到後必須判斷
該訊息型別,EXOSIP_MESSAGE_NEW而是表示不是呼叫內的訊息到來。
該解釋有不妥地方,僅供參考。
*/
printf(" EXOSIP_CALL_MESSAGE_NEW\n");
if (MSG_IS_INFO(je->request) ) //如果傳輸的是INFO方法
{
eXosip_lock ();
i = eXosip_call_build_answer (je->tid, 200, &answer);
if (i == 0)
{
eXosip_call_send_answer (je->tid, 200, answer);
}
eXosip_unlock ();
{
osip_body_t *body;
osip_message_get_body (je->request, 0, &body);
printf ("the body is %s\n", body->body);
}
}
break;
default:
printf ("Could not parse the msg!\n");
}
}
}
</span>
例子已經上傳:http://download.csdn.net/detail/longlong530/5647749
執行示意圖:
原文地址:
相關文章
- 【GBT28181開發:SIP協議實踐】之註冊流程協議
- VoIP通話之sip協議協議
- SIP (Session Initiation Protocol) 協議SessionProtocol協議
- MOSN 多協議擴充套件開發實踐協議套件
- DDS協議解讀及測試開發實踐協議
- 第四章 SIP協議協議
- P2P通訊標準協議(四)之SIP協議
- MQTT協議實踐MQQT協議
- Android開發實踐:編譯VLC-for-androidAndroid編譯
- Windows下編譯OpenOCDWindows編譯
- Windows下編譯VLCWindows編譯
- iOS開發之VPN協議(理論)iOS協議
- 後臺開發-核心技術與應用實踐--TCP協議TCP協議
- 在Windows下編譯WebRTCWindows編譯Web
- Windows下編譯使用AliyunOSSCSDKWindows編譯
- windows 下c++編譯WindowsC++編譯
- 【FFmpeg】Windows下FFmpeg編譯Windows編譯
- Windows下編譯fast rcnnWindows編譯ASTCNN
- Windows下編譯TriangleWindows編譯
- QUIC 協議初探 - iOS 實踐UI協議iOS
- Windows下編譯Caffe並編譯Matlab介面Windows編譯Matlab
- Windows下搭建Android NDK開發環境及命令列編譯WindowsAndroid開發環境命令列編譯
- 編譯提速最佳實踐編譯
- windows下編譯安裝thriftWindows編譯
- Windows下編譯使用AliyunOSSPHPSDKWindows編譯PHP
- windows 下編譯c檔案Windows編譯
- RPC協議實踐入門RPC協議
- Socket開發框架之資料傳輸協議框架協議
- exosip
- Windows下CMake編譯安裝OpenCVWindows編譯OpenCV
- 【FFmpeg】Windows下64位ffmpeg編譯Windows編譯
- WPA Supplicant 在Windows下的編譯Windows編譯
- windows下編譯tflite-runtimeWindows編譯
- WebSocket原理與實踐(二)---WebSocket協議Web協議
- 時間同步協議NTP - 原理&實踐協議
- 最佳實踐(3):Windows應用開發Windows
- bluez藍芽協議棧交叉編譯移植教程(附帶視訊下載地址)藍芽協議編譯
- 微信開發介面API協議API協議