Commit 358b53fa authored by louiz’'s avatar louiz’

Move the basic socket implementation into the SocketHandler class

(that is, the read/write/connect/etc)
Because this code is actually common for both the IrcClient and
XmppComponent class. These two classes have to implement some higher level
callbacks (parsing the data provided in the buffers, doing stuff when the
connection is done) instead of doing the read/write/connect low level
things.
parent 7f580dbc
#include <libirc/irc_client.hpp> #include <libirc/irc_client.hpp>
#include <libirc/irc_message.hpp> #include <libirc/irc_message.hpp>
#include <network/poller.hpp>
#include <utils/scopeguard.hpp>
#include <sys/types.h>
#include <sys/socket.h>
#include <cstring>
#include <netdb.h>
#include <unistd.h>
#include <iostream> #include <iostream>
#include <stdexcept> #include <stdexcept>
...@@ -15,8 +7,6 @@ ...@@ -15,8 +7,6 @@
IrcClient::IrcClient() IrcClient::IrcClient()
{ {
std::cout << "IrcClient()" << std::endl; std::cout << "IrcClient()" << std::endl;
if ((this->socket = ::socket(AF_INET, SOCK_STREAM, 0)) == -1)
throw std::runtime_error("Could not create socket");
} }
IrcClient::~IrcClient() IrcClient::~IrcClient()
...@@ -24,80 +14,6 @@ IrcClient::~IrcClient() ...@@ -24,80 +14,6 @@ IrcClient::~IrcClient()
std::cout << "~IrcClient()" << std::endl; std::cout << "~IrcClient()" << std::endl;
} }
void IrcClient::on_recv()
{
char buf[4096];
ssize_t size = ::recv(this->socket, buf, 4096, 0);
if (0 == size)
this->on_connection_close();
else if (-1 == static_cast<ssize_t>(size))
throw std::runtime_error("Error reading from socket");
else
{
this->in_buf += std::string(buf, size);
this->parse_in_buffer();
}
}
void IrcClient::on_send()
{
const ssize_t res = ::send(this->socket, this->out_buf.data(), this->out_buf.size(), 0);
if (res == -1)
{
perror("send");
this->close();
}
else
{
this->out_buf = this->out_buf.substr(res, std::string::npos);
if (this->out_buf.empty())
this->poller->stop_watching_send_events(this);
}
}
socket_t IrcClient::get_socket() const
{
return this->socket;
}
void IrcClient::connect(const std::string& address, const std::string& port)
{
std::cout << "Trying to connect to " << address << ":" << port << std::endl;
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_flags = 0;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
struct addrinfo* addr_res;
const int res = ::getaddrinfo(address.c_str(), port.c_str(), &hints, &addr_res);
// Make sure the alloced structure is always freed at the end of the
// function
utils::ScopeGuard sg([&addr_res](){ freeaddrinfo(addr_res); });
if (res != 0)
{
perror("getaddrinfo");
throw std::runtime_error("getaddrinfo failed");
}
for (struct addrinfo* rp = addr_res; rp; rp = rp->ai_next)
{
std::cout << "One result" << std::endl;
if (::connect(this->socket, rp->ai_addr, rp->ai_addrlen) == 0)
{
std::cout << "Connection success." << std::endl;
this->on_connected();
return ;
}
std::cout << "Connection failed:" << std::endl;
perror("connect");
}
std::cout << "All connection attempts failed." << std::endl;
this->close();
}
void IrcClient::on_connected() void IrcClient::on_connected()
{ {
} }
...@@ -108,12 +24,6 @@ void IrcClient::on_connection_close() ...@@ -108,12 +24,6 @@ void IrcClient::on_connection_close()
this->close(); this->close();
} }
void IrcClient::close()
{
this->poller->remove_socket_handler(this->get_socket());
::close(this->socket);
}
void IrcClient::parse_in_buffer() void IrcClient::parse_in_buffer()
{ {
while (true) while (true)
...@@ -143,11 +53,7 @@ void IrcClient::send_message(IrcMessage&& message) ...@@ -143,11 +53,7 @@ void IrcClient::send_message(IrcMessage&& message)
res += " " + arg; res += " " + arg;
} }
res += "\r\n"; res += "\r\n";
this->out_buf += res; this->send_data(std::move(res));
if (!this->out_buf.empty())
{
this->poller->watch_send_events(this);
}
} }
void IrcClient::send_user_command(const std::string& username, const std::string& realname) void IrcClient::send_user_command(const std::string& username, const std::string& realname)
......
...@@ -18,20 +18,6 @@ class IrcClient: public SocketHandler ...@@ -18,20 +18,6 @@ class IrcClient: public SocketHandler
public: public:
explicit IrcClient(); explicit IrcClient();
~IrcClient(); ~IrcClient();
/**
* We read the data, try to parse it and generate some event if
* one or more full message is available.
*/
void on_recv();
/**
* Just write as much data as possible on the socket.
*/
void on_send();
socket_t get_socket() const;
/**
* Connect to the remote server
*/
void connect(const std::string& address, const std::string& port);
/** /**
* Called when successfully connected to the server * Called when successfully connected to the server
*/ */
...@@ -39,10 +25,6 @@ public: ...@@ -39,10 +25,6 @@ public:
/** /**
* Close the connection, remove us from the poller * Close the connection, remove us from the poller
*/ */
void close();
/**
* Called when we detect an orderly close by the remote endpoint.
*/
void on_connection_close(); void on_connection_close();
/** /**
* Parse the data we have received so far and try to get one or more * Parse the data we have received so far and try to get one or more
...@@ -69,19 +51,6 @@ public: ...@@ -69,19 +51,6 @@ public:
void send_join_command(const std::string& chan_name); void send_join_command(const std::string& chan_name);
private: private:
socket_t socket;
/**
* Where data read from the socket is added, until we can parse a whole
* IRC message, the used data is then removed from that buffer.
*
* TODO: something more efficient than a string.
*/
std::string in_buf;
/**
* Where data is added, when we want to send something to the client.
*/
std::string out_buf;
IrcClient(const IrcClient&) = delete; IrcClient(const IrcClient&) = delete;
IrcClient(IrcClient&&) = delete; IrcClient(IrcClient&&) = delete;
IrcClient& operator=(const IrcClient&) = delete; IrcClient& operator=(const IrcClient&) = delete;
......
#include <network/socket_handler.hpp>
#include <utils/scopeguard.hpp>
#include <network/poller.hpp>
#include <sys/types.h>
#include <sys/socket.h>
#include <cstring>
#include <netdb.h>
#include <unistd.h>
#include <iostream>
SocketHandler::SocketHandler():
poller(nullptr)
{
if ((this->socket = ::socket(AF_INET, SOCK_STREAM, 0)) == -1)
throw std::runtime_error("Could not create socket");
}
void SocketHandler::connect(const std::string& address, const std::string& port)
{
std::cout << "Trying to connect to " << address << ":" << port << std::endl;
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_flags = 0;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
struct addrinfo* addr_res;
const int res = ::getaddrinfo(address.c_str(), port.c_str(), &hints, &addr_res);
// Make sure the alloced structure is always freed at the end of the
// function
utils::ScopeGuard sg([&addr_res](){ freeaddrinfo(addr_res); });
if (res != 0)
{
perror("getaddrinfo");
throw std::runtime_error("getaddrinfo failed");
}
for (struct addrinfo* rp = addr_res; rp; rp = rp->ai_next)
{
std::cout << "One result" << std::endl;
if (::connect(this->socket, rp->ai_addr, rp->ai_addrlen) == 0)
{
std::cout << "Connection success." << std::endl;
this->on_connected();
return ;
}
std::cout << "Connection failed:" << std::endl;
perror("connect");
}
std::cout << "All connection attempts failed." << std::endl;
this->close();
}
void SocketHandler::set_poller(Poller* poller)
{
this->poller = poller;
}
void SocketHandler::on_recv()
{
char buf[4096];
ssize_t size = ::recv(this->socket, buf, 4096, 0);
if (0 == size)
this->on_connection_close();
else if (-1 == static_cast<ssize_t>(size))
throw std::runtime_error("Error reading from socket");
else
{
this->in_buf += std::string(buf, size);
this->parse_in_buffer();
}
}
void SocketHandler::on_send()
{
const ssize_t res = ::send(this->socket, this->out_buf.data(), this->out_buf.size(), 0);
if (res == -1)
{
perror("send");
this->close();
}
else
{
this->out_buf = this->out_buf.substr(res, std::string::npos);
if (this->out_buf.empty())
this->poller->stop_watching_send_events(this);
}
}
void SocketHandler::close()
{
this->poller->remove_socket_handler(this->get_socket());
::close(this->socket);
}
socket_t SocketHandler::get_socket() const
{
return this->socket;
}
void SocketHandler::send_data(std::string&& data)
{
this->out_buf += std::move(data);
if (!this->out_buf.empty())
{
this->poller->watch_send_events(this);
}
}
#ifndef SOCKET_HANDLER_INCLUDED #ifndef SOCKET_HANDLER_INCLUDED
# define SOCKET_HANDLER_INCLUDED # define SOCKET_HANDLER_INCLUDED
#include <string>
typedef int socket_t; typedef int socket_t;
class Poller; class Poller;
...@@ -14,34 +16,65 @@ class Poller; ...@@ -14,34 +16,65 @@ class Poller;
class SocketHandler class SocketHandler
{ {
public: public:
explicit SocketHandler(): explicit SocketHandler();
poller(nullptr) virtual ~SocketHandler() {}
{} /**
* Connect to the remote server, and call on_connected() if this succeeds
*/
void connect(const std::string& address, const std::string& port);
/** /**
* Set the pointer to the given Poller, to communicate with it. * Set the pointer to the given Poller, to communicate with it.
*/ */
void set_poller(Poller* poller) void set_poller(Poller* poller);
{ /**
this->poller = poller; * Reads data in our in_buf and the call parse_in_buf, for the implementor
}; * to handle the data received so far.
*/
void on_recv();
/** /**
* Happens when the socket is ready to be received from. * Write as much data from out_buf as possible, in the socket.
*/ */
virtual void on_recv() = 0; void on_send();
/** /**
* Happens when the socket is ready to be written to. * Add the given data to out_buf and tell our poller that we want to be
* notified when a send event is ready.
*/ */
virtual void on_send() = 0; void send_data(std::string&& data);
/** /**
* Returns the socket that should be handled by the poller. * Returns the socket that should be handled by the poller.
*/ */
virtual socket_t get_socket() const = 0; socket_t get_socket() const;
/**
* Close the connection, remove us from the poller
*/
void close();
/**
* Called when the connection is successful.
*/
virtual void on_connected() = 0;
/**
* Called when we detect a disconnection from the remote host.
*/
virtual void on_connection_close() = 0;
/** /**
* Close the connection. * Handle/consume (some of) the data received so far. If some data is used, the in_buf
* should be truncated, only the unused data should be left untouched.
*/ */
virtual void close() = 0; virtual void parse_in_buffer() = 0;
protected: protected:
socket_t socket;
/**
* Where data read from the socket is added, until we can parse a whole
* IRC message, the used data is then removed from that buffer.
*
* TODO: something more efficient than a string.
*/
std::string in_buf;
/**
* Where data is added, when we want to send something to the client.
*/
std::string out_buf;
/** /**
* A pointer to the poller that manages us, because we need to communicate * A pointer to the poller that manages us, because we need to communicate
* with it, sometimes (for example to tell it that he now needs to watch * with it, sometimes (for example to tell it that he now needs to watch
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment