参考:
https://www.cnblogs.com/elisha-blogs/p/3974534.html
源码地址:
https://download.savannah.gnu.org/releases
SIP协议有更新后,从网上找的这个例子不好用了,依新的接口要求我对UAC.C, UAS.C做了更新:
UAC.C如下:
#include <eXosip2/eXosip.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <netinet/in.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");
//初始化
struct eXosip_t *ctx;
ctx = eXosip_malloc();
if (ctx==NULL)return -1;
i=eXosip_init(ctx);
if(i!=0)
{
printf("Couldn't initialize eXosip!\n");
return -1;
}
else
{
printf("eXosip_init successfully!\n");
}
//绑定uac自己的端口15060,并进行端口监听
i=eXosip_listen_addr(ctx,IPPROTO_UDP,NULL,15060,AF_INET,0);
if(i!=0)
{
eXosip_quit(ctx);
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(ctx,&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(ctx);
i=eXosip_call_send_initial_invite(ctx,invite); //invite SIP INVITE message to send
eXosip_unlock(ctx);
//发送了INVITE消息,等待应答
flag1=1;
while(flag1)
{
je=eXosip_event_wait(ctx,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(ctx,je->did,&ack);
eXosip_call_send_ack(ctx,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(ctx);
eXosip_call_terminate(ctx,call_id,dialog_id);
eXosip_unlock(ctx);
break;
case 'c':
printf("This modal is not commpleted!\n");
break;
case 's': //传输INFO方法
eXosip_call_build_info(ctx,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(ctx,dialog_id,info);
break;
case 'm':
//传输MESSAGE方法,也就是即时消息,和INFO方法相比,我认为主要区别是:
//MESSAGE不用建立连接,直接传输信息,而INFO消息必须在建立INVITE的基础上传输
printf("the method : MESSAGE\n");
eXosip_message_build_request(ctx,&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(ctx,message);
break;
case 'q':
eXosip_quit(ctx);
printf("Exit the setup!\n");
flag=0;
break;
}
}
return(0);
}
UAS.C如下:
# include <eXosip2/eXosip.h>
# include <stdio.h>
# include <stdlib.h>
# include <stdarg.h>
# include <netinet/in.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
struct eXosip_t *ctx;
ctx = eXosip_malloc();
if (ctx==NULL)return -1;
i=eXosip_init(ctx);
if (i != 0)
{
printf ("Can't initialize eXosip!\n");
return -1;
}
else
{
printf ("eXosip_init successfully!\n");
}
i = eXosip_listen_addr (ctx,IPPROTO_UDP, NULL, 15061, AF_INET, 0);
if (i != 0)
{
eXosip_quit (ctx);
fprintf (stderr, "eXosip_listen_addr error!\nCouldn't initialize transport layer!\n");
}
for(;;)
{
//侦听是否有消息到来
je = eXosip_event_wait (ctx,0,50);
//协议栈带有此语句,具体作用未知
eXosip_lock (ctx);
eXosip_default_action (ctx,je);
eXosip_automatic_refresh (ctx);
eXosip_unlock (ctx);
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 (ctx,je->tid, 200,&answer);
eXosip_message_send_answer (ctx,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 (ctx,je->did);
call_id = je->cid;
dialog_id = je->did;
eXosip_lock (ctx);
eXosip_call_send_answer (ctx,je->tid, 180, NULL);
i = eXosip_call_build_answer (ctx,je->tid, 200, &answer);
if (i != 0)
{
printf ("This request msg is invalid!Cann't response!\n");
eXosip_call_send_answer (ctx,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 (ctx,je->tid, 200, answer);
printf ("send 200 over!\n");
}
eXosip_unlock (ctx);
//显示出在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 (ctx,je->tid, 200, &answer);
if (i != 0)
{
printf ("This request msg is invalid!Cann't response!\n");
eXosip_call_send_answer (ctx,je->tid, 400, NULL);
}
else
{
eXosip_call_send_answer (ctx,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 (ctx);
i = eXosip_call_build_answer (ctx,je->tid, 200, &answer);
if (i == 0)
{
eXosip_call_send_answer (ctx,je->tid, 200, answer);
}
eXosip_unlock (ctx);
{
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");
}
}
}
编译时也多了一个库选项:
gcc uas.c -o uas -losip2 -leXosip2 -lpthread -losipparser2
gcc uac.c -o uac -losip2 -leXosip2 -lpthread -losipparser2
这样就又能编译过了,另外好象网上最新库源码:OSIP2与EXOSIP2不一致,都是2-4.0.0的我编译成功了,版本不一致会报一个错,我没有仔细看过。