From 4027ef8c00ee2a5b808c11c7f3ae50cda117d92a Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Sat, 2 Nov 2013 16:04:10 +0100 Subject: Basic IRC message parsing/sending --- src/libirc/irc_client.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++++++- src/libirc/irc_client.hpp | 24 +++++++++++++++++ src/network/poller.cpp | 34 +++++++++++++++++++++--- src/network/poller.hpp | 10 +++++++ 4 files changed, 129 insertions(+), 5 deletions(-) diff --git a/src/libirc/irc_client.cpp b/src/libirc/irc_client.cpp index a29e588..2780b3c 100644 --- a/src/libirc/irc_client.cpp +++ b/src/libirc/irc_client.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -41,6 +42,18 @@ void IrcClient::on_recv() 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 @@ -75,6 +88,7 @@ void IrcClient::connect(const std::string& address, const std::string& port) 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; @@ -84,6 +98,10 @@ void IrcClient::connect(const std::string& address, const std::string& port) this->close(); } +void IrcClient::on_connected() +{ +} + void IrcClient::on_connection_close() { std::cout << "Connection closed by remote server." << std::endl; @@ -98,5 +116,51 @@ void IrcClient::close() void IrcClient::parse_in_buffer() { - std::cout << "Parsing: [" << this->in_buf << "]" << std::endl; + while (true) + { + auto pos = this->in_buf.find("\r\n"); + if (pos == std::string::npos) + break ; + IrcMessage message(this->in_buf.substr(0, pos)); + this->in_buf = this->in_buf.substr(pos + 2, std::string::npos); + std::cout << message << std::endl; + } +} + +void IrcClient::send_message(IrcMessage&& message) +{ + std::string res; + if (!message.prefix.empty()) + res += ":" + std::move(message.prefix) + " "; + res += std::move(message.command); + for (const std::string& arg: message.arguments) + { + if (arg.find(" ") != std::string::npos) + { + res += " :" + arg; + break; + } + res += " " + arg; + } + res += "\r\n"; + this->out_buf += 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) +{ + this->send_message(IrcMessage("USER", {username, "NONE", "NONE", realname})); +} + +void IrcClient::send_nick_command(const std::string& nick) +{ + this->send_message(IrcMessage("NICK", {nick})); +} + +void IrcClient::send_join_command(const std::string& chan_name) +{ + this->send_message(IrcMessage("JOIN", {chan_name})); } diff --git a/src/libirc/irc_client.hpp b/src/libirc/irc_client.hpp index 73e7efd..d1ecbd5 100644 --- a/src/libirc/irc_client.hpp +++ b/src/libirc/irc_client.hpp @@ -1,6 +1,8 @@ #ifndef IRC_CLIENT_INCLUDED # define IRC_CLIENT_INCLUDED +#include + #include #include @@ -30,6 +32,10 @@ public: * 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 */ @@ -43,6 +49,24 @@ public: * complete messages from it. */ void parse_in_buffer(); + /** + * Serialize the given message into a line, and send that into the socket + * (actually, into our out_buf and signal the poller that we want to wach + * for send events to be ready) + */ + void send_message(IrcMessage&& message); + /** + * Send the USER irc command + */ + void send_user_command(const std::string& username, const std::string& realname); + /** + * Send the NICK irc command + */ + void send_nick_command(const std::string& username); + /** + * Send the JOIN irc command + */ + void send_join_command(const std::string& chan_name); private: socket_t socket; diff --git a/src/network/poller.cpp b/src/network/poller.cpp index c7d9eb2..7ab8bc3 100644 --- a/src/network/poller.cpp +++ b/src/network/poller.cpp @@ -59,12 +59,39 @@ void Poller::remove_socket_handler(const socket_t socket) } } +void Poller::watch_send_events(const SocketHandler* const socket_handler) +{ +#if POLLER == POLL + for (size_t i = 0; i <= this->nfds; ++i) + { + if (this->fds[i].fd == socket_handler->get_socket()) + { + this->fds[i].events = POLLIN|POLLOUT; + return; + } + } +#endif + throw std::runtime_error("Cannot watch a non-registered socket for send events"); +} + +void Poller::stop_watching_send_events(const SocketHandler* const socket_handler) +{ +#if POLLER == POLL + for (size_t i = 0; i <= this->nfds; ++i) + { + if (this->fds[i].fd == socket_handler->get_socket()) + { + this->fds[i].events = POLLIN; + return; + } + } +#endif + throw std::runtime_error("Cannot watch a non-registered socket for send events"); +} + void Poller::poll() { #if POLLER == POLL - std::cout << "Polling:" << std::endl; - for (size_t i = 0; i < this->nfds; ++i) - std::cout << "pollfd[" << i << "]: (" << this->fds[i].fd << ")" << std::endl; int res = ::poll(this->fds, this->nfds, -1); if (res < 0) { @@ -93,4 +120,3 @@ void Poller::poll() } #endif } - diff --git a/src/network/poller.hpp b/src/network/poller.hpp index 46a184e..64e78e4 100644 --- a/src/network/poller.hpp +++ b/src/network/poller.hpp @@ -44,6 +44,16 @@ public: * Remove (and stop managing) a SocketHandler, designed by the given socket_t. */ void remove_socket_handler(const socket_t socket); + /** + * Signal the poller that he needs to watch for send events for the given + * SocketHandler. + */ + void watch_send_events(const SocketHandler* const socket_handler); + /** + * Signal the poller that he needs to stop watching for send events for + * this SocketHandler. + */ + void stop_watching_send_events(const SocketHandler* const socket_handler); /** * Wait for all watched events, and call the SocketHandlers' callbacks * when one is ready. -- cgit v1.2.3