ice开发流程.docx_第1页
ice开发流程.docx_第2页
ice开发流程.docx_第3页
ice开发流程.docx_第4页
ice开发流程.docx_第5页
已阅读5页,还剩22页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

ice开发流程这一篇,我们先讲一些跟ice有关的接口函数的设计我们仿照icedemo写两个结构体struct zktrans_nat_cfg/* STUN srv */const char *stun_srv;int stun_port;/* TURN srv */const char *turn_srv;int turn_port;const char *turn_username;const char *turn_passwd;/* callback */PFN_zktrans_nat_cb_rtp cb_rtp;PFN_zktrans_nat_cb_rtcp cb_rtcp;PFN_zktrans_nat_cb_state cb_state;void *userptr;struct zktrans_nat_tzktrans_nat_cfg cfg; / 用户配置信息pj_caching_pool cp;pj_pool_t *pool;pj_ice_strans_cfg ice_cfg;pj_ice_strans *ice_strans;/* 保存本地, 对方 sdp 字符串 */char *local_sdp, *remote_sdp;pj_thread_t *thread;pj_bool_t thread_quit_flag;char *stun_srv, *turn_srv, *turn_user, *turn_passwd;PFN_zktrans_nat_cb_rtp rtp_cb;PFN_zktrans_nat_cb_rtp rtcp_cb;void *userptr;pj_thread_t *th_rtp, *th_rtcp;pj_thread_desc th_rtp_desc, th_rtcp_desc;pj_sockaddr def_rtp, def_rtcp;1 结构体配置及库初始化 这一步主要围绕zktrans_nat_t 结构体中的ice_cfg这个变量展开。int zktrans_nat_send_rtp(zktrans_nat_t *trans,const char *data,int len) static bool _inited =false; if(!_inited)/ lib initialize pj_init(); pjlib_util_init(); pjnath_init(); _inited = true; zktrans_nat_t *ins =new zktrans_nat_t; ins-cfg =*cfg; ins-ice_strans=0; pj_cacing_pool_init(&init-cp,0,0); /default ice stream trans cfgpj_ice_strans_cfg_default(&ins-ice_cfg); ins-ice_cfg.stun.cfg.pf=&ins-cp.factory; /ins-pool=pj_pool_create(&ins-cp.factory,zktrans_nathlp,512,512,0); /time heap pj_timer_heap_create(ins-pool,100,&ins-ice_cfg.stun.cfg.timer_heap);/创建定时器堆,最多可放入100个定时器 /ioqueue pj_ioqueue_create(ins-pool,16,&ins-ice_cfg.stun_cfg.ioqueue); ins-quit=false;/ start worker thread for timer & ioqueuepj_thread_create(ins-pool,zktrans_nathlp poll thread,_poll_worker_thread,ins,0,0,&ins-worker_thread); ins-local_stun_server=0,ins-local_turn_server=0,ins-local_turn_user=0,ins-local_turn_passwd=0; if(ins-cfg.stun_srv) ins-local_stun_server=strdup(ins-cfg.stun_srv); ins-ice_cfg.stun.server.ptr=ins-local_stun_server; ins-ice_cfg.stun.server.slen=strlen(ins-local_stun_server); ins-ice_cfg.stun.port=ins-cfg.stun_port; if(ins-cfg.turn_srv) ins-local_turn_server=strdup(ins-cfg.turn_srv); ins-local_turn_user=strdup(ins-cfg.turn_username); ins-local_turn_passwd=strdup(ins-cfg.turn_passwd); ins-ice_cfg.turn.server.ptr=ins-local_turn_server; ins-ice_cfg.turn.server.slen=strlen(ins-local_turn.server); ins-ice_cfg.turn.port=ins-cfg.turn_port; ins-ice_cfg.turn.auth_cred.type= PJ_STUN_AUTH_CRED_STATIC; ins-ice_cfg.turn.auth_cred.data.static_cred.username.ptr = ins-local_turn_user; ins-ice_cfg.turn.auth_cred.data.static_cred.username.slen = strlen(ins-local_turn_user);ins-ice_cfg.turn.auth_cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;ins-ice_cfg.turn.auth_cred.data.static_cred.data.ptr = ins-local_turn_passwd;ins-ice_cfg.turn.auth_cred.data.static_cred.data.slen = strlen(ins-local_turn_passwd); ins-local_sdp=0; ins-remote_sdp=0; return ins; 2 通过pj_ice_strans_create收集候选者信息void zktrans_nat_start(zktrans_nat_t *ins,int caller) assert(ins-ice_strans=0); pj_ice_strans_cb *cb=new pj_ice_strans_cb; cb-on_rx_data=cb_on_rx_data; cb-on_ice_complete=cb_on_ice_complete; ins-caller=caller;/通过pj_ice_strans_create收集候选者信息 pj_ice_strans_create(zktrans_nat,&ins-ice_cfg,2,ins,cb,&ins-ice_strans);/这个函数用ins-ice_strans记录创建的成果,然后将ins作为数据信息传入,我们要在后面的cb_on_ice_complete中获取这个数据信息这个函数执行完,我们本方的候选ip端口对已经收集好了,我们看看cb_on_rx_data和cb_on_ice_complete这两个函数。其中cb_on_rx_data用于接收跟ice的消息数据无关的应用程序数据。例如RTP/RTCP。而cb_on_ice_complete用来报告ICE各种操作的结果。也就是说我们收集完候选者信息之后,也可以在这里得到报告。下面我们看看实现吧。(1)先看cb_on_ice_completestatic void cb_on_ice_complete (pj_ice_strans *strans,pj_ice_strans_op op,pj_status_t status) / 我们通过op参数获取ICE状态完成信息 const char *opname=op = PJ_ICE_STRANS_OP_INIT ? initialization:op = PJ_ICE_STRANS_OP_NEGOTIATION? negotiation:unknown_op;/下面我们获取数据信息zktrans_nat_t *ctx=(zktrans_nat_t *)pj_ice_strans_get_user_data(strans);if(op=PJ_ICE_STRANS_OP_INIT &status=PJSUCCESS)/说明收集候选者任务已经完成,启动icepj_ice_strans_init_ice(ctx-ice_strans,ctx-caller?PJ_ICE_SESS_ROLE_CONTROLLING:PJ_ICE_SESS_CONTROLLED,0,0);/开始建立本地的sdp信息_bulid_local_sdp(ctx);if(status = PJ_SUCCESS)const char *info=OK;fprintf(stdout,%s ICE %s successful n,_FUNCTION_,opname);if(op= PJ_ICE_STRANS_OP_INIT)ctx-cfg.cb_state(ctx,1,0,info,ctx-cfg.userptr);else if(op = PJ_ICE_STRANS_OP_NEGOTIATION)ctx-cfg.cb_state(ctx,2,0,info,ctx-cfg.usertpr);else if(op= PJ_ICE_STRANS_OP_KEEP_ALIVE)ctx-cfg.cb_state(ctx,3,0,info,ctx-cfg.userptr);elsectx-cfg.cb_state(ctx,100+op,0,info,ctx-cfg.userptr);elsechar errmsgPJ_ERR_MSG_SIZE;pj_strerror(status, errmsg, sizeof(errmsg);fprintf(stderr, %s ICE %s failed: %sn, _FUNCTION_, opname, errmsg);if (op = PJ_ICE_STRANS_OP_INIT)ctx-cfg.cb_state(ctx, 1, status, errmsg, ctx-cfg.userptr);else if (op = PJ_ICE_STRANS_OP_NEGOTIATION)ctx-cfg.cb_state(ctx, 2, status, errmsg, ctx-cfg.userptr);else if (op = PJ_ICE_STRANS_OP_KEEP_ALIVE)ctx-cfg.cb_state(ctx, 3, status, errmsg, ctx-cfg.userptr);else ctx-cfg.cb_state(ctx, 100+op, status, errmsg, ctx-cfg.userptr);(2)再看看cb_on_rx_datastatic void cb_on_rx_data (pj_ice_strans *strans,unsigned comp_id,void *pkt, pj_size_t size,const pj_sockaddr_t *from,unsigned from_len)/ 收到的 rtp, rtcp 数据zktrans_nat_t *trans = (zktrans_nat_t *)pj_ice_strans_get_user_data(strans);if (comp_id = 1 & trans-cfg.cb_rtp)trans-cfg.cb_rtp(trans, pkt, size, trans-cfg.userptr);else if (comp_id = 2 & trans-cfg.cb_rtcp)trans-cfg.cb_rtcp(trans, pkt, size, trans-cfg.userptr);我们再看看如何建立本地的sdp,其实就把自己的ice的用户名,密码等组成sdp的格式放到结构体的一个数组中存好。static void _build_local_sdp (zktrans_nat_t *trans)size_t pos = 0;pj_ice_sess_cand candsPJ_ICE_ST_MAX_CAND;char ipaddr128;trans-local_sdp = (char*)malloc(4096);/ 足够了 :)/ 实际这里的 v o s t 无用, 仅仅为了符合 sdp 要求strcpy(trans-local_sdp, v=0rno=- 3414953978 3414953978 IN IP4 localhostrns=icernt=0 0rn);pos = strlen(trans-local_sdp);/ ice-ufrag & ice-pwdpj_str_t ufrag, pwd;pj_ice_strans_get_ufrag_pwd(trans-ice_strans, &ufrag, &pwd, 0, 0);sprintf(trans-local_sdp+pos, a=ice-ufrag:%.*srna=ice-pwd:%.*srn,(int)ufrag.slen, ufrag.ptr,(int)pwd.slen, pwd.ptr);pos = strlen(trans-local_sdp);/ default candidate for rtppj_status_t rc = pj_ice_strans_get_def_cand(trans-ice_strans, 1, &cands0);if (rc != PJ_SUCCESS) fprintf(stderr, %s pj_ice_strans_get_def_cand for RTP errn, _FUNCTION_);exit(-1);sprintf(trans-local_sdp+pos, m=%s %d RTP/AVP %drnc=IN IP4 %srn,audio, (int)pj_sockaddr_get_port(&cands0.addr),91,pj_sockaddr_print(&cands0.addr, ipaddr, sizeof(ipaddr), 0);pos = strlen(trans-local_sdp);/ default candidate for rtcprc = pj_ice_strans_get_def_cand(trans-ice_strans, 2, &cands0);if (rc != PJ_SUCCESS) fprintf(stderr, %s pj_ice_strans_get_def_cand for RTCP errn, _FUNCTION_);exit(-1);sprintf(trans-local_sdp+pos, a=rtcp:%d IN IP4 %srn,(int)pj_sockaddr_get_port(&cands0.addr),pj_sockaddr_print(&cands0.addr, ipaddr, sizeof(ipaddr), 0);pos = strlen(trans-local_sdp);/ all candidate attrs of RTPunsigned cnt = PJ_ICE_ST_MAX_CAND;rc = pj_ice_strans_enum_cands(trans-ice_strans, 1, &cnt, cands);pj_assert(rc = PJ_SUCCESS);for (int i = 0; i local_sdp+pos, a=candidate:%.*s %u UDP %u %s %u typ %srn,(int)candsi.foundation.slen, candsi.foundation.ptr,1, candsi.prio,pj_sockaddr_print(&candsi.addr, ipaddr, sizeof(ipaddr), 0),(unsigned)pj_sockaddr_get_port(&candsi.addr),pj_ice_get_cand_type_name(candsi.type);pos = strlen(trans-local_sdp);/ all candidate attrs of RTCPcnt = PJ_ICE_ST_MAX_CAND;rc = pj_ice_strans_enum_cands(trans-ice_strans, 2, &cnt, cands);pj_assert(rc = PJ_SUCCESS);for (int i = 0; i local_sdp+pos, a=candidate:%.*s %u UDP %u %s %u typ %srn,(int)candsi.foundation.slen, candsi.foundation.ptr,2, candsi.prio,pj_sockaddr_print(&candsi.addr, ipaddr, sizeof(ipaddr), 0),(unsigned)pj_sockaddr_get_port(&candsi.addr),pj_ice_get_cand_type_name(candsi.type);pos = strlen(trans-local_sdp);trans-local_sdppos = 0;3 协商出最佳路径,这里要好好说说了。假设我们是clientA,跟clientB要通信。clientA收集好自己候选者IP端口队后,要和clientB的候选者进行配对。找出最好 的一条路径。这就是所谓的协商。那么我们需要知道clientB的候选者们,还有clientB的用户名密码,这些信息都得通过sdp消息格式获取void zktrans_nat_negotiate (zktrans_nat_t *ins, const char *sdp)/这里的sdp就是clientB的sdp信息xfree(ins-remote_sdp);ins-remote_sdp=strdup(sdp);/存好char ufrag128, pwd128;char default_rtp80, default_rtcp80;int default_rtp_port, default_rtcp_port;pj_ice_sess_cand candsPJ_ICE_ST_MAX_CAND;int cnt = _parse_sdp(ins, sdp, ufrag, pwd, default_rtp, &default_rtp_port,default_rtcp, &default_rtcp_port, cands);pj_str_t user = pj_str(ufrag);pj_str_t passwd = pj_str(pwd);到这里我们就获取了clientB的候选者,用户,密码了,另外我们还获取了rtp和rtcp的端口,地址,我们存好,以后好用来向clientB发送rtp,rtcp数据/ save def_rtp, def_rtcppj_sockaddr_init(pj_AF_INET(), &ins-def_rtp, 0, 0);pj_sockaddr_init(pj_AF_INET(), &ins-def_rtcp, 0, 0);pj_str_t ip = pj_str(default_rtp);pj_sockaddr_set_str_addr(pj_AF_INET(), &ins-def_rtp, &ip);ip = pj_str(default_rtcp);pj_sockaddr_set_str_addr(pj_AF_INET(), &ins-def_rtcp, &ip);pj_sockaddr_set_port(&ins-def_rtp, default_rtp_port);pj_sockaddr_set_port(&ins-def_rtcp, default_rtcp_port);pj_ice_sess_options opts;pj_ice_strans_get_options(ins-ice_strans, &opts);opts.controlled_agent_want_nom_timeout = -1;pj_ice_strans_set_options(ins-ice_strans, &opts);4 协商完成后,我们可以发送rtp,rtcp数据了int zktrans_nat_send_rtp (zktrans_nat_t *trans, const char *data, int len)if (!pj_thread_is_registered() pj_thread_register(zknath_rtp_sender_thread, trans-th_rtp_desc, &trans-th_rtp);/ sendto()pj_status_t rc = pj_ice_strans_sendto(trans-ice_strans, 1,data, len, &trans-def_rtp,pj_sockaddr_get_len(&trans-def_rtp);if (rc = PJ_SUCCESS)return len;return -1;发布于 1年前, 阅读(1218) | 评论(1) | 投票(0) | 收藏(0) 阅读全文. ICE协议看明白分类: pjsip 302011-12 这个文件描述了以UDP为基础的请求/回答模式的多媒体会话的NAT穿越的协议。这个协议名叫ICE。ICE利用了STUN协议和它的扩展版本TURN。ICE可以被任何请求/问答模式的协议利用。例如sip。1简介RFC 3264 定义了一个双向会话描述协议(SDP),以建立多媒体回话。这种请求/回答模式被sip利用。 请求/回答的协议很难穿越NAT。因为它们想要建立一个媒体包的流。它们想要携带媒体源的IP地址和端口,并且将这些东西放入消息中。这些东西很难通过NAT。这个协议也想直接在两个伙伴之间创建会话。这样的话,就可以减少媒体数据的延迟,减少报的丢失,并且减少应用程序的消耗。然而很难克服nat穿越的问题。各种各样的情况很难解决,但这正是本协议的目的。各种各样的方法已经被一些协议定义了用于nat的穿越。例如turn,stun。Sdp。但是每种协议都有优点和缺点。我们缺少的是一种试用于多种环境的方法来解决NAT穿越。 这个说明书定义了ICE作为NAT穿越的方法,他可以被扩展成其他的传输协议。ICE是一种请求问答模式的扩展版本,2预览ICE在经典的ICE中,我们有两个端点想要交流。它们可以通过一些传输协议直接交流。客户端可能位于也可能没有位于NAT的后面,ICE让客户端来发现路径使他们之间交流。图1 显示了一个经典的环境。两个点是L和R。它们俩都在NAT的后面。NAT的类型并不知道。客户L和R想进行一个请求/问答模式的交流来交换SDP信息。交流将通过SIP 服务器进行。ICE主要关心的是网络中的STUN或者TURN服务器。每个客户端都有自己的STUN和TURN服务器,或者它们的一样。 ICE的基本观念如下:每个客户端有几个个传输地址端口对的组合。它们可以通过这些组合同其他客户端交流。 这些组合包括以下: 。在子网的传输地址 。在NAT上的传输地址 。在TURN Server上的转发地址 图中L的任何一个组合都可以被用来同R的组合中的任何一个组合交流。然而,有些组合可能用不到。例如,如果L和R都在NAT之后,它们的子网传输地址不能直接进行交流。ICE的目的是发现哪个地址对可以交流。ICE的目的就是尝试所有可能的对,来找到一个或多个可以用来通信。2。.1 获取候选者地址为能用好客户端必须找到所有的地址候选者。候选者是一个ip端口对。这篇文章定了三种类型的候选者。在子网的传输地址叫做HOST CANDIDATE。若一个客户端是多路的。它包含来自每一个IP地址的候选者。对于peer来说,client可被peer到达通过各种各样的方式。若peer跟client一个子网,则用子网的候选者。若在外网则用外网候选者。为了找出哪个候选者最近,client包含了所有的候选者。 若使用TURN协议则候选者有Relayed Address,Server Reflexive Address,Local Address。当client从 IP 地址和端口 X:x发送了一个TURN Allocate request,nat将会映射一个Server Reflexive Address,数据包将由host 地址候选者到Reflexive地址候选者。当这里有多个NAT位于client 和server之间时,turn request将在任何一个nat上绑定,但是只有最靠近server的reflexisive候选者被client发现。然后allocate request到大TURN server。Turn server分配了端口y,地址y,然后产生了一个allocate回应。然后通知客户端这是relayed candidate。Turn服务器也通过回应通知client 它的server reflexive 候选者X1:x1。当仅仅STUN服务器被利用的时候,客户端发送一个STUN绑定请求道STUN server。STUN server将通知客户端它的 server reflexive 候选者 X1:x1 。2。.2 连接检查当L收集了所有的候选者信息,它将他们由高到低的优先级顺序组织起来,并将他们放到SDP请求的属性中发到R。当R接收到请求后,它进行相同的收集过程并将自己的候选者都进行了回复。最后,每一个client都有了他们对方的完整的候选者集合。它将他们组织好,放到CANDIDATE PAIRS中,来看哪个pairs有效,每个代理进行了一些检查。每个检查都是一个STUN request/response传输。连接检查的步骤很简单:1 将候选者进行优先级组织。2 发送检查每个优先级对3 回复从其他代理中收到的每个检查。检查是一个四次握手的过程。2.3 整理候选者依照上述的搜索规则找到了所有的候选者对,为了更快的得到记过,候选者被用一个特定的规则进行了整理。整理候选者的结果列表被叫做CHECK LIST.zhge规则在4.1.2中被描述。它遵循两个基本规则。 。每个代理给候选者一个数字优先级。伴随着候选者发送到peer中。 。本地和远处的候选者的优先级被结合到一起。 当L和R的前面都有NAT时,第二条特性对于让ICE去工作很重要。大多数情况下,NAT将不允许包从主机发来。因此,ICE检查每个方向,直到两边都有了回应才能确定联通。2。.4 冻结候选者先前介绍的情况仅仅符合代理想要用一个组件来建立媒体会话的情况。一个多媒体六可能要求各种各样的组件。每个组建必须为这个多媒体流工作。例如RTP组件和RTCP组建。Agent通常需要建立一个或多个流的连通性。每个流的网络特性可能都非常相似。通常需要为另一个组建改变候选者。每个候选者都与一个叫FOUNDATION的属性相关。当两个候选者相似时,他们由相同的foundation。相同的类型,从相同的主机候选者获得,并且STUN赢相同的协议。否则他们的foundation是不一样的。若一个候选者有2.6 总结ICEICE 检查在特定的环境下执行,高优先级的候选者被率先检查,然后是低优先级。得出结论的一个方法是只要一个媒体流的每个组件都成功完成通信,我们就宣告成功。 实际上,这是一个合理的方法。详细的介绍在下面进行提供。然而,如果这个高优先级的连接在建立过程中,若丢了一个包,则会失败,那我们会朝低优先级找。我 们不如多花一些时间来继续检查,可能产生更好的后果。然而,更多的是,通过这种规则得出的优先级可能不会得到最好的效果。所以,ICE指定了agents中得一个作为CONTROLLING AGENT,其他的是CONTROLLED AGENT。controlling agent从所有有效的候选者中找出适用于媒体的一个进行任命。它可以用一下两种方法。用REGULAR NOMINATION和AGGRESSIVE NOMINATION。在regular nomination中,the controlling agent让检查继续,知道至少一个有效的候选者被找到。然后在这些有效的候选者中挑选,向它任命的候选者对中发送第二次STUN请求发布于 1年前, 阅读(2048) | 评论(0) | 投票(0) | 收藏(1) 阅读全文. TURN协议深入剖析分类: pjsip 292011-12 概括一下若一个主机位于NAT后面,那么在特定的环境 下,它不可能跟其他主机通信。这种情况下,这台主机有必要通过一个转发的主机来实现通信。有种协议叫TURN,允许主机通过转发来和其他主机通信。 TURN协议与其他转发协议不同的地方在于它允许客户端用一个转发地址同多个主机交流。1 简介一个位于NAT后面的主机可能相同其他位于NAT后面的主机交流。为了达到目的,可以用打洞的方法来找到一条路径,但是不通过转发。当两台主机都位于对称NAT之后时,打洞技术可能会失败。TURN协议允许一个位于NAT之后的主机请求另一台主机转发。客户端可以通过服务端转发包到另一端。并且可以控制转发如何结束。这个客户端通过在服务端获取一个IP端口对,也被称作转发端口地址来进行上述操作。当对方发送一个包到转发端口地址时,服务器就将这个包转发到客户端。当一个客户端发送一个数据包到服务器时,服务器转发这个包到转发端口地址所对应的客户端。一个采用TURN协议的客户端一定有一些方法把自己的转发地址告诉对方,并且知道每个通信伙伴的IP端口地址对。若TURN协议被用作ICE协议中,那么它的 转发IP端口对和其他伙伴的IP端口对都包含在ICE候选者信息中。例如,如果TURN和ICE被SIP用于媒体传输时,SIP以会话协议的方式,将 ICE候选者的信息放进SIP消息体中。若TURN和ICE协议被用在其他会话协议中,那么【MUSIC-ICE-NONSIP】将提供一些会话协议必须 实现的功能。虽然TURN服务器的应用能使两个位于NAT 之后的主机交流成为可能,但是这对TURN服务器来说开销太大。因而,最后是在直接交流没法执行的时候,才用TURN服务器。当客户端与伙伴通过ICE协 议来确定通信路径的时候,ICE协议将首先通过打洞的方法来寻找一条直接的路径来让两方通信,仅仅当这条路径找不到的时候才会用TURN服务器。TURN原来被设计用来支持基于SIP的多媒体会话信号传输。TURN被设计成ICE中的用于NAT穿越的一部分。TURN是一个STUN的扩展版本。大多数情况下,TURN消息是STUN格式的消息。该文档的读者应该熟悉STUN协议。2 大体看一下操作这一节给出了TURN操作的一个预览。在一个传统的配置中,TURN客户端被连接到 私有网络中,并且通过这个一个或多个NATS连到共有网络中。在这个没有共有网络中,是TURN服务器。其他的就是这个TURN客户端想连接的同伴客户 端。这些同伴可能处在NAT的后面也可能没有处在NAT的后面。客户端用这个服务器作为一个转发来发送数据包到其他的伙伴,并且通过转发接收来自同伴机的 数据包。图1显示了一个经典的过程。这个图中,TURN客户端和TURN服务端被NAT分割。客户端在私有网络中,服务端在公网中。NAT是地址端口对称。客户端通过一个叫做客户端主机传输地址得到IP地址端口对同server通话。客户端从它的主机传输地址中传输TURN消息到一个TURNServer的一个端口地址中。由于这个客户端位于NAT的后面,这个服务器根据客户端在NAT的传输地址看到了从客户端中传回的包。这个NAT的传输地址叫做SERVER-REFLEXIVEtransport address。这个客户端用TURN命令在服务端进行了一个 ALLOCATION操作。allocation是一个位于server的数据结构体。这个数据结构体包含了分配的RELAYED TRANSPORT ADDRESS。伙伴们可以通过这个RELAYED TRANSPORT ADDRESS让服务器转发消息到客户端。只要这个allocation被创建,客户端就能发送应用程序数据到服务器,并且指明这个数据将送给那个伙伴。这样,服务器将转发数据岛相关的伙伴。客户端发送的应用程序数据包含在TURN消息中。在服务端,应用程序将数据从TURN消息中取 出。并将数据发送以UDP的形式发给伙伴。相反的,伙伴同样可以以UDP的方式发送应用程序数据到服务端分配的转发传输地址。服务端将从TURN消息解析 出数据,并且将这个数据发送到伙伴指定的客户端。由于TURN消息总是包含着客户端将发给的同伴的地址。客户端可以用唯一的一个allocation和多 个peer通信。当peer位于NAT之后,客户端必须通过 peer的server-reflexive transport address 辨别peer而不是host transport address。例如上图,发送数据到PeerA,客户端必须辨别50:32102而不是:49582。每一个allocation属于单一的一个客户端。并且有确切的一个relayed transport address。这样当一个包到达relayed transport address时,服务端知道数据将到哪个客户端。每个allocation属于唯一的一个客户端。但是一个客户端可以有多个allocation。2.1 TransportTURN,就如在这个说明书中定义的那样,总是通过UDP在服务端和peer中通信。然而TCP也允许。2.2 Allocation为了创建一个Allocation在服务端。 客户端发送一个Allocate请求道服务端,服务端将回复一个Allocate Sucess response,这个response包含着一个allocated relayed transport address。客户端可以包含属性到这个Allocate request中,这个属性可以描述allocation的类型(例如 allocation的寿命)。一旦relayed transport address被分配。客户端必须保证allocation 处于活跃状态。为了达到目的,客户端必须周期性的发送更新请求到server。更新的频率跟allocation的寿命相关。默认的allocation 的寿命是10分钟。这个值已经足够的常,以至于更新及时。发布于 1年前, 阅读(1014) | 评论(0) | 投票(0) | 收藏(1) 阅读全文. pjlib深入剖析和使用详解分类: pjsip 222011-12 1. PJSIP简介 PJSIP的实现是为了能在嵌入式设备上高效实现SIP/VOIP.其主要特征包括: 1).极具移植性.(Extremely portable) 2).非常小的足印.(Very small footprint) 官方宣称编译后的库150Kb,我在PC上编译后加上strip后大概173Kb,这对于嵌入 式设备,是个好消息:) 3).高性能.(High performance) 这点我们后面可以看看是否如作者宣称的:) 4).支持众多的特征.(Many features) 这点可以从http:/www.

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论