织梦CMS - 轻松建站从此开始!

罗索

eXosip之publish

落鹤生 发布于 2012-02-14 11:19 点击:次 
客户端会是用来发送publish请求消息。在eXpublish_api.c文件中,调用eXosip_build_publish()函 数,构造一个osip_message_t*结构的消息体,通过函数eXosip_publish (osip_message_t * message, const char *to)发送出去,这里的的第二个参数to,是用来指明这个publish消
TAG:

publish客户端在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_aor[256];            /* sip identity对应第二个参数const char *to */
    char p_sip_etag[64];        /* 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-60>v时,则应该把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流程就这样了。
 
附录1
send 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事务的生存期。
 
 
附录2
 
void 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 transaction\n"));
osip_list_remove (&eXosip.j_transactions, pos);
__eXosip_delete_jinfo (tr);
osip_transaction_free (tr);
} else if (tr->birth_time + 180 < now)    /* Wait a max of 2 minutes */
{
osip_list_remove (&eXosip.j_transactions, pos);
__eXosip_delete_jinfo (tr);
osip_transaction_free (tr);
} else
pos++;
}
}
 
从这段代码中,我们可以看出,有两个条件可以释放一个事务:
B1:当事务的状态是XXX_TERMINATED时
B2:即这个事务的已经存在2分钟了
 
void eXosip_release_terminated_publications (void)
{
  eXosip_pub_t *jpub;
  eXosip_pub_t *jpubnext;
  time_t now = time (NULL);
 
  for (jpub = eXosip.j_pub; jpub != NULL;)
    {
      jpubnext = jpub->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 publication\n"));
              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 <= 299)
            {
              OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO1, NULL,
                                      "Release a terminated publication with 2xx\n"));
              REMOVE_ELEMENT (eXosip.j_pub, jpub);
              _eXosip_pub_free (jpub);
            }
        }
 
      jpub = jpubnext;
    }
 
}
这个函数中,我们可以看出,只有当p_period值为0,并且有p_last_tr不为空时,才会从eXosip.j_pub链表中移除,同 时_eXosip_pub_free (jpub),这个里面将会把事务p_last_tr添加到eXosip.j_transactions链表中。
 
从上面看出,只要事务在eXosip.j_transactions链表中,那么它就将被释放。这也是osip库中释放事务的唯一条路径。
那么对publish消息来说,事务到eXosip.j_transactions链表有两条路径:一、用户发送terminated publish,即Expires头域值为0的请求publish消息。二、在调用eXosip_publish()函数时,会把上个事务加入到 eXosip.j_transactions链表中。这里就有一个问题,假如用户A只发送了initial publish,就再也不发送publish包了,那么这个用户A的这次publish产生的事务将永远存在eXosip.j_pub链表中。直到用户A 下次发送publish时,才会将这次事务移除到期Xosip.j_transactions链表中。这也就是说假如用户A发送第二个publish等了 一年,那么前次的事务将会在eXosip.j_pub中存放一年,如果等二年,那么就存放二年,如果再也不发,那么将永远存在。
publish产生的事务在客户和服务端的区别就在这里。
 
publish服务端在osip库中的生存期:
 
服务端会接收到一个publish请求消息。此时生一个事件(event)。用这个事件去对应的事务链表 (eXosip->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 from the oSIP stack) */
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO3, NULL, "Release a terminated transaction\n"));
osip_list_remove (&eXosip.j_transactions, pos);
__eXosip_delete_jinfo (tr);
osip_transaction_free (tr);
} else if (tr->birth_time + 180 < now)    /* Wait a max of 2 minutes */
{
osip_list_remove (&eXosip.j_transactions, pos);
__eXosip_delete_jinfo (tr);
osip_transaction_free (tr);
} else
pos++;
}
从这段代码中,我们可以看出,有两个条件可以释放一个事务:
B1:当事务的状态是XXX_TERMINATED时
B2:即这个事务的已经存在2分钟了
在A1这种条件下,正常结束时,是运用B1条件的。如果这个事务发生了什么异常,则会运用B2条件。
 
A2:客户端在500ms内没有收到响应,则会再次发送publish请求消息。
在这种情况下,当程序到达i = osip_find_transaction_and_add_event (eXosip.j_osip, se)这个函数时,此时由于第一次publish进来时,创建了一个事务,所以第二次publish来时,就能找到这个事务,即这里的返回值i是等于0 的,但是,可以从这个函数名看出来,在这个函数的内部,当找到这个事务时,会将这个事件添加到这个事务中,但是不会唤醒线程,只有等线程的等待时间到了后 才会去执行这个事务。当执行这个事务时,在nist_fsm.c文件,判断事务状态(NIST_TRYING)和事件类型(RCV_REQUEST),当 然是找不到的,所以在osip_transaction.c文件的osip_transaction_execute()这个函数内,也就是整个文件的 423行会打印USELESS EVENT.然后回到线程中,继续等待事件。这也就是为什么我们在应用程序里也只会看到第一次来的publish,第二次publish请求消息是不会通 知到应用程序的原因。如果这里服务端一直没有响应回给客户端,那么2分钟后,用B2条件释放这个事务。如果回响应,则就和A1条件一样了。
 
可以知道,当客户端没有在timer e的时间段内收到响应包时,会重发请求消息,但是服务端收到重发的请求包时,只是去判断下,但是原来的事务的所有状态均没有改变。
 
publish消息在服务端的生存期我们已经说明白了。但是在A1的那种情况下:
我们知道应用中的publish有如下4种publish请求消息。
1. initial publish
2. refresh publish
3. modify publish
4. terminated publish
这4种publish到达服务端,都会产生一个事务,即每次的publish是不相同的事务。也就是说一个publish请求,再加上一个publish响应就是一个完整的事务了。

图片1
(cr0_3)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201202/15706.html]
本文出处:百度博客 作者:cr0_3 原文
顶一下
(1)
100%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容