⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sip_transaction.c

📁 一个开源的sip源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
{
    pj_time_val timeout;
    int msec_time;

    pj_assert((tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) == 0);

    if (tsx->role==PJSIP_ROLE_UAC && tsx->status_code >= 100)
	msec_time = PJSIP_T2_TIMEOUT;
    else
	msec_time = (1 << (tsx->retransmit_count)) * PJSIP_T1_TIMEOUT;

    if (tsx->role == PJSIP_ROLE_UAC) {
	pj_assert(tsx->status_code < 200);
	/* Retransmission for non-INVITE transaction caps-off at T2 */
	if (msec_time>PJSIP_T2_TIMEOUT && tsx->method.id!=PJSIP_INVITE_METHOD)
	    msec_time = PJSIP_T2_TIMEOUT;
    } else {
	/* Retransmission of INVITE final response also caps-off at T2 */
	pj_assert(tsx->status_code >= 200);
	if (msec_time>PJSIP_T2_TIMEOUT)
	    msec_time = PJSIP_T2_TIMEOUT;
    }

    timeout.sec = msec_time / 1000;
    timeout.msec = msec_time % 1000;
    tsx->retransmit_timer.id = TIMER_ACTIVE;
    pjsip_endpt_schedule_timer( tsx->endpt, &tsx->retransmit_timer, 
				&timeout);
}

/*
 * Retransmit last message sent.
 */
static pj_status_t tsx_retransmit( pjsip_transaction *tsx, int resched)
{
    pj_status_t status;

    PJ_ASSERT_RETURN(tsx->last_tx!=NULL, PJ_EBUG);

    PJ_LOG(5,(tsx->obj_name, "Retransmiting %s, count=%d, restart?=%d", 
	      pjsip_tx_data_get_info(tsx->last_tx), 
	      tsx->retransmit_count, resched));

    ++tsx->retransmit_count;

    /* Restart timer T1 first before sending the message to ensure that
     * retransmission timer is not engaged when loop transport is used.
     */
    if (resched) {
	pj_assert(tsx->state != PJSIP_TSX_STATE_CONFIRMED);
	if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
	    tsx->transport_flag |= TSX_HAS_PENDING_RESCHED;
	} else {
	    tsx_resched_retransmission(tsx);
	}
    }

    status = tsx_send_msg( tsx, tsx->last_tx);
    if (status != PJ_SUCCESS) {
	return status;
    }

    return PJ_SUCCESS;
}


/*
 * Handler for events in state Null.
 */
static pj_status_t tsx_on_state_null( pjsip_transaction *tsx, 
                                      pjsip_event *event )
{
    pj_status_t status;

    pj_assert(tsx->state == PJSIP_TSX_STATE_NULL);

    if (tsx->role == PJSIP_ROLE_UAS) {

	/* Set state to Trying. */
	pj_assert(event->type == PJSIP_EVENT_RX_MSG &&
		  event->body.rx_msg.rdata->msg_info.msg->type == 
		    PJSIP_REQUEST_MSG);
	tsx_set_state( tsx, PJSIP_TSX_STATE_TRYING, PJSIP_EVENT_RX_MSG,
		       event->body.rx_msg.rdata);

    } else {
	pjsip_tx_data *tdata;

	/* Must be transmit event. 
	 * You may got this assertion when using loop transport with delay 
	 * set to zero. That would cause on_rx_response() callback to be 
	 * called before tsx_send_msg() has completed.
	 */
	PJ_ASSERT_RETURN(event->type == PJSIP_EVENT_TX_MSG, PJ_EBUG);

	/* Get the txdata */
	tdata = event->body.tx_msg.tdata;

	/* Save the message for retransmission. */
	if (tsx->last_tx && tsx->last_tx != tdata) {
	    pjsip_tx_data_dec_ref(tsx->last_tx);
	    tsx->last_tx = NULL;
	}
	if (tsx->last_tx != tdata) {
	    tsx->last_tx = tdata;
	    pjsip_tx_data_add_ref(tdata);
	}

	/* Send the message. */
        status = tsx_send_msg( tsx, tdata);
	if (status != PJ_SUCCESS) {
	    return status;
	}

	/* Start Timer B (or called timer F for non-INVITE) for transaction 
	 * timeout.
	 */
	tsx->timeout_timer.id = TIMER_ACTIVE;
	pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, 
				    &timeout_timer_val);

	/* Start Timer A (or timer E) for retransmission only if unreliable 
	 * transport is being used.
	 */
	if (!tsx->is_reliable)  {
	    tsx->retransmit_count = 0;
	    if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
		tsx->transport_flag |= TSX_HAS_PENDING_RESCHED;
	    } else {
	    	tsx->retransmit_timer.id = TIMER_ACTIVE;
		pjsip_endpt_schedule_timer(tsx->endpt, &tsx->retransmit_timer,
					   &t1_timer_val);
	    }
	}

	/* Move state. */
	tsx_set_state( tsx, PJSIP_TSX_STATE_CALLING, 
                       PJSIP_EVENT_TX_MSG, tdata);
    }

    return PJ_SUCCESS;
}


/*
 * State Calling is for UAC after it sends request but before any responses
 * is received.
 */
static pj_status_t tsx_on_state_calling( pjsip_transaction *tsx, 
				         pjsip_event *event )
{
    pj_assert(tsx->state == PJSIP_TSX_STATE_CALLING);
    pj_assert(tsx->role == PJSIP_ROLE_UAC);

    if (event->type == PJSIP_EVENT_TIMER && 
	event->body.timer.entry == &tsx->retransmit_timer) 
    {
        pj_status_t status;

	/* Retransmit the request. */
        status = tsx_retransmit( tsx, 1 );
	if (status != PJ_SUCCESS) {
	    return status;
	}

    } else if (event->type == PJSIP_EVENT_TIMER && 
	       event->body.timer.entry == &tsx->timeout_timer) 
    {

	/* Cancel retransmission timer. */
	if (tsx->retransmit_timer.id != 0) {
	    pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer);
	    tsx->retransmit_timer.id = 0;
	}
	tsx->transport_flag &= ~(TSX_HAS_PENDING_RESCHED);

	/* Set status code */
	tsx_set_status_code(tsx, PJSIP_SC_TSX_TIMEOUT, NULL);

	/* Inform TU. */
	tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
                       PJSIP_EVENT_TIMER, &tsx->timeout_timer);

	/* Transaction is destroyed */
	//return PJSIP_ETSXDESTROYED;

    } else if (event->type == PJSIP_EVENT_RX_MSG) {
	pjsip_msg *msg;
	int code;

	/* Get message instance */
	msg = event->body.rx_msg.rdata->msg_info.msg;

	/* Better be a response message. */
	if (msg->type != PJSIP_RESPONSE_MSG)
	    return PJSIP_ENOTRESPONSEMSG;

	code = msg->line.status.code;

	/* If the response is final, cancel both retransmission and timeout
	 * timer.
	 */
	if (code >= 200) {
	    if (tsx->retransmit_timer.id != 0) {
		pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer);
		tsx->retransmit_timer.id = 0;
	    }

	    if (tsx->timeout_timer.id != 0) {
		pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer);
		tsx->timeout_timer.id = 0;
	    }

	} else {
	    /* Cancel retransmit timer (for non-INVITE transaction, the
	     * retransmit timer will be rescheduled at T2.
	     */
	    if (tsx->retransmit_timer.id != 0) {
		pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer);
		tsx->retransmit_timer.id = 0;
	    }

	    /* For provisional response, only cancel retransmit when this
	     * is an INVITE transaction. For non-INVITE, section 17.1.2.1
	     * of RFC 3261 says that:
	     *	- retransmit timer is set to T2
	     *	- timeout timer F is not deleted.
	     */
	    if (tsx->method.id == PJSIP_INVITE_METHOD) {

		/* Cancel timeout timer */
		pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer);

	    } else {
		if (!tsx->is_reliable) {
		    tsx->retransmit_timer.id = TIMER_ACTIVE;
		    pjsip_endpt_schedule_timer(tsx->endpt, 
					       &tsx->retransmit_timer,
					       &t2_timer_val);
		}
	    }
	}
	 
	tsx->transport_flag &= ~(TSX_HAS_PENDING_RESCHED);


	/* Discard retransmission message if it is not INVITE.
	 * The INVITE tdata is needed in case we have to generate ACK for
	 * the final response.
	 */
	/* Keep last_tx for authorization. */
	//blp: always keep last_tx until transaction is destroyed
	//code = msg->line.status.code;
	//if (tsx->method.id != PJSIP_INVITE_METHOD && code!=401 && code!=407) {
	//    pjsip_tx_data_dec_ref(tsx->last_tx);
	//    tsx->last_tx = NULL;
	//}

	/* Processing is similar to state Proceeding. */
	tsx_on_state_proceeding_uac( tsx, event);

    } else {
	pj_assert(!"Unexpected event");
        return PJ_EBUG;
    }

    return PJ_SUCCESS;
}


/*
 * State Trying is for UAS after it received request but before any responses
 * is sent.
 * Note: this is different than RFC3261, which can use Trying state for
 *	 non-INVITE client transaction (bug in RFC?).
 */
static pj_status_t tsx_on_state_trying( pjsip_transaction *tsx, 
                                        pjsip_event *event)
{
    pj_status_t status;

    pj_assert(tsx->state == PJSIP_TSX_STATE_TRYING);

    /* This state is only for UAS */
    pj_assert(tsx->role == PJSIP_ROLE_UAS);

    /* Better be transmission of response message.
     * If we've got request retransmission, this means that the TU hasn't
     * transmitted any responses within 500 ms, which is not allowed. If
     * this happens, just ignore the event (we couldn't retransmit last
     * response because we haven't sent any!).
     */
    if (event->type != PJSIP_EVENT_TX_MSG) {
	return PJ_SUCCESS;
    }

    /* The rest of the processing of the event is exactly the same as in
     * "Proceeding" state.
     */
    status = tsx_on_state_proceeding_uas( tsx, event);

    /* Inform the TU of the state transision if state is still State_Trying */
    if (status==PJ_SUCCESS && tsx->state == PJSIP_TSX_STATE_TRYING) {

	tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, 
                       PJSIP_EVENT_TX_MSG, event->body.tx_msg.tdata);

    }

    return status;
}


/*
 * Handler for events in Proceeding for UAS
 * This state happens after the TU sends provisional response.
 */
static pj_status_t tsx_on_state_proceeding_uas( pjsip_transaction *tsx,
                                                pjsip_event *event)
{
    pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING || 
	      tsx->state == PJSIP_TSX_STATE_TRYING);

    /* This state is only for UAS. */
    pj_assert(tsx->role == PJSIP_ROLE_UAS);

    /* Receive request retransmission. */
    if (event->type == PJSIP_EVENT_RX_MSG) {

        pj_status_t status;

	/* Must have last response sent. */
	PJ_ASSERT_RETURN(tsx->last_tx != NULL, PJ_EBUG);

	/* Send last response */
	if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
	    tsx->transport_flag |= TSX_HAS_PENDING_SEND;
	} else {
	    status = tsx_send_msg(tsx, tsx->last_tx);
	    if (status != PJ_SUCCESS)
		return status;
	}
	
    } else if (event->type == PJSIP_EVENT_TX_MSG ) {
	pjsip_tx_data *tdata = event->body.tx_msg.tdata;
        pj_status_t status;

	/* The TU sends response message to the request. Save this message so
	 * that we can retransmit the last response in case we receive request
	 * retransmission.
	 */
	pjsip_msg *msg = tdata->msg;

	/* This can only be a response message. */
	PJ_ASSERT_RETURN(msg->type==PJSIP_RESPONSE_MSG, PJSIP_ENOTRESPONSEMSG);

	/* Update last status */
	tsx_set_status_code(tsx, msg->line.status.code, 
			    &msg->line.status.reason);

	/* Discard the saved last response (it will be updated later as
	 * necessary).
	 */
	if (tsx->last_tx && tsx->last_tx != tdata) {
	    pjsip_tx_data_dec_ref( tsx->last_tx );
	    tsx->last_tx = NULL;
	}

	/* Send the message. */
        status = tsx_send_msg(tsx, tdata);
	if (status != PJ_SUCCESS) {
	    return status;
	}

	// Update To tag header for RFC2543 transaction.
	// TODO:

	/* Update transaction state */
	if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 100)) {

	    if (tsx->last_tx != tdata) {
		tsx->last_tx = tdata;
		pjsip_tx_data_add_ref( tdata );
	    }

	    tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, 
                           PJSIP_EVENT_TX_MSG, tdata );

	} else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {

	    if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->handle_200resp==0) {

		/* 2xx class message is not saved, because retransmission 
                 * is handled by TU.
		 */
		tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
                               PJSIP_EVENT_TX_MSG, tdata );

		/* Transaction is destroyed. */
		//return PJSIP_ETSXDESTROYED;

	    } else {
		pj_time_val timeout;

		if (tsx->method.id == PJSIP_INVITE_METHOD) {
		    tsx->retransmit_count = 0;
		    if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
			tsx->transport_flag |= TSX_HAS_PENDING_RESCHED;
		    } else {
		    	tsx->retransmit_timer.id = TIMER_ACTIVE;
			pjsip_endpt_schedule_timer( tsx->endpt, 
						    &tsx->retransmit_timer,
						    &t1_timer_val);
		    }
		}

		/* Save last response sent for retransmission when request 
		 * retransmission is received.
		 */
		if (tsx->last_tx != tdata) {
		    tsx->last_tx = tdata;
		    pjsip_tx_data_add_ref(tdata);
		}

		/* Setup timeout timer: */
		
		if (tsx->method.id == PJSIP_INVITE_METHOD) {
		    
		    /* Start Timer H at 64*T1 for INVITE server transaction,
		     * regardless of transport.
		     */
		    timeout = timeout_timer_val;
		    
		} else if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) {
		    
		    /* For non-INVITE, start timer J at 64*T1 for unreliable
		     * transport.
		     */
		    timeout = timeout_timer_val;
		    
		} else {
		    
		    /* Transaction terminates immediately for non-INVITE when
		     * reliable transport is used.
		     */
		    timeout.sec = timeout.msec = 0;
		}

		tsx->timeout_timer.id = TIMER_ACTIVE;
		pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, 
                                            &time

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -