VS2013下編譯osip和exosip的5.0版本靜態庫及搭建和簡單例子的實現

參考文章:

https://www.cnblogs.com/baiduboy/p/6089197.html

http://blog.csdn.net/bikeytang/article/details/53448735

http://blog.csdn.net/dgyanyong/article/details/50818101

http://www.360doc.com/content/16/0510/09/9408846_557774783.shtml

一、平臺的搭建

1.下載庫文件

http://ftp.gnu.org/gnu/osip/下載osip的5.0版本:libosip2-5.0.0.tar.gz

http://www.antisip.com/download/exosip2/下載exosip的5.0版本:libexosip2-5.0.0.tar.gz

從連接https://c-ares.haxx.se/處下載:c-ares(我下載的是最新版本)

然後解壓統一放到一個文件夾下,修改文件夾的名字:

 libexosip2-xxx 修改爲exosip

 libosip2-xxx 修改爲osip 

 c-ares-xxx 修改爲c-ares

2.進入exosip文件夾目錄:exosip\platform\vsnet中,打開eXosip.sln,會自動進行加載

打開libcares.vcxproj,在裏面加入三個文件選項

ares_platform.h

ares_platform.c

ares_create_query.c

否則在後面例子中編譯時調用生成庫時會出現錯誤

error LNK2019:無法解析的外部符號_ares_getplatform,該符號在函數_get_DNS_Registry中被引用。 

error LNK2019:無法解析的外部符號_ares_create_query,該符號在函數_ares_query中被引用。

3.點擊右側工程exosip,右鍵選擇屬性,配置屬性-> C/C++ -> 預處理器 ->預處理器定義中,去掉一些預定義(HAVE_OPENSSL_SSL_H,TSC_SUPPORT),不然在編譯的時候出現

error C1083: 無法打開包括文件: “openssl/opensslv.h”: No such file or directory 
IntelliSense: 無法打開 源 文件 “tsc_socket_api.h” 
IntelliSense: 未定義標識符 “tsc_config”

4.這個版本少eXrefer_api.c文件,從之前的版本中找到eXrefer_api.c,並添加到相應的位置

5.修改\osip\platform\vsnet目錄下面的osip2.def、osipparser2.def兩個文件,在文件的末尾導出這些函數,具體如下: 打開osip2.def文件,在最末尾加一行:

osip_transaction_set_naptr_record  @138 

打開osipparser2.def文件,在文件最後添加:  

osip_realloc @416     

osip_strcasestr @417     

__osip_uri_escape_userinfo @418 

6.Win7系統可能會出現如下編譯警告:

   osip\include\osipparser2/internal.h(71): warning C4067: 預處理器指令後有意外標記 - 應輸入換行符
   osip\include\osip2/internal.h(163): warning C4067: 預處理器指令後有意外標記 - 應輸入換行符

   修改如下:
   osip/include/osip2/internal.h和osip/include/osipparser2/internal.h
   add start和add end中間部分爲添加代碼

   #if (_MSC_VER >= 1700) && !defined(_USING_V110_SDK71_)
   #include <winapifamily.h>
   // add start 增加WINAPI_FAMILY_ONE_PARTITION定義
   /* Macro to determine if only one partition is enabled from a set */
   #define WINAPI_FAMILY_ONE_PARTITION(PartitionSet, Partition) ((WINAPI_FAMILY & PartitionSet) == Partition)
   // add end

   #endif

7.配置屬性->C/C++->代碼生成->運行庫->多線程調試 DLL (/MDd) 改爲 多線程調試 (/MTd)
  否則出現錯誤 LIBCMTD.lib(dbgheap.obj) : error LNK2005: __CrtSetCheckCount 已經在 MSVCRTD.lib(MSVCR120D.dll) 中定義

8.點擊生成->生成解決方案進行編譯,編譯成功!由於eXosip依賴於libcares、osip2、osipparser2這3個庫。所以直接編譯eXosip即可

二、簡單例子的實現

1.首先編寫代碼UAS,建立工程UAS,源代碼UASmain.c

2.工程名-->右擊-->屬性-->配置屬性-->鏈接器 --> 輸入 -->附加依賴項:增加靜態庫引用:Dnsapi.lib;Iphlpapi.lib;ws2_32.lib;eXosip.lib;osip2.lib;osipparser2.lib;Qwave.lib;libcares.lib;delayimp.lib;

3、 工程名-->右擊-->屬性-->配置屬性-->C/C++ -->常規 -->附加包含目錄: 將osip和eXosip的頭文件include包含進來

4、 工程名-->右擊-->屬性-->配置屬性-->鏈接器 --> 常規  --> 加附庫目錄:將eXosip的庫包含進來,exosip\platform\vsnet\v120\Win32\Debug

5.工程名-->右擊-->屬性-->配置屬性-->C/C++ -->預處理器-->預處理器定義:添加_CRT_SECURE_NO_DEPRECATE,可以防止scanf的錯誤

代碼如下:

# 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_malloc();
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:[email protected]";
char *dest_call = "sip:[email protected]:15060";//client ip  
//char command;  
char tmp[4096];
//char localip[128];  
int pos = 0;
//初始化sip  
i = eXosip_init(context_eXosip);
if (i != 0)
{
printf("Can't initialize eXosip!\n");
return -1;
}
else
{
printf("eXosip_init successfully!\n");
}
i = eXosip_listen_addr(context_eXosip, IPPROTO_UDP, NULL, 15061, AF_INET, 0);
if (i != 0)
{
eXosip_quit(context_eXosip);
fprintf(stderr, "eXosip_listen_addr error!\nCouldn't initialize transport layer!\n");
}
for (;;)
{
//偵聽是否有消息到來  
je = eXosip_event_wait(context_eXosip, 0, 50);
//協議棧帶有此語句,具體作用未知  
eXosip_lock(context_eXosip);
eXosip_default_action(context_eXosip, je);
//eXosip_automatic_refresh(context_eXosip);
eXosip_unlock(context_eXosip);
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(context_eXosip, je->tid, 200, &answer);
eXosip_message_send_answer(context_eXosip, 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(context_eXosip, je->did);
call_id = je->cid;
dialog_id = je->did;


eXosip_lock(context_eXosip);
eXosip_call_send_answer(context_eXosip, je->tid, 180, NULL);
i = eXosip_call_build_answer(context_eXosip, je->tid, 200, &answer);
if (i != 0)
{
printf("This request msg is invalid!Cann't response!\n");
eXosip_call_send_answer(context_eXosip, 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(context_eXosip, je->tid, 200, answer);
printf("send 200 over!\n");
}
eXosip_unlock(context_eXosip);


//顯示出在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(context_eXosip, je->tid, 200, &answer);
if (i != 0)
{
printf("This request msg is invalid!Cann't response!\n");
eXosip_call_send_answer(context_eXosip, je->tid, 400, NULL);


}
else
{
eXosip_call_send_answer(context_eXosip, 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(context_eXosip);
i = eXosip_call_build_answer(context_eXosip, je->tid, 200, &answer);
if (i == 0)
{
eXosip_call_send_answer(context_eXosip, je->tid, 200, answer);
}
eXosip_unlock(context_eXosip);
{
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");
}
}
}

6.編寫UAC,建立工程UAC,源代碼UACmian.c

設置同上面的UAS一樣

代碼如下:

#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_malloc();


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:[email protected]";   //UAC1,端口是15060  
char *registar = "sip:[email protected]:15061"; //UAS,端口是15061  
char *source_call = "sip:[email protected]";
char *dest_call = "sip:[email protected]:15061";
//identify和register這一組地址是和source和destination地址相同的  
//在這個例子中,uac和uas通信,則source就是自己的地址,而目的地址就是uac1的地址  
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(context_eXosip);


if (i != 0)
{
printf("Couldn't initialize eXosip!\n");
return -1;
}
else
{
printf("eXosip_init successfully!\n");
}


//綁定uac自己的端口15060,並進行端口監聽  
i = eXosip_listen_addr(context_eXosip, IPPROTO_UDP, NULL, 15060, AF_INET, 0);
if (i != 0)
{
eXosip_quit(context_eXosip);
return -1;
}
flag = 1;
char command;
while (flag)
{
//輸入命令  
printf("Please input the command:\n");
scanf("%c", &command);
getchar();
printf("command = %c\n", command);


switch (command)
{
case 'r':
printf("This modal is not completed!\n");
break;
case 'i'://INVITE,發起呼叫請求  
i = eXosip_call_build_initial_invite(context_eXosip, &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(context_eXosip);
i = eXosip_call_send_initial_invite(context_eXosip, invite); //invite SIP INVITE message to send  
eXosip_unlock(context_eXosip);


//發送了INVITE消息,等待應答  
flag1 = 1;
while (flag1)
{
je = eXosip_event_wait(context_eXosip, 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(context_eXosip, je->did, &ack);
eXosip_call_send_ack(context_eXosip, 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(context_eXosip);
eXosip_call_terminate(context_eXosip, call_id, dialog_id);
eXosip_unlock(context_eXosip);
break;


case 'c':
printf("This modal is not commpleted!\n");
break;


case 's': //傳輸INFO方法  
eXosip_call_build_info(context_eXosip, 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(context_eXosip, dialog_id, info);
break;


case 'm':
//傳輸MESSAGE方法,也就是即時消息,和INFO方法相比,我認爲主要區別是:  
//MESSAGE不用建立連接,直接傳輸信息,而INFO消息必須在建立INVITE的基礎上傳輸  
printf("the method : MESSAGE\n");
eXosip_message_build_request(context_eXosip, &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(context_eXosip, message);
break;


case 'q':
eXosip_quit(context_eXosip);
printf("Exit the setup!\n");
flag = 0;
break;
}
}


return(0);
}

兩個工程同時運行,點擊調試-->開始調試,會出現下面現象:



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章