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

📄 sip_transaction.c

📁 一个开源SIP协议栈
💻 C
📖 第 1 页 / 共 5 页
字号:
    *p_tsx = tsx;
    return PJ_SUCCESS;
}


/*
 * Bind transaction to a specific transport/listener. 
 */
PJ_DEF(pj_status_t) pjsip_tsx_set_transport(pjsip_transaction *tsx,
					    const pjsip_tpselector *sel)
{
    struct tsx_lock_data lck;

    /* Must be UAC transaction */
    PJ_ASSERT_RETURN(tsx && sel, PJ_EINVAL);

    /* Start locking the transaction. */
    lock_tsx(tsx, &lck);

    /* Decrement reference counter of previous transport selector */
    pjsip_tpselector_dec_ref(&tsx->tp_sel);

    /* Copy transport selector structure .*/
    pj_memcpy(&tsx->tp_sel, sel, sizeof(*sel));

    /* Increment reference counter */
    pjsip_tpselector_add_ref(&tsx->tp_sel);

    /* Unlock transaction. */
    unlock_tsx(tsx, &lck);

    return PJ_SUCCESS;
}


/*
 * Set the UAC absolute transaction timeout.
 */
PJ_DEF(pj_status_t) pjsip_tsx_set_uac_timeout(pjsip_transaction *tsx,
					      unsigned msec_time,
					      pjsip_tsx_timeout_policy policy)
{
    PJ_ASSERT_RETURN(tsx && tsx->role==PJSIP_ROLE_UAC &&
		     tsx->state==PJSIP_TSX_STATE_NULL, PJ_EINVALIDOP);

    tsx->msec_timeout = msec_time;
    tsx->timeout_policy = policy;

    return PJ_SUCCESS;
}


/*
 * Set transaction status code and reason.
 */
static void tsx_set_status_code(pjsip_transaction *tsx,
			   	int code, const pj_str_t *reason)
{
    tsx->status_code = code;
    if (reason)
	pj_strdup(tsx->pool, &tsx->status_text, reason);
    else
	tsx->status_text = *pjsip_get_status_text(code);
}


/*
 * Forcely terminate transaction.
 */
PJ_DEF(pj_status_t) pjsip_tsx_terminate( pjsip_transaction *tsx, int code )
{
    struct tsx_lock_data lck;

    PJ_LOG(5,(tsx->obj_name, "Request to terminate transaction"));

    PJ_ASSERT_RETURN(tsx != NULL, PJ_EINVAL);
    PJ_ASSERT_RETURN(code >= 200, PJ_EINVAL);

    if (tsx->state == PJSIP_TSX_STATE_TERMINATED)
	return PJ_SUCCESS;

    lock_tsx(tsx, &lck);
    tsx_set_status_code(tsx, code, NULL);
    tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, PJSIP_EVENT_USER, NULL);
    unlock_tsx(tsx, &lck);

    return PJ_SUCCESS;
}


/*
 * This function is called by TU to send a message.
 */
PJ_DEF(pj_status_t) pjsip_tsx_send_msg( pjsip_transaction *tsx, 
				        pjsip_tx_data *tdata )
{
    pjsip_event event;
    struct tsx_lock_data lck;
    pj_status_t status;

    if (tdata == NULL)
	tdata = tsx->last_tx;

    PJ_ASSERT_RETURN(tdata != NULL, PJ_EINVALIDOP);

    PJ_LOG(5,(tsx->obj_name, "Sending %s in state %s",
                             pjsip_tx_data_get_info(tdata),
			     state_str[tsx->state]));

    PJSIP_EVENT_INIT_TX_MSG(event, tdata);

    /* Dispatch to transaction. */
    lock_tsx(tsx, &lck);

    /* Set transport selector to tdata */
    pjsip_tx_data_set_transport(tdata, &tsx->tp_sel);

    /* Dispatch to state handler */
    status = (*tsx->state_handler)(tsx, &event);

    unlock_tsx(tsx, &lck);

    /* Only decrement reference counter when it returns success.
     * (This is the specification from the .PDF design document).
     */
    if (status == PJ_SUCCESS) {
	pjsip_tx_data_dec_ref(tdata);
    }

    return status;
}


/*
 * This function is called by endpoint when incoming message for the 
 * transaction is received.
 */
PJ_DEF(void) pjsip_tsx_recv_msg( pjsip_transaction *tsx, 
				 pjsip_rx_data *rdata)
{
    pjsip_event event;
    struct tsx_lock_data lck;
    pj_status_t status;

    PJ_LOG(5,(tsx->obj_name, "Incoming %s in state %s", 
	      pjsip_rx_data_get_info(rdata), state_str[tsx->state]));

    /* Put the transaction in the rdata's mod_data. */
    rdata->endpt_info.mod_data[mod_tsx_layer.mod.id] = tsx;

    /* Init event. */
    PJSIP_EVENT_INIT_RX_MSG(event, rdata);

    /* Dispatch to transaction. */
    lock_tsx(tsx, &lck);
    status = (*tsx->state_handler)(tsx, &event);
    unlock_tsx(tsx, &lck);
}


/* Callback called by send message framework */
static void send_msg_callback( pjsip_send_state *send_state,
			       pj_ssize_t sent, pj_bool_t *cont )
{
    pjsip_transaction *tsx = send_state->token;
    struct tsx_lock_data lck;

    lock_tsx(tsx, &lck);

    if (sent > 0) {
	/* Successfully sent! */
	pj_assert(send_state->cur_transport != NULL);

	if (tsx->transport != send_state->cur_transport) {
	    /* Update transport. */
	    if (tsx->transport) {
		pjsip_transport_dec_ref(tsx->transport);
		tsx->transport = NULL;
	    }
	    tsx->transport = send_state->cur_transport;
	    pjsip_transport_add_ref(tsx->transport);

	    /* Update remote address. */
	    tsx->addr_len = send_state->addr.entry[send_state->cur_addr].addr_len;
	    pj_memcpy(&tsx->addr, 
		      &send_state->addr.entry[send_state->cur_addr].addr,
		      tsx->addr_len);

	    /* Update is_reliable flag. */
	    tsx->is_reliable = PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport);
	}

	/* Clear pending transport flag. */
	tsx->transport_flag &= ~(TSX_HAS_PENDING_TRANSPORT);

	/* Mark that we have resolved the addresses. */
	tsx->transport_flag |= TSX_HAS_RESOLVED_SERVER;

	/* Pending destroy? */
	if (tsx->transport_flag & TSX_HAS_PENDING_DESTROY) {
	    tsx_set_state( tsx, PJSIP_TSX_STATE_DESTROYED, 
			   PJSIP_EVENT_UNKNOWN, NULL );
	    unlock_tsx(tsx, &lck);
	    return;
	}

	/* Need to transmit a message? */
	if (tsx->transport_flag & TSX_HAS_PENDING_SEND) {
	    tsx->transport_flag &= ~(TSX_HAS_PENDING_SEND);
	    tsx_send_msg(tsx, tsx->last_tx);
	}

	/* Need to reschedule retransmission? */
	if (tsx->transport_flag & TSX_HAS_PENDING_RESCHED) {
	    tsx->transport_flag &= ~(TSX_HAS_PENDING_RESCHED);

	    /* Only update when transport turns out to be unreliable. */
	    if (!tsx->is_reliable) {
		tsx_resched_retransmission(tsx);
	    }
	}

    } else {
	/* Failed to send! */
	pj_assert(sent != 0);

	/* If transaction is using the same transport as the failed one, 
	 * release the transport.
	 */
	if (send_state->cur_transport==tsx->transport &&
	    tsx->transport != NULL)
	{
	    pjsip_transport_dec_ref(tsx->transport);
	    tsx->transport = NULL;
	}

	if (!*cont) {
	    char errmsg[PJ_ERR_MSG_SIZE];
	    pj_str_t err;

	    tsx->transport_err = -sent;

	    err =pj_strerror(-sent, errmsg, sizeof(errmsg));

	    PJ_LOG(2,(tsx->obj_name, 
		      "Failed to send %s! err=%d (%s)",
		      pjsip_tx_data_get_info(send_state->tdata), -sent,
		      errmsg));

	    /* Clear pending transport flag. */
	    tsx->transport_flag &= ~(TSX_HAS_PENDING_TRANSPORT);

	    /* Mark that we have resolved the addresses. */
	    tsx->transport_flag |= TSX_HAS_RESOLVED_SERVER;

	    /* Terminate transaction, if it's not already terminated. */
	    tsx_set_status_code(tsx, PJSIP_SC_TSX_TRANSPORT_ERROR, &err);
	    if (tsx->state != PJSIP_TSX_STATE_TERMINATED &&
		tsx->state != PJSIP_TSX_STATE_DESTROYED)
	    {
		tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
			       PJSIP_EVENT_TRANSPORT_ERROR, send_state->tdata);
	    }

	} else {
	    char errmsg[PJ_ERR_MSG_SIZE];

	    PJ_LOG(2,(tsx->obj_name, 
		      "Temporary failure in sending %s, "
		      "will try next server. Err=%d (%s)",
		      pjsip_tx_data_get_info(send_state->tdata), -sent,
		      pj_strerror(-sent, errmsg, sizeof(errmsg)).ptr));
	}
    }

    unlock_tsx(tsx, &lck);
}


/* Transport callback. */
static void transport_callback(void *token, pjsip_tx_data *tdata,
			       pj_ssize_t sent)
{
    if (sent < 0) {
	pjsip_transaction *tsx = token;
	struct tsx_lock_data lck;
	char errmsg[PJ_ERR_MSG_SIZE];
	pj_str_t err;

	tsx->transport_err = -sent;

	err = pj_strerror(-sent, errmsg, sizeof(errmsg));

	PJ_LOG(2,(tsx->obj_name, "Transport failed to send %s! Err=%d (%s)",
		  pjsip_tx_data_get_info(tdata), -sent, errmsg));

	lock_tsx(tsx, &lck);

	/* Dereference transport. */
	pjsip_transport_dec_ref(tsx->transport);
	tsx->transport = NULL;

	/* Terminate transaction. */
	tsx_set_status_code(tsx, PJSIP_SC_TSX_TRANSPORT_ERROR, &err);
	tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
		       PJSIP_EVENT_TRANSPORT_ERROR, tdata );

	unlock_tsx(tsx, &lck);
   }
}

/*
 * Send message to the transport.
 */
static pj_status_t tsx_send_msg( pjsip_transaction *tsx, 
                                 pjsip_tx_data *tdata)
{
    pj_status_t status = PJ_SUCCESS;

    PJ_ASSERT_RETURN(tsx && tdata, PJ_EINVAL);

    /* Send later if transport is still pending. */
    if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
	tsx->transport_flag |= TSX_HAS_PENDING_SEND;
	return PJ_SUCCESS;
    }

    /* If we have the transport, send the message using that transport.
     * Otherwise perform full transport resolution.
     */
    if (tsx->transport) {
	status = pjsip_transport_send( tsx->transport, tdata, &tsx->addr,
				       tsx->addr_len, tsx, 
				       &transport_callback);
	if (status == PJ_EPENDING)
	    status = PJ_SUCCESS;

	if (status != PJ_SUCCESS) {
	    char errmsg[PJ_ERR_MSG_SIZE];

	    PJ_LOG(2,(tsx->obj_name, 
		      "Error sending %s: Err=%d (%s)",
		      pjsip_tx_data_get_info(tdata), status, 
		      pj_strerror(status, errmsg, sizeof(errmsg)).ptr));

	    /* On error, release transport to force using full transport
	     * resolution procedure.
	     */
	    if (tsx->transport) {
		pjsip_transport_dec_ref(tsx->transport);
		tsx->transport = NULL;
	    }
	    tsx->addr_len = 0;
	    tsx->res_addr.transport = NULL;
	    tsx->res_addr.addr_len = 0;
	} else {
	    return PJ_SUCCESS;
	}
    }

    /* We are here because we don't have transport, or we failed to send
     * the message using existing transport. If we haven't resolved the
     * server before, then begin the long process of resolving the server
     * and send the message with possibly new server.
     */
    pj_assert(status != PJ_SUCCESS || tsx->transport == NULL);

    /* If we have resolved the server, we treat the error as permanent error.
     * Terminate transaction with transport error failure.
     */
    if (tsx->transport_flag & TSX_HAS_RESOLVED_SERVER) {
	
	char errmsg[PJ_ERR_MSG_SIZE];
	pj_str_t err;

	if (status == PJ_SUCCESS) {
	    pj_assert(!"Unexpected status!");
	    status = PJ_EUNKNOWN;
	}

	/* We have resolved the server!.
	 * Treat this as permanent transport error.
	 */
	err = pj_strerror(status, errmsg, sizeof(errmsg));

	PJ_LOG(2,(tsx->obj_name, 
		  "Transport error, terminating transaction. "
		  "Err=%d (%s)",
		  status, errmsg));

	tsx_set_status_code(tsx, PJSIP_SC_TSX_TRANSPORT_ERROR, &err);
	tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
		       PJSIP_EVENT_TRANSPORT_ERROR, NULL );

	return status;
    }

    /* Must add reference counter because the send request functions
     * decrement the reference counter.
     */
    pjsip_tx_data_add_ref(tdata);

    /* Begin resolving destination etc to send the message. */
    if (tdata->msg->type == PJSIP_REQUEST_MSG) {

	tsx->transport_flag |= TSX_HAS_PENDING_TRANSPORT;
	status = pjsip_endpt_send_request_stateless(tsx->endpt, tdata, tsx,
						    &send_msg_callback);
	if (status == PJ_EPENDING)
	    status = PJ_SUCCESS;
	if (status != PJ_SUCCESS)
	    pjsip_tx_data_dec_ref(tdata);
	
	/* Check if transaction is terminated. */
	if (status==PJ_SUCCESS && tsx->state == PJSIP_TSX_STATE_TERMINATED)
	    status = tsx->transport_err;

    } else {

	tsx->transport_flag |= TSX_HAS_PENDING_TRANSPORT;
	status = pjsip_endpt_send_response( tsx->endpt, &tsx->res_addr, 
					    tdata, tsx, 
					    &send_msg_callback);
	if (status == PJ_EPENDING)
	    status = PJ_SUCCESS;
	if (status != PJ_SUCCESS)
	    pjsip_tx_data_dec_ref(tdata);

	/* Check if transaction is terminated. */
	if (status==PJ_SUCCESS && tsx->state == PJSIP_TSX_STATE_TERMINATED)
	    status = tsx->transport_err;

    }


    return status;
}


/*
 * Retransmit last message sent.
 */
static void tsx_resched_retransmission( pjsip_transaction *tsx )
{
    pj_time_val timeout;
    int msec_time;

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

    msec_time = (1 << (tsx->retransmit_count)) * PJSIP_T1_TIMEOUT;

    if (tsx->role == PJSIP_ROLE_UAC) {
	/* Retransmission for non-INVITE transaction caps-off at T2 */
	if (msec_time>PJSIP_T2_TIMEOUT && tsx->method.id!=PJSIP_INVITE_METHOD)

⌨️ 快捷键说明

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