c++ - Boost asio async operation bad file descriptor -


i'm usig boost asio irc bot, , 1 of async operation results in bad file descriptor. tried put socket in shared_ptr, still got "bad file descriptor" error. don't know whats wrong in it.

here files, omitted of functions cpp file. want read full file, it's here on github.

the error happends in _read function.

thanks!

irc.hpp

#ifndef h_irc #define h_irc  #include <vector> #include <boost/asio.hpp> #include <boost/tokenizer.hpp> #include <boost/shared_ptr.hpp>  class irc  {     public:         irc(const std::string &server, const std::string &port, const std::function<void()> onconnect);          void connect();         void close();          void user(const std::string &username);         void user(const std::string &username, const std::string &hostname, const std::string &server, const std::string &realname);         void nick(std::string &nickname);         void join(const std::string &chan);         void part(const std::string &chan);         void privmsg(const std::string &to, const std::string &msg);         void command(const std::string &cmd, const std::string &msg);         void command(const std::string &cmd, const std::string &to, const std::string &msg);          void run();      private:         void _read(const boost::system::error_code &error);         void _send(std::string &message);         void _readhandler(const boost::tokenizer<boost::char_separator<char> > &tokenizer);         void _connecthandler(const boost::system::error_code &error);          void _pong(const std::string &ping);          std::string _server;         std::string _port;         std::string _chan;         std::vector<std::function<void (const boost::tokenizer<boost::char_separator<char> >&)>> _readhandlers;         std::function<void()> _onconnect;         boost::asio::streambuf _buffer;         boost::asio::io_service _ios;         boost::shared_ptr<boost::asio::ip::tcp::socket> _socket; };  #endif 

irc.cpp

#include "irc.hpp" #include <iostream> #include <boost/bind.hpp> #include <boost/make_shared.hpp>  irc::irc(const std::string &server, const std::string &port, const std::function<void()> onconnect)     : _server(server), _port(port), _onconnect(onconnect),        _socket(boost::make_shared<boost::asio::ip::tcp::socket>(boost::ref(_ios))) {     // ping handler     _readhandlers.push_back([this](const boost::tokenizer<boost::char_separator<char> > &tokenizer) {         std::vector<std::string> tokens(begin(tokenizer), end(tokenizer));           if(tokens[0].compare("ping") == 0)             _pong(tokens[1]);        });  }  void irc::connect() {     boost::asio::ip::tcp::resolver resolver(_ios);     boost::asio::ip::tcp::resolver::query query(_server, _port);     boost::asio::ip::tcp::resolver::iterator = resolver.resolve(query);     boost::asio::ip::tcp::resolver::iterator end;     boost::system::error_code error = boost::asio::error::host_not_found;      while(it != end)     {         if(!error)             break;          std::cout << "connecting " << _server << " " << _port << std::endl;          boost::asio::async_connect(*_socket, it,             boost::bind(&irc::_connecthandler, this, error)         );          it++;          if(error)             std::cout << "error : " << error.message() << std::endl;      }      if(error)         std::cout << "error connectinf " << _server << " " << error.message() << std::endl;     else         std::cout << "connection success" << std::endl;  }  void irc::close() {     _socket->close();     _ios.stop(); }  void irc::run() {     boost::asio::async_read_until(*_socket, _buffer, "\r\n",         boost::bind(&irc::_read, this,             boost::asio::placeholders::error         )     );      _ios.run(); }  /*  * private  */  void irc::_read(const boost::system::error_code &error) {     if(error)     {         std::cerr << "error in read : " << error.message() << std::endl;     }     else     {         std::string data(buffers_begin(_buffer.data()), buffers_begin(_buffer.data()) + _buffer.size());         std::cout << data << std::endl;               boost::char_separator<char> sep("!@:; ");         boost::tokenizer<boost::char_separator<char> > tokenizer(data, sep);          _readhandler(tokenizer);         boost::asio::async_read_until(*_socket, _buffer, "\r\n",             boost::bind(&irc::_read, this,                 boost::asio::placeholders::error             )         );      } }  inline void irc::_send(std::string &message) {     boost::asio::write(*_socket, boost::asio::buffer(message + "\r\n")); }  void irc::_readhandler(const boost::tokenizer<boost::char_separator<char> > &tokenizer) {     for(auto : _readhandlers)         it(tokenizer); }  void irc::_connecthandler(const boost::system::error_code &error) {     if(!error)     {         _onconnect();     } } 

  • connect never called.

this causes "bad file handle" error

further notes

  • suddenly, _send uses synchronous asio::write. why?
  • error handling should added there, (catch or pass error_code& argument).
  • there's 1 socket never gets re-initialized or assigned. embedding shared pointer isn't changing anything¹.

  • this strange:

        std::cout << "connecting " << _server << " " << _port << std::endl;      boost::asio::async_connect(*_socket, it,         boost::bind(&irc::_connecthandler, this, error)     ); 

    this potentially many asynchronous connect operations on same socket simultaneously. data race , therefore undefined behaviour, see: documentation.

    so need fix use several sockets or sequential. turns out, simple: you're using free function version of async_connect:

    this function attempts connect socket 1 of sequence of endpoints. it repeated calls socket's async_connect member function, once each endpoint in sequence, until connection established.

    so fix call once.

  • your bind doesn't use placeholder, instead uses hardcoded error!

    boost::system::error_code error = boost::asio::error::host_not_found; boost::asio::async_connect(_socket, it, boost::bind(&irc::_connecthandler, this, error)); 

    needs more like

    boost::asio::async_connect(_socket, it, boost::bind(&irc::_connecthandler, this, boost::asio::placeholders::error())); 
  • after handling incoming traffic, need consume buffer contents, or infinitely repeat same:

    _readhandler(tokenizer);  _buffer.consume(_buffer.size()); 

pull request

adds:

869c225 use shared_ptr 9042c6d add function level trace 50dee1b revert shared_ptr , rename _onconnect(ed) 20475b9 fixing async_connect debacle c6d8a2e fixed channel handling , consistency join/part 6fd9242 initiate `connect()` instead of read `run()` 06a6c06 **not** assume contiguous buffer storage (ub) 090fe8c consume handled input 68e5e8a comment 

all above changed, have connected irc channel , receiving mesages.

enter image description here


¹ (especially not unless make sure hangs on instance of shared_ptr)


Comments

Popular posts from this blog

authentication - Mongodb revoke acccess to connect test database -

r - Update two sets of radiobuttons reactively - shiny -

ios - Realm over CoreData should I use NSFetchedResultController or a Dictionary? -