/** * * @file Connector.cc * @author An Tao * * Public header file in trantor lib. * * Copyright 2018, An Tao. All rights reserved. * Use of this source code is governed by a BSD-style license * that can be found in the License file. * * */ #include "connector.h" #include "core/loops/channel.h" #include "core/net/socket.h" using namespace trantor; Connector::Connector(EventLoop *loop, const InetAddress &addr, bool retry) : loop_(loop), serverAddr_(addr), retry_(retry) { } Connector::Connector(EventLoop *loop, InetAddress &&addr, bool retry) : loop_(loop), serverAddr_(std::move(addr)), retry_(retry) { } void Connector::start() { connect_ = true; loop_->runInLoop([this]() { startInLoop(); }); } void Connector::restart() { } void Connector::stop() { } void Connector::startInLoop() { loop_->assertInLoopThread(); assert(status_ == Status::Disconnected); if (connect_) { connect(); } else { LOG_DEBUG << "do not connect"; } } void Connector::connect() { int sockfd = Socket::createNonblockingSocketOrDie(serverAddr_.family()); errno = 0; int ret = Socket::connect(sockfd, serverAddr_); int savedErrno = (ret == 0) ? 0 : errno; switch (savedErrno) { case 0: case EINPROGRESS: case EINTR: case EISCONN: LOG_TRACE << "connecting"; connecting(sockfd); break; case EAGAIN: case EADDRINUSE: case EADDRNOTAVAIL: case ECONNREFUSED: case ENETUNREACH: if (retry_) { retry(sockfd); } break; case EACCES: case EPERM: case EAFNOSUPPORT: case EALREADY: case EBADF: case EFAULT: case ENOTSOCK: LOG_SYSERR << "connect error in Connector::startInLoop " << savedErrno; #ifndef _WIN32 ::close(sockfd); #else closesocket(sockfd); #endif if (errorCallback_) errorCallback_(); break; default: LOG_SYSERR << "Unexpected error in Connector::startInLoop " << savedErrno; #ifndef _WIN32 ::close(sockfd); #else closesocket(sockfd); #endif if (errorCallback_) errorCallback_(); break; } } void Connector::connecting(int sockfd) { status_ = Status::Connecting; assert(!channelPtr_); channelPtr_.reset(new Channel(loop_, sockfd)); channelPtr_->setWriteCallback( std::bind(&Connector::handleWrite, shared_from_this())); channelPtr_->setErrorCallback( std::bind(&Connector::handleError, shared_from_this())); channelPtr_->setCloseCallback( std::bind(&Connector::handleError, shared_from_this())); LOG_TRACE << "connecting:" << sockfd; channelPtr_->enableWriting(); } int Connector::removeAndResetChannel() { channelPtr_->disableAll(); channelPtr_->remove(); int sockfd = channelPtr_->fd(); // Can't reset channel_ here, because we are inside Channel::handleEvent loop_->queueInLoop([this]() { channelPtr_.reset(); }); return sockfd; } void Connector::handleWrite() { if (status_ == Status::Connecting) { int sockfd = removeAndResetChannel(); int err = Socket::getSocketError(sockfd); if (err) { LOG_WARN << "Connector::handleWrite - SO_ERROR = " << err << " " << strerror_tl(err); if (retry_) { retry(sockfd); } else { #ifndef _WIN32 ::close(sockfd); #else closesocket(sockfd); #endif } if (errorCallback_) { errorCallback_(); } } else if (Socket::isSelfConnect(sockfd)) { LOG_WARN << "Connector::handleWrite - Self connect"; if (retry_) { retry(sockfd); } else { #ifndef _WIN32 ::close(sockfd); #else closesocket(sockfd); #endif } if (errorCallback_) { errorCallback_(); } } else { status_ = Status::Connected; if (connect_) { newConnectionCallback_(sockfd); } else { #ifndef _WIN32 ::close(sockfd); #else closesocket(sockfd); #endif } } } else { // what happened? assert(status_ == Status::Disconnected); } } void Connector::handleError() { if (status_ == Status::Connecting) { status_ = Status::Disconnected; int sockfd = removeAndResetChannel(); int err = Socket::getSocketError(sockfd); LOG_TRACE << "SO_ERROR = " << err << " " << strerror_tl(err); if (retry_) { retry(sockfd); } else { #ifndef _WIN32 ::close(sockfd); #else closesocket(sockfd); #endif } if (errorCallback_) { errorCallback_(); } } } void Connector::retry(int sockfd) { assert(retry_); #ifndef _WIN32 ::close(sockfd); #else closesocket(sockfd); #endif status_ = Status::Disconnected; if (connect_) { LOG_INFO << "Connector::retry - Retry connecting to " << serverAddr_.toIpPort() << " in " << retryInterval_ << " milliseconds. "; loop_->runAfter(retryInterval_ / 1000.0, std::bind(&Connector::startInLoop, shared_from_this())); retryInterval_ = retryInterval_ * 2; if (retryInterval_ > maxRetryInterval_) retryInterval_ = maxRetryInterval_; } else { LOG_DEBUG << "do not connect"; } }