UAC(User Agent Client) 和 UAS(User Agent Server)

SIP协议采用Client/Server模型。每一个请求(Request)触发服务器的一个操作;每个操作被称为方法(Method);每个请求除了指明具体方法外,还携带了一系列的头域(Header field),这些头域携带了各种业务信息;除了头域外,消息中还可以携带任意类型的消息体(Body),呼叫中最常使用的SDP信息就是通过消息体在Client和Server之间传递的。Server从接收到请求到处理完毕,要回复多个临时响应(Response),和一个终结响应(Response),终结响应有且只有一个。
请求和他的所有响应构成一个事务(Transaction),一个完整的呼叫过程包含多个事务,比如呼叫建立和呼叫释放就是
两个相互独立的事务。
用户代理(User Agent)是发起或者接收呼叫的逻辑实体。

用户代理客户端-UAC(User Agent Client),用于发起请求;
用户代理服务器-UAS(User Agent Server),用于接收请求。UAC/UAS的划分是针对一个事务的。在一个呼叫中的多个事务里,UACUAS的角色是可以互换的。例如在A和B的呼叫中,A向B发起呼叫,在呼叫建立的事务中,A是UAC,B是UAS;呼叫结束时,B先挂机,在呼叫释放的事务中,B是UAC,A是UAS。换句话说,每个一般的UA都是UASUAC的结合体。
UA的实际物理形态有:IP Phone,SoftPhone,GateWay......
Proxy Server作为一个网络逻辑实体代理客户端转发请求或者响应;同Proxy Server类似的还有一种设备是B2BUA,
顾名思义,就是背背的两个UA组成的一个逻辑实体,它作为UAS终结一个事务,同时作为UAC发起另外一个事务。Proxy Server同B2BUA相比,Proxy Server是一个事务传递过程中的中间节点,而B2BUA将一个事务转变成另一个事务。
在SIP组网中还包括Location Server、Registrar、Redirect Server,分别负责维护地址映射表,注册管理,呼叫重定向。他们和Proxy Server 可以在同一台设备上也可以运行于不同的设备上。SIP Server是Proxy Server、Location Server、Registrar、Redirect Server的总称。
SIP Server采用B2BUA模型。接受请求端为UAS端,代理转发或主动发起请求端为UAC端,整个SIPServr为UAC/UAS的组合体。通过UAC/UAS之间消息交互完成会话的建立、改变、结束的阶段。SIP Server协助网关交换媒体信息,
但不参与会话建立后媒体流传输。 
 
 
代码 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=; 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[]; 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!=)
{
printf("Couldn't initialize eXosip!\n");
return -;
}
else
{
printf("eXosip_init successfully!\n");
} //绑定uac自己的端口15060,并进行端口监听
i=eXosip_listen_addr(IPPROTO_UDP,NULL,,AF_INET,);
if(i!=)
{
eXosip_quit();
fprintf(stderr,"Couldn't initialize transport layer!\n");
return -;
}
flag=; 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!=)
{
printf("Initial INVITE failed!\n");
break;
}
//符合SDP格式,其中属性a是自定义格式,也就是说可以存放自己的信息,
//但是只能有两列,比如帐户信息
//但是经过测试,格式vot必不可少,原因未知,估计是协议栈在传输时需要检查的
snprintf(tmp,,
"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=;
while(flag1)
{
je=eXosip_event_wait(,); //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=; //推出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 with 200ok to INVITE (就是发送INVITE后,收到了对该INVITE的ACK)
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,,"\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,,"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=;
break;
}
} return();
}

代码 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[];
char localip[];
int pos = ;
//初始化sip
i = eXosip_init ();
if (i != )
{
printf ("Can't initialize eXosip!\n");
return -;
}
else
{
printf ("eXosip_init successfully!\n");
}
i = eXosip_listen_addr (IPPROTO_UDP, NULL, , AF_INET, );
if (i != )
{
eXosip_quit ();
fprintf (stderr, "eXosip_listen_addr error!\nCouldn't initialize transport layer!\n");
}
for(;;)
{
//侦听是否有消息到来
je = eXosip_event_wait (,);
//协议栈带有此语句,具体作用未知
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, , &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, ,&answer);
eXosip_message_send_answer (je->tid, ,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, , NULL);
i = eXosip_call_build_answer (je->tid, , &answer);
if (i != )
{
printf ("This request msg is invalid!Cann't response!\n");
eXosip_call_send_answer (je->tid, , NULL);
}
else
{
snprintf (tmp, ,
"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, , 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, , &answer);
if (i != )
{
printf ("This request msg is invalid!Cann't response!\n");
eXosip_call_send_answer (je->tid, , NULL); }
else
{
eXosip_call_send_answer (je->tid, , 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, , &answer);
if (i == )
{
eXosip_call_send_answer (je->tid, , answer);
}
eXosip_unlock ();
{
osip_body_t *body;
osip_message_get_body (je->request, , &body);
printf ("the body is %s\n", body->body);
}
}
break;
default:
printf ("Could not parse the msg!\n");
}
}
}
 
 
 
 
 
 

eXosip、osip,以及UAC和UAS的例子的更多相关文章

  1. linux下编译eXosip、osip,以及UAC和UAS的例子

    从网站上看到了这样的一篇博文 :Windows下编译eXosip.osip,以及UAC和UAS的例子 (链接:http://www.cnblogs.com/dyllove98/archive/2013 ...

  2. Windows下编译eXosip、osip,以及UAC和UAS的例子

    今天开始了SIP开源库的学习,我选择了osip和eXosip,但是这两个库的编译使用有些麻烦,源码下来之后编译会出现很多问题,网上也没有找到完整的编译介绍,只能一步一步的找办法解决,最后终于编译成功! ...

  3. exosip/osip 杂项

    exosip 对比osip osip2和eXosip2协议 exosip 和 pjsip 简介 如果要实现嵌入式设备上的SIP电话或者其它,PJSIP是我所见的Coding和Design最为优秀的了, ...

  4. UAC(User Agent Client) 和 UAS(User Agent Server)

    SIP协议采用Client/Server模型.每一个请求(Request)触发服务器的一个操作:每个操作被称为方法(Method):每个请求除了指明具体方法外,还携带了一系列的头域(Header fi ...

  5. eXosip和osip详解

    文档 可以查看exosip osip的在线文档 http://www.antisip.com/doc/ 在线文档 一般先看mainpage 会有库的一个整体说明. 其次看看 modules 会有一些使 ...

  6. 转: OSIP协议栈使用入门

    转自百度文库 很长时间之前,简单粗略地看了下Osip,eXosip,ortp等并快速“封装”了一个Windows下的基于VC6的MFC的SIP软电话(全部源代码VC6工程文件及Lib库可在本Blog共 ...

  7. 基于GBT28181:SIP协议组件开发-----------第五篇SIP注册流程eXosip2实现(二)

    原创文章,引用请保证原文完整性,尊重作者劳动,原文地址http://www.cnblogs.com/qq1269122125/p/3966794.html. 上章节讲解了讲解一个用eXosip2库实现 ...

  8. 国标28181sip开源库介绍(陆续补充完备)

    (1)osip一个基于 osip 库的 UAC 和 UAS 的代码整理http://blog.csdn.net/aflyeaglenku/article/details/51601270(2)pjsi ...

  9. 【转】SIP 中的Dialog,call,session 和 transaction

    如果你对Sip协议中Call, Dialog, Transaction和Message之间的关系感觉到迷惑,那么,那么我可以告诉你,你并不孤单,因为大多数初学者对于这些名词之间的关系都会感到疑惑.   ...

随机推荐

  1. 【案例】保健品行业如何优化供应链管理?APS系统来帮忙

    仙乐健康一直致力于为了客户提供世界级的产品及服务,随着业务量的不断扩大,公司先后实施了ERP系统,CRM系统,WMS系统,OA系统,朝着行业信息化水平领先的目标迈进. 但近年仅仅拥有传统ERP系统和手 ...

  2. Scrapy爬虫提高效率

    如何提高scrapy的爬取效率 增加并发: 默认scrapy开启的并发线程为32个,可以适当进行增加.在settings配置文件中修改CONCURRENT_REQUESTS = 100值为100,并发 ...

  3. phpstorm goland webstorm jetbrain

    1.去官网搞点安装包2.下载完之后一定要打开一下否则个别机型会出现包已损坏的错误提示3.下载Jet Brains License链接:https://pan.baidu.com/s/10nRk7Gei ...

  4. PAT 乙级 1007.素数对猜想 C++/Java

    1007 素数对猜想 (20 分) 题目来源 让我们定义d​n​​为:d​n​​=p​n+1​​−p​n​​,其中p​i​​是第i个素数.显然有d​1​​=1,且对于n>1有d​n​​是偶数.“ ...

  5. Dump文件的生成和使用

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/lk142500/article/detai ...

  6. POJ 3322 Bloxorz

    #include<cstring> #include<algorithm> #include<iostream> #include<cstdio> #i ...

  7. mysql 事件计划

    一.开启mysql事件计划 首先在sql中查询计划事件的状态:SHOW VARIABLES LIKE 'event_scheduler'如果返回的是off表示当前是关闭状态,如果是on当前已经开启了计 ...

  8. 云打印 对Echo的Beta产品测试报告

    云打印 对Echo的Beta产品测试报告 课程名称:软件工程1916|W(福州大学) 团队名称: 云打印 作业要求: 项目Beta冲刺(团队) 作业目标:作业集合 团队队员 队员学号 队员姓名 个人博 ...

  9. 追光的人beta冲刺总结

    所属课程 软件工程1916 作业要求 Beta冲刺博客汇总 团队名称 追光的人 作业目标 beta阶段总结 队员学号 队员博客 221600219 小墨 https://www.cnblogs.com ...

  10. Gym-101242B:Branch Assignment(最短路,四边形不等式优化DP)

    题意:要完成一个由s个子项目组成的项目,给b(b>=s)个部门分配,从而把b个部门分成s个组.分组完成后,每一组的任 意两个点之间都要传递信息.假设在(i,j)两个点间传送信息,要先把信息加密, ...