mirror of
https://github.com/Relintai/rcpp_framework.git
synced 2024-11-14 04:57:21 +01:00
218 lines
4.7 KiB
C++
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";
|
|
}
|
|
}
|