Connection to servers does not block the process anymore
This commit is contained in:
@@ -155,8 +155,7 @@ int Poller::poll(const std::chrono::milliseconds& timeout)
|
||||
else if (this->fds[i].revents & POLLIN)
|
||||
{
|
||||
auto socket_handler = this->socket_handlers.at(this->fds[i].fd);
|
||||
if (socket_handler->is_connected())
|
||||
socket_handler->on_recv();
|
||||
socket_handler->on_recv();
|
||||
nb_events--;
|
||||
}
|
||||
else if (this->fds[i].revents & POLLOUT)
|
||||
@@ -164,6 +163,8 @@ int Poller::poll(const std::chrono::milliseconds& timeout)
|
||||
auto socket_handler = this->socket_handlers.at(this->fds[i].fd);
|
||||
if (socket_handler->is_connected())
|
||||
socket_handler->on_send();
|
||||
else
|
||||
socket_handler->connect();
|
||||
nb_events--;
|
||||
}
|
||||
}
|
||||
@@ -185,7 +186,12 @@ int Poller::poll(const std::chrono::milliseconds& timeout)
|
||||
if (revents[i].events & EPOLLIN)
|
||||
socket_handler->on_recv();
|
||||
if (revents[i].events & EPOLLOUT)
|
||||
socket_handler->on_send();
|
||||
{
|
||||
if (socket_handler->is_connected())
|
||||
socket_handler->on_send();
|
||||
else
|
||||
socket_handler->connect();
|
||||
}
|
||||
}
|
||||
return nb_events;
|
||||
#endif
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -17,18 +18,38 @@
|
||||
|
||||
SocketHandler::SocketHandler():
|
||||
poller(nullptr),
|
||||
connected(false)
|
||||
connected(false),
|
||||
connecting(false)
|
||||
{
|
||||
this->init_socket();
|
||||
}
|
||||
|
||||
void SocketHandler::init_socket()
|
||||
{
|
||||
if ((this->socket = ::socket(AF_INET, SOCK_STREAM, 0)) == -1)
|
||||
throw std::runtime_error("Could not create socket");
|
||||
int optval = 1;
|
||||
if (::setsockopt(this->socket, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) == -1)
|
||||
log_warning("Failed to enable TCP keepalive on socket: " << strerror(errno));
|
||||
// Set the socket on non-blocking mode. This is useful to receive a EAGAIN
|
||||
// error when connect() would block, to not block the whole process if a
|
||||
// remote is not responsive.
|
||||
const int existing_flags = ::fcntl(this->socket, F_GETFL, 0);
|
||||
if ((existing_flags == -1) ||
|
||||
(::fcntl(this->socket, F_SETFL, existing_flags | O_NONBLOCK) == -1))
|
||||
throw std::runtime_error(std::string("Could not initialize socket: ") + strerror(errno));
|
||||
}
|
||||
|
||||
std::pair<bool, std::string> SocketHandler::connect(const std::string& address, const std::string& port)
|
||||
void SocketHandler::connect(const std::string& address, const std::string& port)
|
||||
{
|
||||
log_info("Trying to connect to " << address << ":" << port);
|
||||
if (!this->connecting)
|
||||
{
|
||||
log_info("Trying to connect to " << address << ":" << port);
|
||||
}
|
||||
this->connecting = true;
|
||||
this->address = address;
|
||||
this->port = port;
|
||||
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_flags = 0;
|
||||
@@ -43,7 +64,8 @@ std::pair<bool, std::string> SocketHandler::connect(const std::string& address,
|
||||
{
|
||||
log_warning(std::string("getaddrinfo failed: ") + gai_strerror(res));
|
||||
this->close();
|
||||
return std::make_pair(false, gai_strerror(res));
|
||||
this->on_connection_failed(gai_strerror(res));
|
||||
return ;
|
||||
}
|
||||
|
||||
// Make sure the alloced structure is always freed at the end of the
|
||||
@@ -56,14 +78,28 @@ std::pair<bool, std::string> SocketHandler::connect(const std::string& address,
|
||||
{
|
||||
log_info("Connection success.");
|
||||
this->connected = true;
|
||||
this->connecting = false;
|
||||
this->on_connected();
|
||||
return std::make_pair(true, "");
|
||||
return ;
|
||||
}
|
||||
else if (errno == EINPROGRESS || errno == EALREADY)
|
||||
{ // retry this process later, when the socket
|
||||
// is ready to be written on.
|
||||
log_debug("Need to retry connecting later..." << strerror(errno));
|
||||
this->poller->watch_send_events(this);
|
||||
return ;
|
||||
}
|
||||
log_info("Connection failed:" << strerror(errno));
|
||||
}
|
||||
log_error("All connection attempts failed.");
|
||||
this->close();
|
||||
return std::make_pair(false, "");
|
||||
this->on_connection_failed(strerror(errno));
|
||||
return ;
|
||||
}
|
||||
|
||||
void SocketHandler::connect()
|
||||
{
|
||||
this->connect(this->address, this->port);
|
||||
}
|
||||
|
||||
void SocketHandler::set_poller(Poller* poller)
|
||||
@@ -114,11 +150,11 @@ void SocketHandler::on_send()
|
||||
void SocketHandler::close()
|
||||
{
|
||||
this->connected = false;
|
||||
this->connecting = false;
|
||||
this->poller->remove_socket_handler(this->get_socket());
|
||||
::close(this->socket);
|
||||
// recreate the socket for a potential future usage
|
||||
if ((this->socket = ::socket(AF_INET, SOCK_STREAM, 0)) == -1)
|
||||
throw std::runtime_error("Could not create socket");
|
||||
this->init_socket();
|
||||
}
|
||||
|
||||
socket_t SocketHandler::get_socket() const
|
||||
@@ -139,3 +175,8 @@ bool SocketHandler::is_connected() const
|
||||
{
|
||||
return this->connected;
|
||||
}
|
||||
|
||||
bool SocketHandler::is_connecting() const
|
||||
{
|
||||
return this->connecting;
|
||||
}
|
||||
|
||||
@@ -19,10 +19,15 @@ class SocketHandler
|
||||
public:
|
||||
explicit SocketHandler();
|
||||
virtual ~SocketHandler() {}
|
||||
/**
|
||||
* (re-)Initialize the socket
|
||||
*/
|
||||
void init_socket();
|
||||
/**
|
||||
* Connect to the remote server, and call on_connected() if this succeeds
|
||||
*/
|
||||
std::pair<bool, std::string> connect(const std::string& address, const std::string& port);
|
||||
void connect(const std::string& address, const std::string& port);
|
||||
void connect();
|
||||
/**
|
||||
* Set the pointer to the given Poller, to communicate with it.
|
||||
*/
|
||||
@@ -53,6 +58,11 @@ public:
|
||||
* Called when the connection is successful.
|
||||
*/
|
||||
virtual void on_connected() = 0;
|
||||
/**
|
||||
* Called when the connection fails. Not when it is closed later, just at
|
||||
* the connect() call.
|
||||
*/
|
||||
virtual void on_connection_failed(const std::string& reason) = 0;
|
||||
/**
|
||||
* Called when we detect a disconnection from the remote host.
|
||||
*/
|
||||
@@ -63,6 +73,7 @@ public:
|
||||
*/
|
||||
virtual void parse_in_buffer() = 0;
|
||||
bool is_connected() const;
|
||||
bool is_connecting() const;
|
||||
|
||||
protected:
|
||||
socket_t socket;
|
||||
@@ -86,7 +97,17 @@ protected:
|
||||
* (actually it is sharing our ownership with a Bridge).
|
||||
*/
|
||||
Poller* poller;
|
||||
/**
|
||||
* Hostname we are connected/connecting to
|
||||
*/
|
||||
std::string address;
|
||||
/**
|
||||
* Port we are connected/connecting to
|
||||
*/
|
||||
std::string port;
|
||||
|
||||
bool connected;
|
||||
bool connecting;
|
||||
|
||||
private:
|
||||
SocketHandler(const SocketHandler&) = delete;
|
||||
|
||||
Reference in New Issue
Block a user