一個用eXosip實現的UAC和UAS的例子(除錯成功)
阿新 • • 發佈:2019-01-01
經過一段時間的學習,對sip總算有了一點認識,在學習過程中,遇到了太多的問題,鬱悶過,惆悵過,但是一咬牙,還是過來了。令我感動的是,在網上遇到一些很熱心的朋友,不厭其煩地給我以解惑,感謝他們,尤其是友善的大狗,呵呵,希望將來有一天他能看到這篇文章。
我是利用eXosip協議棧進行開發的,網上有一篇<一個簡單的sip呼叫例子>,寫的不錯,但是好像有一些問題,而對於初學者來說,能拿到一個好的例子,對sip的理解可以到達事半功倍的效果。於是便把自己的寫的例子拿出來,讓大家參考一下,若有問題,歡迎指正。
只需把裡面的IP地址改正、編譯即可使用。
/******************************************
編譯方法:
gcc xxx.c -o xxx -leXosip2
****************************************/
/*******************UAS*****************************************************
本文可以任意轉載,但必須保留出處
作者:rainfish
網址:http://blog.csdn.net/bat603/
測試環境:eXosip3.0.1/redhat AS 4
***************************************************************************/
#include <eXosip2/eXosip.h>
#include <osip2/osip_mt.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.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: [email protected]";
char *dest_call = "sip:[email protected]:5060";
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, 5060, 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);
}
//按照規則,需要回復200 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");
}
}
}
/*******************UAC*****************************************************
本文可以任意轉載,但必須保留出處
作者:rainfish
網址:http://blog.csdn.net/bat603/
測試環境:eXosip3.0.1/redhat AS 4
***************************************************************************/
#include <eXosip2/eXosip.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
int
main (int argc, char *argv[])
{
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;
int id;
char *identity = "sip: [email protected]";
char *registerer = "sip:192.168.0.133:5060";
char *source_call = "sip:[email protected]";
char *dest_call = "sip:[email protected]:5060";
char command;
char tmp[4096];
char localip[128];
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");
}
i = eXosip_listen_addr (IPPROTO_UDP, NULL, 5060, 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 comand:/n");
scanf ("%c", &command);
getchar ();
switch (command)
{
case 'r':
printf ("This modal isn't commpleted!/n");
break;
case 'i':/* INVITE */
i = eXosip_call_build_initial_invite (&invite, dest_call, source_call, NULL, "This si a call for a conversation");
if (i != 0)
{
printf ("Intial INVITE failed!/n");
break;
}
//符合SDP格式,其中屬性a是自定義格式,也就是說可以存放自己的資訊,但是隻能是兩列,比如帳戶資訊
//但是經測試,格式:v o t必不可少,原因未知,估計是協議棧在傳輸時需要檢查的
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);
eXosip_unlock ();
flag1 = 1;
while (flag1)
{
je = eXosip_event_wait (0, 200);
if (je == NULL)
{
printf ("No response or the time is over!/n");
break;
}
switch (je->type)
{
case EXOSIP_CALL_INVITE:
printf ("a new invite reveived!/n");
break;
case EXOSIP_CALL_PROCEEDING:
printf ("proceeding!/n");
break;
case EXOSIP_CALL_RINGING:
printf ("ringing!/n");
// call_id = je->cid;
// dialog_id = je->did;
printf ("call_id is %d, dialog_id is %d /n", je->cid, je->did);
break;
case EXOSIP_CALL_ANSWERED:
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);
eXosip_call_build_ack (je->did, &ack);
eXosip_call_send_ack (je->did, ack);
flag1 = 0;
break;
case EXOSIP_CALL_CLOSED:
printf ("the other sid closed!/n");
break;
case EXOSIP_CALL_ACK:
printf ("ACK received!/n");
break;
default:
printf ("other response!/n");
break;
}
eXosip_event_free (je);
}
break;
case 'h':
printf ("Holded !/n");
eXosip_lock ();
eXosip_call_terminate (call_id, dialog_id);
eXosip_unlock ();
break;
case 'c':
printf ("This modal isn't commpleted!/n");
break;
case 's':
//傳輸INFO方法
eXosip_call_build_info (dialog_id, &info);
snprintf (tmp , 4096,
"hello,rainfish");
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 mothed :MESSAGE/n");
eXosip_message_build_request (&message, "MESSAGE", dest_call, source_call, NULL);
snprintf (tmp, 4096,
"hellor rainfish");
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);
}
我是利用eXosip協議棧進行開發的,網上有一篇<一個簡單的sip呼叫例子>,寫的不錯,但是好像有一些問題,而對於初學者來說,能拿到一個好的例子,對sip的理解可以到達事半功倍的效果。於是便把自己的寫的例子拿出來,讓大家參考一下,若有問題,歡迎指正。
只需把裡面的IP地址改正、編譯即可使用。
/******************************************
編譯方法:
gcc xxx.c -o xxx -leXosip2
****************************************/
/*******************UAS*****************************************************
本文可以任意轉載,但必須保留出處
作者:rainfish
網址:http://blog.csdn.net/bat603/
測試環境:eXosip3.0.1/redhat AS 4
***************************************************************************/
#include <eXosip2/eXosip.h>
#include <osip2/osip_mt.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.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:
char *dest_call = "sip:[email protected]:5060";
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, 5060, 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);
}
//按照規則,需要回復200 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");
}
}
}
/*******************UAC*****************************************************
本文可以任意轉載,但必須保留出處
作者:rainfish
網址:http://blog.csdn.net/bat603/
測試環境:eXosip3.0.1/redhat AS 4
***************************************************************************/
#include <eXosip2/eXosip.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
int
main (int argc, char *argv[])
{
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;
int id;
char *identity = "sip:
char *registerer = "sip:192.168.0.133:5060";
char *source_call = "sip:[email protected]";
char *dest_call = "sip:[email protected]:5060";
char command;
char tmp[4096];
char localip[128];
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");
}
i = eXosip_listen_addr (IPPROTO_UDP, NULL, 5060, 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 comand:/n");
scanf ("%c", &command);
getchar ();
switch (command)
{
case 'r':
printf ("This modal isn't commpleted!/n");
break;
case 'i':/* INVITE */
i = eXosip_call_build_initial_invite (&invite, dest_call, source_call, NULL, "This si a call for a conversation");
if (i != 0)
{
printf ("Intial INVITE failed!/n");
break;
}
//符合SDP格式,其中屬性a是自定義格式,也就是說可以存放自己的資訊,但是隻能是兩列,比如帳戶資訊
//但是經測試,格式:v o t必不可少,原因未知,估計是協議棧在傳輸時需要檢查的
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);
eXosip_unlock ();
flag1 = 1;
while (flag1)
{
je = eXosip_event_wait (0, 200);
if (je == NULL)
{
printf ("No response or the time is over!/n");
break;
}
switch (je->type)
{
case EXOSIP_CALL_INVITE:
printf ("a new invite reveived!/n");
break;
case EXOSIP_CALL_PROCEEDING:
printf ("proceeding!/n");
break;
case EXOSIP_CALL_RINGING:
printf ("ringing!/n");
// call_id = je->cid;
// dialog_id = je->did;
printf ("call_id is %d, dialog_id is %d /n", je->cid, je->did);
break;
case EXOSIP_CALL_ANSWERED:
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);
eXosip_call_build_ack (je->did, &ack);
eXosip_call_send_ack (je->did, ack);
flag1 = 0;
break;
case EXOSIP_CALL_CLOSED:
printf ("the other sid closed!/n");
break;
case EXOSIP_CALL_ACK:
printf ("ACK received!/n");
break;
default:
printf ("other response!/n");
break;
}
eXosip_event_free (je);
}
break;
case 'h':
printf ("Holded !/n");
eXosip_lock ();
eXosip_call_terminate (call_id, dialog_id);
eXosip_unlock ();
break;
case 'c':
printf ("This modal isn't commpleted!/n");
break;
case 's':
//傳輸INFO方法
eXosip_call_build_info (dialog_id, &info);
snprintf (tmp , 4096,
"hello,rainfish");
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 mothed :MESSAGE/n");
eXosip_message_build_request (&message, "MESSAGE", dest_call, source_call, NULL);
snprintf (tmp, 4096,
"hellor rainfish");
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);
}