复旦光华之exosip-publish.doc_第1页
复旦光华之exosip-publish.doc_第2页
复旦光华之exosip-publish.doc_第3页
复旦光华之exosip-publish.doc_第4页
复旦光华之exosip-publish.doc_第5页
已阅读5页,还剩5页未读 继续免费阅读

下载本文档

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

文档简介

eXosip之publish2010-12-07 13:22publish客户端在osip库中的生存期:客户端会是用来发送publish请求消息。在eXpublish_api.c文件中,调用eXosip_build_publish()函数,构造一个osip_message_t*结构的消息体,通过函数eXosip_publish (osip_message_t * message, const char *to)发送出去,这里的的第二个参数to,是用来指明这个publish消息是谁的。与服务端不同的是,在客户端里面有一个管理publish消息的结构(eXosip_pub_t),每一个to,对应一个eXosip_pub_t结构,在这个结构中包含了以下信息 struct eXosip_pub_t int p_id; int p_period; /* delay between registration 也就是在publish消息中的Expires头域值*/ char p_aor256; /* sip identity对应第二个参数const char *to */ char p_sip_etag64; /* sip_etag from 200ok 在客户端收到服务端的200Ok时,用其中的SIP-Etag头域值填充该字段*/ osip_transaction_t *p_last_tr;/*代表当前或者最后一个的事务结构指针*/ int p_retry; eXosip_pub_t *next; eXosip_pub_t *parent;从最后两个字段来看,在osip库中,其实是用一个链表来管理所有用户的publish消息(也就是全局eXosip-j_pub来管理,当然此时是为空的,不一会儿这里将有一个publish消息要发送,此时会分配一个eXosip_pub_t结构给这个用户,因为这里第一个,所以这里eXosip-j_pub就指向了这个用户的eXosip_put_t结构,也就是说这个用户将会是在j_pub链表上的第一个用户,当这里有另一个用户B上来时,会将B的eXosip_pub_t结构加入到eXosip-j_pub链表中去,在这条链表中,查找用户的KEY就是结构中的p_aor字段。所以当B再一次发送时,就会在j_pub链表中找到一个eXosip_put_t的结构,然后只需要更新这个结构的某些字段即可,而不需要再分配亲的结构了)。这里可以先说下不同用户上来和相同用来上来,eXosip_pub_t结构的链表的变化。当用户A发送publish请求消息到服务端,首先会用to去eXosip-j_pub链表中查找有没有对应于用户A的eXosip_pub_t结构,这里是没有的(A第一次上来),所以会分配一个新的结构(这个过程中,会填充p_aor, p_period,p_id值),然后把这个结构添加到eXosip-j_pub链表中去,然后创建一个事务,这里会有个判断,看p_last_tr是否空,如果是不为空,则会将这个事务发到eXosip-j_transactions链表中去,等待释放事务,然后将p_last_tr指向新创建的事务;否则,将p_last_tr指向新创建的事务。这里用户A是第一次,所以会直接指向新的事务。等了一会儿,用户再来一次publish,这时就在eXosip-j_pub链表中能够找到用户A(根据p_aor字段),然后就会用p_sip_etag字段来填充这次的publish消息SIP-If-Match头域,同时将p_period字段值赋予当前这次publish消息中的Expires头域值,然后分配一个新的事务,由于用户A对应的eXosip_pub_t结构中p_last_tr是指向上一次publish消息产生的事务结构,所以在这里,先将上次的事务添加到eXosip-j_transactions链表中,等待释放事务,然后再将p_last_tr指向新分配的事务结构。publish在客户端几乎就是这种流程了。下面将以一个完整的publish流程来举例说明下publish在osip库中的生存期。step 1: 用户A initial publish在应用程序中,调用函数 eXosip_build_publish (osip_message_t * message, const char *to, const char *from, const char *route, const char *event, const char *expires, const char *ctype, const char *body)为用户A构造一个osip_message_t结构的publish请求消息,在这里值得注意下,如果ctype和body要同时为空,或者要同时不为空,否则函数会返回OSIP_BADPARAMETER错误。这个函数返回成功后,表示现在publish请求消息构造成功了。然后调用eXosip_publish (osip_message_t * message, const char *to),将publish消息发送出去。这是对应到应用程序来说,如果该函数返回OSIP_SUCCESS就表示,发送成功,在应用程序中,程序员就不需要在管什么了,就等着接收响应了,但是在osip库里面,调用这个函数后,其实还没有发送,来看看下面的。在eXosip_publish()函数中,首先调用i = _eXosip_pub_find_by_aor (&pub, to)这个函数,这个函数的目的就是根据传入参数to去eXosip-j_pub链表中找出一个对应于to用户的eXosip_pub_t结构。当然,由于这是用户A的initial,所以这里是找不到的。所以在调用完_eXosip_pub_find_by_aor ()函数后,会继续调用i = _eXosip_pub_init (&pub, to, expires-hvalue)函数,去分配一个亲的eXosip_pub_t结构P1,在分配过程中,就会填充p_aor, p_period,p_id值。然后会调用 i = _eXosip_transaction_init (&transaction, NICT, eXosip.j_osip, message)函数分配一个事务(transaction)结构T1。这个过程中,T1已经加入到全局事务管理链表(eXosip.j_osip-osip_nict_transactions)中。然后将P1-p_last_tr指向T1。 调用sipevent = osip_new_outgoing_sipmessage (message)函数产生一个事件(event)结构E1。然后再将事件E1加入到T1下面的事件fifo(队列)中去。最后调用_eXosip_wakeup ()函数来唤醒线程。在线程中。eXconf.c文件。eXosip_execute (void)会从select等待中苏醒过来,调用osip_nict_execute (eXosip.j_osip)去执行nict状态事务。在osip.c文件。在osip_nict_execute()函数中,先从eXosip.j_osip-osip_nict_transactions链表中得到每一个事务,然后从执行每个事务下的事件fifo。在该函数中执行到T1事务,然后先从E1事务中的事件队列中移除事件E1。调用osip_transaction_execute (transaction, se)函数,去执行这个事件。在osip_transactions.c文件。在osip_transaction_execute ()函数中会去判断事务状态和事件类型,如果在fsm中找到了对应的事务状态和事件类型,则会执行相应的函数,否则,就会打印USELESS event!,然后释放事件下的osip_message_t结构,并释放事件,然后这个事件也就执行完了。在这里当然能够找到对应的fsm执行。在nict_fsm.c文件中。判断事务状态(NICT_PRE_TRYING)和事件类型(SND_REQUEST),找到执行nict_snd_request()函数。在nict_snd_request()函数中。将T1-orig_request指向事件E1下的osip_message_t消息(在这里,即为publish请求消息)。然后调用回调函数(在jcallback.c文件中)发送消息到服务端。在这里即为真正的发送。发送不成功,则会调用nict_handle_transport_error()函数,这个函数下面,则是把事务T1的状态设置为NICT_TERMINATED,然后再将T1事务从eXosip.j_osip中的nict事务链表中删除掉(.)。发送成功后,会根据OSIP_NICT_UNKNOWN_REQUEST_SENT类型,再次去jcallback.c文件中调用一个回调函数,在这里,这个函数只是打印一个句而已。然后将timer e启动(时长为500ms),同时事务T1的状态也就变成了NICT_TRYING(等待接收服务端来的响应)。在这里事件E1也就执行完了,线程继续去监听socket事件,而进程(应用程序)也闲下来,当timer e超时时,会产生一个超时事件,在eXconf.c文件中。eXosip_execute()函数中执行 osip_timers_nict_execute (eXosip.j_osip),这里就会产生一个TIMEOUT_E事件。这个事件的作用是用来重发请求消息的,然后会将timer e的时长设置为4s。如果客户一直收到不响应,则会一直重发,直到timer f超时(时长32s)。timer f超时,则事务的状态就会变成NICT_TERMINATED,同时将T1事务从eXosip.j_osip的nict事务链表中删除掉(.)。如果在500 ms内接收到服务端返回的响应,则通信socket有消息来到客户端就会唤醒线程。在eXconf.c文件中。在eXosip_execute (void)函数中调用的i = eXosip_read_message (1, lower_tv.tv_sec, lower_tv.tv_usec)会从select等待中苏醒过来。在udp.c文件中。函数eXosip_read_message ()会调用eXtl_udp.tl_read_message (&osip_fdset),这个将对应到eXtl_udp.c文件中的udp_tl_read_message (fd_set * osip_fdset)函数。在这个函数中首先会判断if (FD_ISSET (udp_socket, osip_fdset)看看这个socket信号是不是服务端和客户端的socket,如果是的话,则接收包。在这函数中将调用_eXosip_handle_incoming_message (buf, i, udp_socket, src6host, recvport)函数来处理incoming消息。在udp.c文件中。函数_eXosip_handle_incoming_message ()首先会产生一个事件E2,然后根据接收到的包,来解释成一个对应的osip_message_t结构M2。这里E2-sip指向M2。然后调用 i = osip_find_transaction_and_add_event (eXosip.j_osip, se)这个函数去全局管理事务的链表中去查找事务,如果找到就加入到该事务下的fifo中。因为这里是initial publish的响应,所以是会找到事务T1的,同时将E2添加到T1中的fifo中去。然后一层层返回,最终将返回到eXosip_read_message ()调用处。往下执行。到osip_nict_execute (eXosip.j_osip)。在osip.c文件。在osip_nict_execute()函数中,先从eXosip.j_osip-osip_nict_transactions链表中得到每一个事务,然后从执行每个事务下的事件fifo。在该函数中执行到T1事务,然后先从E1事务中的事件队列中移除事件E1。调用osip_transaction_execute (transaction, se)函数,去执行这个事件。在nict_fsm.c文件中。判断事务状态(NICT_TRYING)和事件类型(RCV_STATUS_2XX),找到执行nict_rcv_23456xx()函数。在这个函数中,首先会判断T1-last_response(用来存放上一次的响应消息)是否为空,如果不为空,则要先释放T1-last_response,再将T1-last_response指向M2,否则就直接指向M2。当然这里是intial publish,所以不需要释放它。然后根据类型OSIP_NICT_STATUS_3XX_RECEIVED去jcallback.c文件中执行cb_rcv2xx (int type, osip_transaction_t * tr, osip_message_t * sip)函数。在这个函数中会调用 i = _eXosip_pub_update (&pub, tr, sip)函数,目的是用200 OK响应中的SIP-Etag来更新P1中的p_sip_etag字段。同时会用200OK中的Expires头域值来替换p_period,当Expires头域值V大于0时并且p_period-60v时,则应该把p_period值设置为V + 60,否则就不管,最后将p_retry字段设置为0。再最后调用je = eXosip_event_init_for_message (EXOSIP_MESSAGE_ANSWERED, tr)和report_event (je, sip)函数,这个两个函数将产生事件,而这个事件是对应于应用层的。而在应用程序调用了eXosip_event_wait()函数,则能将收到的200 OK响应反应到应用层。然后从回调函数cb_rcv2xx()返回到nict_rcv_23456xx()函数里,这里将timer k启动,同时将事务T1的状态设置为NICT_COMPLETED。这里一个正常的200OK响应事务就说完了。假如这里事件类型为RCV_STATUS_3456XX,也就是说,响应为4XX响应,则在nict_fsm.c文件中,还是调用nict_rcv_23456xx()函数,但是在这个函数里根据类型OSIP_NICT_STATUS_4XX_RECEIVED在jcallback.c文件中将调用回调函数cb_rcv4xx (int type, osip_transaction_t * tr, osip_message_t * sip),在函数里还是会调用 i = _eXosip_pub_update (&pub, tr, sip),但是对不是2XX的响应是不会更新p_sip_etag的,然后产生EXOSIP_MESSAGE_REQUESTFAILURE失败响应事件给应用层,返回到RCV_STATUS_3456XX()函数,这里将timer k启动,同时将事务T1的状态设置为NICT_COMPLETED。2XX响应与4XX响应的区别在于是否更新P1下的p_sip_etag及其上报给应用层的类型。当 timer K 超时时,会产生一个TIMEOUT_K事件,这个事件执行完后,它对应的事务的状态将会设置为NICT_TERMINATED, 同时将T1事务从eXosip.j_osip的nict事务链表中删除掉(.)。这也是正常情况下publish事务的流程(见附录1)。这里执行完后,将会一层层返回到eXconf.c文件的eXosip_execute()函数调用osip_nict_execute()函数的位置。然后会执行eXosip_release_terminated_calls ()和eXosip_release_terminated_publications ()。这两个函数将会决定那个事务将会被释放(见附录2)。step2: refresh publish.应用程序调用eXosip_build_publish ()根据参数的不同,产生一个refresh publish的osip_messasge_t结构M3,然后调用eXosip_publish ()发送refresh publish请求。在这个函数中,由于用户A已经发送了一个initial publish,所以在eXosip.j_pub链表中存在用户A的eXosip_pub_t结构P1,所以在调用i = _eXosip_pub_find_by_aor (&pub, to)去查找时,就能找到。这时会产生用p1-p_sip_etag来填写M3中的SIP-IF-Match头域,同时更新p1中的p_period字段。然后产生一个事务T2,同时将T1事务添加到eXosip.j_transactions链表中,并将P1-p_last_tr指向T2。最后再产生一个事件E3。然后去执行E3事件,这个就和step 1中执行事件E1是一样的了。以后,比如接收响应和step1中对应步骤就一样了。step3: terminated publish:应用程序调用eXosip_build_publish ()根据参数的不同,产生一个terminated publish的osip_messasge_t结构M4,然后调用eXosip_publish ()发送terminated publish请求。在这个函数中,由于用户A已经发送了一个initial publish和refresh publish,所以在eXosip.j_pub链表中存在用户A的eXosip_pub_t结构P1,所以在调用i = _eXosip_pub_find_by_aor (&pub, to)去查找,就能找到。这时会产生用p1-p_sip_etag来填写M4中的SIP-IF-Match头域,同时更新p1中的p_period字段,在这里,由请求消息是terminated publish,也就是说Expires头域值为0, 所以在这里P1-p_period字段的值将会变成0。然后产生一个事务T3,同时将T2事务添加到eXosip.j_transactions链表中,并将P1-p_last_tr指向T3。最后再产生一个事件E4。然后去执行E4事件,然后在eXosip_execute()函数中执行这个eXosip_release_terminated_publications ()函数,此时p_period是为0的,但是没有收到响应,所以不会执行_eXosip_pub_free (jpub)。收到响应时和step 1中执行事件E1是一样的了。然后再次执行eXosip_release_terminated_publications ()函数时,就满足条件了,将执行_eXosip_pub_free (jpub)。将T4添加到eXosip.j_transactions链且中。整个publish流程就这样了。附录1send publish:产生事务,timer f启动且状态为NICT_PRE_TRYING,发送完后状态变为NICT_TRYING, timer e启动。timer e超时,重发请求。并且timer e不停止。recv response:查找事务,成功找到事务,状态为NICT_TRYING,处理成功状态变为NICT_COMPLETED,timer K启动。timer k超时,停止timer K,并将事务从全局事务管理链表eXosip.j_osip的nict事务链表中删除掉,同时设置事务状态为NICT_TERMINATED。当用户下次再发送publish请求消息时,会将这个事务添加eXosip.j_transacions链表中,等待释放。这就是一个正常的publish事务的生存期。附录2void eXosip_release_terminated_calls (void)while (!osip_list_eol (&eXosip.j_transactions, pos)osip_transaction_t *tr = (osip_transaction_t *) osip_list_get (&eXosip.j_transactions, pos);if (tr-state = IST_TERMINATED | tr-state = ICT_TERMINATED | tr-state = NICT_TERMINATED | tr-state = NIST_TERMINATED) /* free (transaction is already removed from the oSIP stack) */OSIP_TRACE (osip_trace (_FILE_, _LINE_, OSIP_INFO3, NULL, Release a terminated transactionn);osip_list_remove (&eXosip.j_transactions, pos);_eXosip_delete_jinfo (tr);osip_transaction_free (tr); else if (tr-birth_time + 180 next; if (jpub-p_period = 0 & jpub-p_last_tr != NULL) if (now - jpub-p_last_tr-birth_time 60) OSIP_TRACE (osip_trace (_FILE_, _LINE_, OSIP_INFO1, NULL, Release a terminated publicationn); REMOVE_ELEMENT (eXosip.j_pub, jpub); _eXosip_pub_free (jpub); else if (jpub-p_last_tr-last_response != NULL & jpub-p_last_tr-last_response-status_code = 200 & jpub-p_last_tr-last_response-status_code osip-osip_nist_transactions)中寻找,这个事件是否存在事务(transaction)中。没有找到,则创建会创建一个事务(transaction),找到了就返回找到的事务。当然,这里是第一个publish请求,所以肯定是找不到的,则会创建一个事务,并将这个事务添加事务链表中,即由全局变量eXosip 下的j_osip管理。假如服务端没有规则的时间内给客户端回应响应,则客户端会重发这个publish消息,此时服务端接收到这第二个publish请求消息,当然,这里也会先创建一个事件,然后去事务链表中查找,这里是能找到事务的(因为这两个publish消息是一模一样的),所以这里不会再创建事务了。A.publish第一次到达服务端:这个事件在事务链表中找不到事务(i = osip_find_transaction_and_add_event (eXosip.j_osip, se); udp.c),然后会创建一个事务(eXosip_process_newrequest (se, socket); udp.c)并且同时把这个事务加入到eXosip-j_transactions链表(这条链表的作用在osip库中是用来释放事务的)中,最后唤醒线程。在eXconf.c中,在eXosip_execute函数中接收到唤醒信号然后调用osip_nist_execute(eXosip-j_osip)函数。然后去执行这个事务下的事件,即这个publish请求。在nist_fsm.c中,判断事务状态(NIST_PRE_TRYING)和事件类型(RCV_REQUEST),决定调用nist_rcv_request()函数;在这个函数,将publish请求消息保存在事务结构的orig_request字段(osip_message_t)中,然后这里会调用一个回调用函数(跳到jcallback.c中执行这个回调函数cb_rcvrequest()。在执行cb_rcvrequest()这个函数对publish消息来说只是将这个publish消息生成一个eXosip的事件(eXosip_event_t),将其通知应用程序。执行完这个回调函数后,程序将返回到nist_fsm.c的nist_rcv_request()函数中,此时将把这个事务的状态设置成NIST_TRYING(意思是服务端可以发送响应给客户端了).由于我们知道,在客户端,发送完publish会设置一个timer e,这个定时器是时间长度是500ms。什么意思呢,就是说客户端在500ms内没有收到服务端给的响应,则会再次重发上一次的请求消息。下面我们分两种情况来说下:A1:客户端在500ms内收到响应。 服务端调用eXmessge_api.c文件中的eXosip_message_build_answer()函数构造一个osip_message_t结构的响应消息。在构造响应消息这前,先会调用 eXosip_transaction_find (tid, &tr)这个函数来查找事务。进入这个函数中一看,用tid(即事务id)去eXosip-j_transactions链表中查找事务,如果没有找到事务,则返回错误,也就是构响应消息失败;如果找到了一个事务,则调用_eXosip_build_response_default()构造成响应消息。然后调用eXosip_message_send_answer()函数来发送响应消息。但是这在发送函数中还会调用 eXosip_transaction_find (tid, &tr)函数,如果找到了,会去检查事务的状态,如果事务状态是NIST_COMPLETED或者NIST_TERMINATED,则表示这个事务已经回答了,所以不用再回答。如果事务状态不是它们,则会生成一个事件,并将这个事件添加到事务的队列中,然后将唤醒线程。在在eXconf.c中,在eXosip_execute函数中接收到唤醒信号然后调用osip_nist_execute(eXosip-j_osip)函数。然后去执行这个事务下的事件,即这个publish响应。在nist_fsm.c中,判断事务状态(NIST_TRYING)和事件类型(SND_STATUS_2XX(成功响应)或者(SND_STATUS_3456XX错误响应)去调用nist_snd_23456xx()函数。在这个函数,将publish响应消息保存在事务结构的last_response字段(osip_message_t)中,然后这里会调用两个回调用函数(跳到jcallback.c中执行),第一个是cb_snd_message()即通过socket发送响应消息给客户的函数;第二个是cb_snd123456xx()。最后将启动timer J,时间长度为32s。同时将事务状态设置成NIST_COMPLETED。当timer J超时时,在eXconf.c中,在eXosip_execute函数中osip_timers_nist_execute (eXosip.j_osip)会产生一个timer J超时事件,然后调用 osip_nist_execute (eXosip.j_osip)去执行这个超时事件。在nist_fsm.c中,判断事务状态(NIST_COMPLETED)和事件类型(TIMEOUT_J),则会调用osip_nist_timeout_j_event()函数,首先会停掉timer J定时器,然后调用 事务状态设置成NIST_TERMINATED(表示这个事务结束了,可以释放了)。然后会调用一个杀死事务的回调用函数,在jcallback.c中调用cb_xixt_kill_transaction()函数,这个函数里会调用i = osip_remove_transaction (eXosip.j_osip, tr),这个函数的作用就是将该事务从全局管理的事务链表(eXosip-osip-osip_nist_transactions)中移除掉。从这里返回到eXosip_execute()函数后,执行eXosip_release_terminated_calls ()函数,这个函数的作用是释放状态为terminated的事务。这个函数里有一段代码是:while (!osip_list_eol (&eXosip.j_transactions, pos)osip_transaction_t *tr = (osip_transaction_t *) osip_list_get (&eXosip.j_transactions, pos);if (tr-state = IST_TERMINATED | tr-state = ICT_TERMINATED | tr-state = NICT_TERMINATED | tr-state = NIST_TERMINATED) /* free (transaction is already removed

温馨提示

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

评论

0/150

提交评论