void OpenDDS::DCPS::TcpDataLink::pre_stop_i() { DBG_ENTRY_LVL("TcpDataLink","pre_stop_i",6); DataLink::pre_stop_i(); TcpReceiveStrategy * rs = dynamic_cast <TcpReceiveStrategy*>(this->receive_strategy_.in()); if (rs != NULL) { // If we received the GRACEFUL_DISCONNECT message from peer before we // initiate the disconnecting of the datalink, then we will not send // the GRACEFUL_DISCONNECT message to the peer. bool disconnected = rs->gracefully_disconnected(); if (!this->connection_.is_nil() && !this->graceful_disconnect_sent_ && !disconnected) { this->send_graceful_disconnect_message(); this->graceful_disconnect_sent_ = true; } } if (!this->connection_.is_nil()) { this->connection_->shutdown(); } }
/// Associate the new connection object with this datalink object. /// The states of the "old" connection object are copied to the new /// connection object and the "old" connection object is replaced by /// the new connection object. int OpenDDS::DCPS::TcpDataLink::reconnect(TcpConnection* connection) { DBG_ENTRY_LVL("TcpDataLink","reconnect",6); // Sanity check - the connection should exist already since we are reconnecting. if (this->connection_.is_nil()) { VDBG_LVL((LM_ERROR, "(%P|%t) ERROR: TcpDataLink::reconnect old connection is nil.\n") , 1); return -1; } this->connection_->transfer(connection); bool released = false; TransportStrategy_rch brs; TransportSendStrategy_rch bss; { GuardType guard2(this->strategy_lock_); if (this->receive_strategy_.is_nil() && this->send_strategy_.is_nil()) { released = true; this->connection_ = 0; } else { brs = this->receive_strategy_; bss = this->send_strategy_; } } TcpConnection_rch conn_rch(connection, false); if (released) { TcpDataLink_rch this_rch(this, false); return this->transport_->connect_tcp_datalink(this_rch, conn_rch); } this->connection_ = conn_rch._retn(); TcpReceiveStrategy* rs = static_cast<TcpReceiveStrategy*>(brs.in()); TcpSendStrategy* ss = static_cast<TcpSendStrategy*>(bss.in()); // Associate the new connection object with the receiveing strategy and disassociate // the old connection object with the receiveing strategy. int rs_result = rs->reset(this->connection_.in()); // Associate the new connection object with the sending strategy and disassociate // the old connection object with the sending strategy. int ss_result = ss->reset(this->connection_.in()); if (rs_result == 0 && ss_result == 0) { return 0; } return -1; }
//Allows the passive side to detect that the active side is connecting again //prior to discovery identifying the released datalink from the active side. //The passive side still believes it has a connection to the remote, however, //the connect has created a new link/connection, thus the passive side can try //to reuse the existing structures but reset it to associate the datalink with //this new connection. int OpenDDS::DCPS::TcpDataLink::reuse_existing_connection(const TcpConnection_rch& connection) { DBG_ENTRY_LVL("TcpDataLink","reuse_existing_connection",6); if (this->is_active_) { return -1; } //Need to check if connection is nil. If connection is not nil, then connection //has previously gone through connection phase so this is a reuse of the connection //proceed to determine if we can reuse/reset existing mechanisms or need to start from //scratch. if (!this->connection_.is_nil()) { VDBG_LVL((LM_DEBUG, "(%P|%t) TcpDataLink::reuse_existing_connection - " "trying to reuse existing connection\n"), 0); this->connection_->transfer(connection.in()); //Connection already exists. TransportStrategy_rch brs; TransportSendStrategy_rch bss; if (this->receive_strategy_.is_nil() && this->send_strategy_.is_nil()) { this->connection_ = 0; return -1; } else { brs = this->receive_strategy_; bss = this->send_strategy_; this->connection_ = connection; TcpReceiveStrategy* rs = static_cast<TcpReceiveStrategy*>(brs.in()); TcpSendStrategy* ss = static_cast<TcpSendStrategy*>(bss.in()); // Associate the new connection object with the receiving strategy and disassociate // the old connection object with the receiving strategy. int rs_result = rs->reset(this->connection_.in()); // Associate the new connection object with the sending strategy and disassociate // the old connection object with the sending strategy. int ss_result = ss->reset(this->connection_.in(), true); if (rs_result == 0 && ss_result == 0) { return 0; } } } return -1; }
// This method is called on acceptor side when the lost connection is detected. // A timer is scheduled to check if a new connection is created within the // passive_reconnect_duration_ period. int OpenDDS::DCPS::TcpConnection::passive_reconnect_i() { DBG_ENTRY_LVL("TcpConnection","passive_reconnect_i",6); GuardType guard(this->reconnect_lock_); // The passive_reconnect_timer_id_ is used as flag to allow the timer scheduled just once. if (this->reconnect_state_ == INIT_STATE) { // Mark the connection lost since the recv/send just failed. this->connected_ = false; if (this->tcp_config_->passive_reconnect_duration_ == 0) return -1; ACE_Time_Value timeout(this->tcp_config_->passive_reconnect_duration_/1000, this->tcp_config_->passive_reconnect_duration_%1000 * 1000); this->reconnect_state_ = PASSIVE_WAITING_STATE; this->link_->notify(DataLink::DISCONNECTED); // It is possible that the passive reconnect is called after the new connection // is accepted and the receive_strategy of this old connection is reset to nil. if (!this->receive_strategy_.is_nil()) { TcpReceiveStrategy* rs = dynamic_cast <TcpReceiveStrategy*>(this->receive_strategy_.in()); // Give a copy to reactor. this->_add_ref(); this->passive_reconnect_timer_id_ = rs->get_reactor()->schedule_timer(this, 0, timeout); if (this->passive_reconnect_timer_id_ == -1) { this->_remove_ref(); ACE_ERROR_RETURN((LM_ERROR, ACE_TEXT("(%P|%t) ERROR: TcpConnection::passive_reconnect_i") ACE_TEXT(", %p.\n"), ACE_TEXT("schedule_timer")), -1); } } } return 0; }
/// This object would be "old" connection object and the provided is the new /// connection object. The "old" connection object will copy its states to /// to the "new" connection object. This is called by the TcpDataLink /// when a new connection is accepted (with a new TcpConnection object). /// We need make the state in "new" connection object consistent with the "old" /// connection object. void OpenDDS::DCPS::TcpConnection::transfer(TcpConnection* connection) { DBG_ENTRY_LVL("TcpConnection","transfer",6); GuardType guard(this->reconnect_lock_); bool notify_reconnect = false; switch (this->reconnect_state_) { case INIT_STATE: // We have not detected the lost connection and the peer is faster than us and // re-established the connection. so do not notify reconnected. break; case LOST_STATE: // The reconnect timed out. case PASSIVE_TIMEOUT_CALLED_STATE: // TODO: If the handle_timeout is called before the old connection // transfer its state to new connection then should we disconnect // the new connection or keep it alive ? // I think we should keep the connection, the user will get a // lost connection notification and then a reconnected notification. notify_reconnect = true; break; case PASSIVE_WAITING_STATE: { TcpReceiveStrategy* rs = dynamic_cast <TcpReceiveStrategy*>(this->receive_strategy_.in()); // Cancel the timer since we got new connection. if (rs->get_reactor()->cancel_timer(this) == -1) { ACE_ERROR((LM_ERROR, ACE_TEXT("(%P|%t) ERROR: TcpConnection::transfer, ") ACE_TEXT(" %p. \n"), ACE_TEXT("cancel_timer"))); } else passive_reconnect_timer_id_ = -1; this->_remove_ref(); notify_reconnect = true; } break; default : ACE_ERROR((LM_ERROR, ACE_TEXT("(%P|%t) ERROR: TcpConnection::transfer, ") ACE_TEXT(" unknown state or it should not be in state=%d \n"), this->reconnect_state_)); break; } // Verify if this acceptor side. if (this->is_connector_ || connection->is_connector_) { ACE_ERROR((LM_ERROR, ACE_TEXT("(%P|%t) ERROR: TcpConnection::transfer, ") ACE_TEXT(" should NOT be called by the connector side \n"))); } this->reconnect_task_.close(1); connection->receive_strategy_ = this->receive_strategy_; connection->send_strategy_ = this->send_strategy_; connection->remote_address_ = this->remote_address_; connection->local_address_ = this->local_address_; connection->tcp_config_ = this->tcp_config_; connection->link_ = this->link_; VDBG((LM_DEBUG, "(%P|%t) DBG: " "transfer(%C:%d->%C:%d) passive reconnected. new con %@ " " old con %@ \n", this->remote_address_.get_host_addr(), this->remote_address_.get_port_number(), this->local_address_.get_host_addr(), this->local_address_.get_port_number(), connection, this)); if (notify_reconnect) { this->reconnect_state_ = RECONNECTED_STATE; this->link_->notify(DataLink::RECONNECTED); } }