public void RemoveExpiredTransactions() { try { lock (m_transactions) { List <string> expiredTransactionIds = new List <string>(); foreach (SIPTransaction transaction in m_transactions.Values) { if (transaction.TransactionType == SIPTransactionTypesEnum.Invite) { if (transaction.TransactionState == SIPTransactionStatesEnum.Confirmed) { // Need to wait until the transaction timeout period is reached in case any ACK re-transmits are received. if (DateTime.Now.Subtract(transaction.CompletedAt).TotalMilliseconds >= m_t6) { expiredTransactionIds.Add(transaction.TransactionId); } } else if (transaction.TransactionState == SIPTransactionStatesEnum.Completed) { if (DateTime.Now.Subtract(transaction.CompletedAt).TotalMilliseconds >= m_t6) { expiredTransactionIds.Add(transaction.TransactionId); } } else if (transaction.HasTimedOut) { // For INVITES need to give timed out transactions time to send the reliable repsonses and receive the ACKs. if (DateTime.Now.Subtract(transaction.TimedOutAt).TotalSeconds >= m_t6) { expiredTransactionIds.Add(transaction.TransactionId); } } else if (transaction.TransactionState == SIPTransactionStatesEnum.Proceeding) { if (DateTime.Now.Subtract(transaction.Created).TotalMilliseconds >= m_maxRingTime) { // INVITE requests that have been ringing too long. transaction.HasTimedOut = true; transaction.TimedOutAt = DateTime.Now; transaction.DeliveryPending = false; transaction.DeliveryFailed = true; transaction.FireTransactionTimedOut(); } } else if (DateTime.Now.Subtract(transaction.Created).TotalMilliseconds >= m_t6) { //logger.LogDebug("INVITE transaction (" + transaction.TransactionId + ") " + transaction.TransactionRequestURI.ToString() + " in " + transaction.TransactionState + " has been alive for " + DateTime.Now.Subtract(transaction.Created).TotalSeconds.ToString("0") + "."); if (transaction.TransactionState == SIPTransactionStatesEnum.Calling || transaction.TransactionState == SIPTransactionStatesEnum.Trying) { transaction.HasTimedOut = true; transaction.TimedOutAt = DateTime.Now; transaction.DeliveryPending = false; transaction.DeliveryFailed = true; transaction.FireTransactionTimedOut(); } } } else if (transaction.HasTimedOut) { expiredTransactionIds.Add(transaction.TransactionId); } else if (DateTime.Now.Subtract(transaction.Created).TotalMilliseconds >= m_t6) { if (transaction.TransactionState == SIPTransactionStatesEnum.Calling || transaction.TransactionState == SIPTransactionStatesEnum.Trying || transaction.TransactionState == SIPTransactionStatesEnum.Proceeding) { //logger.LogWarning("Timed out transaction in SIPTransactionEngine, should have been timed out in the SIP Transport layer. " + transaction.TransactionRequest.Method + "."); transaction.DeliveryPending = false; transaction.DeliveryFailed = true; transaction.TimedOutAt = DateTime.Now; transaction.HasTimedOut = true; transaction.FireTransactionTimedOut(); } expiredTransactionIds.Add(transaction.TransactionId); } } foreach (string transactionId in expiredTransactionIds) { if (m_transactions.ContainsKey(transactionId)) { SIPTransaction expiredTransaction = m_transactions[transactionId]; expiredTransaction.FireTransactionRemoved(); RemoveTransaction(expiredTransaction); } } } //PrintPendingTransactions(); } catch (Exception excp) { logger.LogError("Exception RemoveExpiredTransaction. " + excp.Message); } }
private void RemoveExpiredTransactions() { try { List <string> expiredTransactionIds = new List <string>(); var now = DateTime.Now; foreach (var(_, transaction) in m_pendingTransactions) { if (transaction.TransactionType == SIPTransactionTypesEnum.InviteClient || transaction.TransactionType == SIPTransactionTypesEnum.InviteServer) { if (transaction.TransactionState == SIPTransactionStatesEnum.Confirmed) { // Need to wait until the transaction timeout period is reached in case any ACK re-transmits are received. // No proactive actions need to be undertaken in the Confirmed state. If any ACK requests are received // we use this tx to ensure they get matched and not detected as orphans. if (now.Subtract(transaction.CompletedAt).TotalMilliseconds >= m_t6) { expiredTransactionIds.Add(transaction.TransactionId); } } else if (transaction.TransactionState == SIPTransactionStatesEnum.Completed) { // If a server INVITE transaction is in the following state: // - Completed it means we sent a final response but did not receive an ACK // (which is what transitions the tx to the Confirmed state). if (now.Subtract(transaction.CompletedAt).TotalMilliseconds >= m_t6) { // It's important that an un-Confirmed server INVITE tx fires the event to // inform the application that the tx timed out. This allows it to make a decision // on whether to clean up resources such as RTP and media or whether to give the // caller the benefit of the doubt and see if it was an ACK only problem and // give them a chance to send RTP. transaction.Expire(now); expiredTransactionIds.Add(transaction.TransactionId); } } else if (transaction.DeliveryFailed && transaction.TransactionFinalResponse == null) { // This transaction timed out attempting to send the initial request. No // final response was received so it does not need to be kept alive for ACK // re-transmits. expiredTransactionIds.Add(transaction.TransactionId); } else if (transaction.TransactionState == SIPTransactionStatesEnum.Proceeding) { if (now.Subtract(transaction.Created).TotalMilliseconds >= m_maxRingTime) { // INVITE requests that have been ringing too long. This can apply to both // client and server INVITE transactions. transaction.Expire(now); expiredTransactionIds.Add(transaction.TransactionId); } } else if (now.Subtract(transaction.Created).TotalMilliseconds >= m_t6) { //logger.LogDebug("INVITE transaction (" + transaction.TransactionId + ") " + transaction.TransactionRequestURI.ToString() + " in " + transaction.TransactionState + " has been alive for " + DateTime.Now.Subtract(transaction.Created).TotalSeconds.ToString("0") + "."); // If a client INVITE transaction is in the following states: // - Calling: it means no response of any kind (provisional or final) was received from the server in time. // - Trying: it means all we got was a "100 Trying" response without any follow up progress indications or final response. if (transaction.TransactionState == SIPTransactionStatesEnum.Calling || transaction.TransactionState == SIPTransactionStatesEnum.Trying) { transaction.Expire(now); expiredTransactionIds.Add(transaction.TransactionId); } else { // The INVITE transaction has ended up in some other state and should be removed. expiredTransactionIds.Add(transaction.TransactionId); } } } else if (transaction.HasTimedOut) { expiredTransactionIds.Add(transaction.TransactionId); } else if (now.Subtract(transaction.Created).TotalMilliseconds >= m_t6) { if (transaction.TransactionState == SIPTransactionStatesEnum.Calling || transaction.TransactionState == SIPTransactionStatesEnum.Trying || transaction.TransactionState == SIPTransactionStatesEnum.Proceeding) { //logger.LogWarning("Timed out transaction in SIPTransactionEngine, should have been timed out in the SIP Transport layer. " + transaction.TransactionRequest.Method + "."); transaction.Expire(now); } expiredTransactionIds.Add(transaction.TransactionId); } } foreach (string transactionId in expiredTransactionIds) { if (m_pendingTransactions.ContainsKey(transactionId)) { SIPTransaction expiredTransaction = m_pendingTransactions[transactionId]; expiredTransaction.FireTransactionRemoved(); RemoveTransaction(expiredTransaction); } } } catch (Exception excp) { logger.LogError("Exception RemoveExpiredTransaction. " + excp.Message); } }