rcpp_framework/core/net/connector.cpp

218 lines
4.7 KiB
C++

/**
*
* @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";
}
}