diff options
-rw-r--r-- | src/libirc/irc_client.cpp | 96 | ||||
-rw-r--r-- | src/libirc/irc_client.hpp | 31 | ||||
-rw-r--r-- | src/network/socket_handler.cpp | 113 | ||||
-rw-r--r-- | src/network/socket_handler.hpp | 61 |
4 files changed, 161 insertions, 140 deletions
diff --git a/src/libirc/irc_client.cpp b/src/libirc/irc_client.cpp index 2780b3c..a427026 100644 --- a/src/libirc/irc_client.cpp +++ b/src/libirc/irc_client.cpp @@ -1,13 +1,5 @@ #include <libirc/irc_client.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 <stdexcept> @@ -15,8 +7,6 @@ IrcClient::IrcClient() { 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() @@ -24,80 +14,6 @@ IrcClient::~IrcClient() 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() { } @@ -108,12 +24,6 @@ void IrcClient::on_connection_close() this->close(); } -void IrcClient::close() -{ - this->poller->remove_socket_handler(this->get_socket()); - ::close(this->socket); -} - void IrcClient::parse_in_buffer() { while (true) @@ -143,11 +53,7 @@ void IrcClient::send_message(IrcMessage&& message) res += " " + arg; } res += "\r\n"; - this->out_buf += res; - if (!this->out_buf.empty()) - { - this->poller->watch_send_events(this); - } + this->send_data(std::move(res)); } void IrcClient::send_user_command(const std::string& username, const std::string& realname) diff --git a/src/libirc/irc_client.hpp b/src/libirc/irc_client.hpp index d1ecbd5..9778876 100644 --- a/src/libirc/irc_client.hpp +++ b/src/libirc/irc_client.hpp @@ -19,30 +19,12 @@ public: explicit 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 */ void on_connected(); /** * 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(); /** * Parse the data we have received so far and try to get one or more @@ -69,19 +51,6 @@ public: void send_join_command(const std::string& chan_name); 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(IrcClient&&) = delete; IrcClient& operator=(const IrcClient&) = delete; diff --git a/src/network/socket_handler.cpp b/src/network/socket_handler.cpp new file mode 100644 index 0000000..e738302 --- /dev/null +++ b/src/network/socket_handler.cpp @@ -0,0 +1,113 @@ +#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); + } +} diff --git a/src/network/socket_handler.hpp b/src/network/socket_handler.hpp index 165f732..4152a4e 100644 --- a/src/network/socket_handler.hpp +++ b/src/network/socket_handler.hpp @@ -1,6 +1,8 @@ #ifndef SOCKET_HANDLER_INCLUDED # define SOCKET_HANDLER_INCLUDED +#include <string> + typedef int socket_t; class Poller; @@ -14,34 +16,65 @@ class Poller; class SocketHandler { public: - explicit SocketHandler(): - poller(nullptr) - {} + explicit SocketHandler(); + 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. */ - void set_poller(Poller* poller) - { - this->poller = poller; - }; + void set_poller(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. */ - 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: + 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 * with it, sometimes (for example to tell it that he now needs to watch |