Boost asio async operation bad file descriptor

bl4ckb0ne

I'm usig boost asio for an IRC bot, and one of my async operation results in a bad file descriptor. I tried to put the socket in a shared_ptr, but I still got the "Bad File Descriptor" error. I don't know whats wrong in it.

Here are the files, I omitted some of the functions from the cpp file. But I you want to read the full file, it's here on my Github.

The error happends in the _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 back 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 it = 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 to " << _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 to " << _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 it : _readHandlers)
        it(tokenizer);
}

void Irc::_connectHandler(const boost::system::error_code &error)
{
    if(!error)
    {
        _onConnect();
    }
}
sehe
  • connect is never called.

This causes the "bad file handle" error

Further Notes

  • Suddenly, _send uses synchronous asio::write. Why?
  • Error handling should probably be added there, too (catch or pass error_code& argument).
  • There's only one socket which never gets re-initialized or assigned. Embedding it into a shared pointer isn't changing anything¹.

  • This however is strange:

        std::cout << "Connecting to " << _server << " " << _port << std::endl;
    
        boost::asio::async_connect(*_socket, it,
            boost::bind(&Irc::_connectHandler, this, error)
        );
    

    This does potentially many asynchronous connect operations on the same socket simultaneously. This is a data race and therefore Undefined Behaviour, see: documentation.

    So you need to fix it to use several sockets or sequential. Turns out, this is very simple: you're already using the free function version of async_connect:

    This function attempts to connect a socket to one of a sequence of endpoints. It does this by repeated calls to the socket's async_connect member function, once for each endpoint in the sequence, until a connection is successfully established.

    So the fix is to just call it once.

  • Your bind doesn't use a placeholder, instead uses a 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 to be more like

    boost::asio::async_connect(_socket, it, boost::bind(&Irc::_connectHandler, this, boost::asio::placeholders::error()));
    
  • after handling incoming traffic, you need to consume the buffer contents, or it will infinitely repeat the same:

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

Pull Request

Adds:

869c225 Use shared_ptr
9042c6d Add function level trace
50dee1b Revert shared_ptr and rename _onConnect(ed)
20475b9 Fixing the async_connect debacle
c6d8a2e Fixed channel handling and consistency join/part
6fd9242 Initiate `connect()` instead of read from `run()`
06a6c06 Do **not** assume contiguous buffer storage (UB)
090fe8c Consume handled input
68e5e8a Comment

All the above changed, I have successfully connected to an IRC channel and receiving mesages.

enter image description here


¹ (especially not unless you make sure something hangs on to an instance of the shared_ptr)

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

boost asio bind: bad file descriptor

From Dev

Integrate boost::asio into file descriptor based eventloops (select/poll)

From Dev

boost asio priority queue, add async operation from handler

From Dev

Bad File descriptor with uWSGI native async websockets and redis

From Dev

Boost ASIO - What is async

From Dev

Golang bad file descriptor

From Dev

write(): Bad file descriptor

From Dev

boost::asio::async_read return end of file error on newline

From Dev

boost::asio::async_read return end of file error on newline

From Dev

boost::asio UDP "gather" operation

From Dev

Boost::Asio Async write failed

From Dev

Memory leak from boost::asio socket async_read_some operation

From Dev

Boost asio TCP async server not async?

From Dev

flock in Perl: Bad file descriptor

From Dev

Linux Socket Bad File Descriptor

From Dev

Bad file descriptor in Python 2.7

From Dev

bad file descriptor from fread

From Dev

ERROR on accept: Bad file descriptor

From Dev

AFNetworking/NSURLConnection receiving NSPOSIXErrorDomain Code=9 "The operation couldn’t be completed. Bad file descriptor"

From Dev

AFNetworking/NSURLConnection receiving NSPOSIXErrorDomain Code=9 "The operation couldn’t be completed. Bad file descriptor"

From Dev

boost.asio composed operation run in strand

From Dev

C++ boost asio Windows file handle async_read_until infinite loop - no eof

From Dev

C++ boost asio Windows file handle async_read_until infinite loop - no eof

From Dev

boost asio async udp server - poor performance

From Dev

Generic way to timeout async operations in boost::asio

From Dev

Boost.Asio: Async operations timeout

From Dev

Boost asio trouble with async_read_until

From Dev

boost asio async udp server - poor performance

From Dev

Boost.Asio: Async operations timeout

Related Related

  1. 1

    boost asio bind: bad file descriptor

  2. 2

    Integrate boost::asio into file descriptor based eventloops (select/poll)

  3. 3

    boost asio priority queue, add async operation from handler

  4. 4

    Bad File descriptor with uWSGI native async websockets and redis

  5. 5

    Boost ASIO - What is async

  6. 6

    Golang bad file descriptor

  7. 7

    write(): Bad file descriptor

  8. 8

    boost::asio::async_read return end of file error on newline

  9. 9

    boost::asio::async_read return end of file error on newline

  10. 10

    boost::asio UDP "gather" operation

  11. 11

    Boost::Asio Async write failed

  12. 12

    Memory leak from boost::asio socket async_read_some operation

  13. 13

    Boost asio TCP async server not async?

  14. 14

    flock in Perl: Bad file descriptor

  15. 15

    Linux Socket Bad File Descriptor

  16. 16

    Bad file descriptor in Python 2.7

  17. 17

    bad file descriptor from fread

  18. 18

    ERROR on accept: Bad file descriptor

  19. 19

    AFNetworking/NSURLConnection receiving NSPOSIXErrorDomain Code=9 "The operation couldn’t be completed. Bad file descriptor"

  20. 20

    AFNetworking/NSURLConnection receiving NSPOSIXErrorDomain Code=9 "The operation couldn’t be completed. Bad file descriptor"

  21. 21

    boost.asio composed operation run in strand

  22. 22

    C++ boost asio Windows file handle async_read_until infinite loop - no eof

  23. 23

    C++ boost asio Windows file handle async_read_until infinite loop - no eof

  24. 24

    boost asio async udp server - poor performance

  25. 25

    Generic way to timeout async operations in boost::asio

  26. 26

    Boost.Asio: Async operations timeout

  27. 27

    Boost asio trouble with async_read_until

  28. 28

    boost asio async udp server - poor performance

  29. 29

    Boost.Asio: Async operations timeout

HotTag

Archive