Cannot get boost::asio::read_until to properly read a line. [new to boost/asio]
Hello everyone! I'm writing an IRC framework (still in very rough, early stages), and am having trouble reading a single line from the server (in this case irc.freenode.net:6667). Ideally, I would get output that looks like the following:
:wolfe.freenode.net NOTICE * :*** Looking up your hostname... :wolfe.freenode.net NOTICE * :*** Checking Ident :wolfe.freenode.net NOTICE * :*** Your forward and reverse DNS do not match, ignoring hostname :wolfe.freenode.net NOTICE * :*** No Ident response :wolfe.freenode.net 001 foobarbazball :Welcome to the freenode Internet Relay Chat Network foobarbazball :wolfe.freenode.net 002 foobarbazball :Your host is wolfe.freenode.net[ 91.217.189.44/6667], running version ircd-seven-1.1.3 :wolfe.freenode.net 003 foobarbazball :This server was created Sun Aug 2 2015 at 18:58:22 UTC :wolfe.freenode.net 004 foobarbazball wolfe.freenode.net ircd-seven-1.1.3 DOQRSZaghilopswz CFILMPQSbcefgijklmnopqrstvz bkloveqjfI :wolfe.freenode.net 005 foobarbazball CHANTYPES=# EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLMPQScgimnprstz CHANLIMIT=#:120 PREFIX=(ov)@+ MAXLIST=bqeI:100 MODES=4 NETWORK=freenode KNOCK STATUSMSG=@+ CALLERID=g :are supported by this server :wolfe.freenode.net 005 foobarbazball CASEMAPPING=rfc1459 CHARSET=ascii NICKLEN=16 CHANNELLEN=50 TOPICLEN=390 ETRACE CPRIVMSG CNOTICE DEAF=D MONITOR=100 FNC TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: :are supported by this server :wolfe.freenode.net 005 foobarbazball EXTBAN=$,ajrxz WHOX CLIENTVER=3.0 SAFELIST ELIST=CTU :are supported by this server :wolfe.freenode.net 251 foobarbazball :There are 153 users and 93407 invisible on 27 servers :wolfe.freenode.net 252 foobarbazball 24 :IRC Operators online :wolfe.freenode.net 253 foobarbazball 8 :unknown connection(s) :wolfe.freenode.net 254 foobarbazball 54832 :channels formed :wolfe.freenode.net 255 foobarbazball :I have 6701 clients and 1 servers :wolfe.freenode.net 265 foobarbazball 6701 7241 :Current local users 6701, max 7241 :wolfe.freenode.net 266 foobarbazball 93560 97293 :Current global users 93560, max 97293 :wolfe.freenode.net 250 foobarbazball :Highest connection count: 7242 (7241 clients) (668628 connections received) :wolfe.freenode.net 375 foobarbazball :- wolfe.freenode.net Message of the Day - :wolfe.freenode.net 372 foobarbazball :- Welcome to wolfe.freenode.net in Stockholm, SE. :wolfe.freenode.net 372 foobarbazball :- Thanks to http://www.portlane.com/ for sponsoring :wolfe.freenode.net 372 foobarbazball :- this server! :wolfe.freenode.net 372 foobarbazball :- :wolfe.freenode.net 372 foobarbazball :- WOLFE, GENE [1931-]. Prolific writer of short stories and :wolfe.freenode.net 372 foobarbazball :- novels. His best-known work is the multi-volume novel The :wolfe.freenode.net 372 foobarbazball :- Book of the New Sun. He has won multiple awards including :wolfe.freenode.net 372 foobarbazball :- the Nebula Award, the World Fantasy Award, The Campell :wolfe.freenode.net 372 foobarbazball :- Memorial Award and the Locus Award. He was awarded the World :wolfe.freenode.net 372 foobarbazball :- Fantasy Award for lifetime achievement in 1996. :wolfe.freenode.net 372 foobarbazball :- :wolfe.freenode.net 372 foobarbazball :- Welcome to freenode - supporting the free and open source :wolfe.freenode.net 372 foobarbazball :- software communities since 1998. :wolfe.freenode.net 372 foobarbazball :- :wolfe.freenode.net 372 foobarbazball :- By connecting to freenode you indicate that you have read and :wolfe.freenode.net 372 foobarbazball :- accept our policies as set out on http://www.freenode.net :wolfe.freenode.net 372 foobarbazball :- freenode runs an open proxy scanner. Please join #freenode for :wolfe.freenode.net 372 foobarbazball :- any network-related questions or queries, where a number of :wolfe.freenode.net 372 foobarbazball :- volunteer staff and helpful users will be happy to assist you. :wolfe.freenode.net 372 foobarbazball :- :wolfe.freenode.net 372 foobarbazball :- You can meet us at FOSSCON ( http://www.fosscon.org) where we get :wolfe.freenode.net 372 foobarbazball :- together with like-minded FOSS enthusiasts for talks and :wolfe.freenode.net 372 foobarbazball :- real-life collaboration. :wolfe.freenode.net 372 foobarbazball :- :wolfe.freenode.net 372 foobarbazball :- We would like to thank Private Internet Access :wolfe.freenode.net 372 foobarbazball :- ( https://www.privateinternetaccess.com/) and the other :wolfe.freenode.net 372 foobarbazball :- organisations that help keep freenode and our other projects :wolfe.freenode.net 372 foobarbazball :- running for their sustained support. :wolfe.freenode.net 372 foobarbazball :- :wolfe.freenode.net 372 foobarbazball :- In particular we would like to thank the sponsor :wolfe.freenode.net 372 foobarbazball :- of this server, details of which can be found above. :wolfe.freenode.net 372 foobarbazball :- :wolfe.freenode.net 376 foobarbazball :End of /MOTD command. :foobarbazball MODE foobarbazball :+i
My output on the other hand looks more like:
:sendak.freenode.net NOTICE * :*** Looking up your hostname... :sendak.freenode.net NOTICE * :*** Checking Ident :sendak.freenode.net NOTICE * :*** No Ident response :sendak.freenode.net 001 squawker :Welcome to the freenode Internet Relay Chat Network squawker Ibq,k,flj,CFLMPQScgimnprstz CHANLIMIT=#:120 PREFIX=(ov)@+ MAXLIST=bqeI:100 MODES=4 NETWORK=freenode KNOCK STATUSMSG=@+ CALLERID=g :are supported by this server upported by this server 93546, max 95827 freenode.net 372 squawker :- SENDAK, MAURICE [1928-2012]. The author of Seven Little 372 squawker :- Welcome to freenode - supporting the free and open source wker :- any network-related questions or queries, where a number of sendak.freenode.net 372 squawker :- We would like to thank Private Internet Access :- of this server, details of which can be found above.
main.cpp:
#include
/* #include #include */ int main(int argc, char *argv[]) { /* if(argc > 2) { logger::log(logger::output_type::STDERR, "usage: {1} [config_file]", argv[0]); return 1; } */ client::irc bot( "irc.freenode.net", // server to connect to "6667", // port to connect on "", // password false, // don't connect with ssl "squawker", // nick "squawker", // realname "A c++11 Utility bot written by nchambers", // ident "#cplusplus.com", // channels to join "." // command prefix );
bot.on_connect([&bot]() -> void { bot.nickserv(client::ns::identify, <REDACTED>); }); /* bot.add_handler(client::input_type::privmsg, [](std::string channel, std::string user, std::string msg) -> void { logger::log(logger::output_type::STDOUT, "[{1}/{2}] {3}", user, channel, msg); });
bot.add_handler(client::input_type::command, "load", [&bot](std::string channel, std::string user, std::string msg) -> void { bot.load_plugin(plugin::plugin(msg));
if(bot.plugin_operation_successful()) { logger::log(logger::output_type::STDOUT, "successfully loaded plugin {1}", msg); bot.msg(channel, "{1}: successfully loaded plugin {2}", user, msg); }
else { logger::log(logger::output_type::STDERR, "failure to load plugin {1}", msg); bot.msg(channel, "{1}: failure to load plugin {2}", user, msg); } });
bot.add_handler(client::input_type::command, "unload", [&bot](std::string channel, std::string user, std::string msg) -> void { bot.unload_plugin(plugin::plugin(msg));
if(bot.plugin_operation_successful()) { logger::log(logger::output_type::STDOUT, "successfully unloaded plugin {1}", msg); bot.msg(channel, "{1}: successfully unloaded plugin {2}", user, msg); }
else { logger::log(logger::output_type::STDERR, "failure to unload plugin {1}", msg); bot.msg(channel, "{1}: failure to unload plugin {2}", user, msg); } });
bot.add_handler(client::input_type::command, "reload", [&bot](std::string channel, std::string user, std::string msg) -> void { bot.reload_plugin(plugin::plugin(msg));
if(bot.plugin_operation_successful()) { logger::log(logger::output_type::STDOUT, "successfully reloaded plugin {1}", msg); bot.msg(channel, "{1}: successfully reloaded plugin {2}", user, msg); }
else { logger::log(logger::output_type::STDERR, "failure to reload plugin {1}", msg); bot.msg(channel, "{1}: failure to reload plugin {2}", user, msg); } });*/
bot.run(); return 0; }
client.hpp:
#ifndef _ROOT_SQUAWKER_IRCCLIENT_CLIENT_HPP__ #define _ROOT_SQUAWKER_IRCCLIENT_CLIENT_HPP__
#include <string> #include <functional>
#include
namespace client { enum class ns: int { identify=0, };
enum class input_type: int { privmsg=0, };
class irc { public: irc(std::string, std::string, std::string, bool, std::string, std::string, std::string, std::string, std::string);
void on_connect(std::function
); void nickserv(client::ns, std::string); void run(); void send(std::string);
private: void connect(); std::string readline();
std::string server; std::string port; std::string pass; bool use_ssl; std::string nick; std::string realname; std::string ident; std::string channels; std::string prefix; std::function
on_connect_callback; boost::asio::io_service io_service; boost::asio::ip::tcp::socket socket; }; } #endif
client.cpp:
#include
#include <iostream> #include <sstream> #include <array> client::irc::irc( std::string server, std::string port, std::string pass, bool use_ssl, std::string nick, std::string realname, std::string ident, std::string channels, std::string prefix ) : server(server), port(port), pass(pass), use_ssl(use_ssl), nick(nick), realname(realname), ident(ident), channels(channels), prefix(prefix), on_connect_callback([]()->void{}), socket(this->io_service) { }
void client::irc::on_connect(std::function
on_connect_callback) { this->on_connect_callback = on_connect_callback; } void client::irc::nickserv(client::ns command, std::string arguments) { switch(command) { case client::ns::identify: this->send("nickserv identify " + arguments); break; } }
void client::irc::run() { this->connect();
while(true) { std::array
buf; boost::system::error_code error; size_t len = this->socket.read_some(boost::asio::buffer(buf), error); if(error == boost::asio::error::eof) { break; }
else if (error) { throw boost::system::system_error(error); }
std::cout.write(buf.data(), len); } }
void client::irc::send(std::string msg) { this->socket.write_some(boost::asio::buffer(msg + std::string("\r\n"))); }
void client::irc::connect() { boost::asio::ip::tcp::resolver resolver(this->io_service); boost::asio::ip::tcp::resolver::query query(this->server, this->port); boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
boost::asio::connect(this->socket, endpoint_iterator); this->send("NICK " + this->nick); this->send("USER " + this->realname + " * * :" + this->ident);
while(true) { std::string line = this->readline(); std::cout << line << std::endl;
if(line == ":squawker MODE squawker :+i") { break; } }
this->on_connect_callback(); }
std::string client::irc::readline() { boost::asio::streambuf buffer; size_t received = boost::asio::read_until(this->socket, buffer, "\r\n"); std::istream is(&buffer); std::string line; std::getline(is, line); return std::string(std::begin(line), std::end(line)-1); }
any help would be very much appreciated! thank you in advance!
On 5/11/2015 07:25, Nick Chambers wrote:
Hello everyone! I'm writing an IRC framework (still in very rough, early stages), and am having trouble reading a single line from the server (in this case irc.freenode.net:6667 http://irc.freenode.net:6667). [...] void client::irc::send(std::string msg) { this->socket.write_some(boost::asio::buffer(msg + std::string("\r\n"))); }
Firstly, be aware that write_some is permitted to write less than the full amount of data provided, if eg. some internal buffer is nearly full. If you're not going to check for this, then use the free function boost::asio::write instead, which handles it for you.
std::string client::irc::readline() { boost::asio::streambuf buffer; size_t received = boost::asio::read_until(this->socket, buffer, "\r\n"); std::istream is(&buffer); std::string line; std::getline(is, line); return std::string(std::begin(line), std::end(line)-1); }
Secondly, note that read_until may (and usually does) read data beyond the delimiter into the buffer. It is therefore essential to preserve this buffer between calls (ie. don't make it a local variable), and to properly mark the bytes that you are extracting from it as consumed so that you don't keep getting the same line again. This also means that you shouldn't mix calls to read_until with other read calls on the same socket, at least not without taking the buffer's existing contents into account.
Mere moments ago, quoth I:
Secondly, note that read_until may (and usually does) read data beyond the delimiter into the buffer. It is therefore essential to preserve this buffer between calls (ie. don't make it a local variable), and to properly mark the bytes that you are extracting from it as consumed so that you don't keep getting the same line again.
To clarify, std::getline does consume the bytes. If you were using some other method to extract data from the streambuf then you might need to do something more explicit.
participants (2)
-
Gavin Lambert
-
Nick Chambers