提升asio异步操作坏文件描述符

时间:2016-01-27 19:48:16

标签: c++ boost boost-asio

对于IRC机器人我是usig boost asio,而我的一个异步操作会导致错误的文件描述符。我试图将套接字放在shared_ptr中,但我仍然得到了#34; Bad File Descriptor"错误。我不知道它有什么不对。

这是文件,我省略了cpp文件中的一些功能。但我想在我的Github上阅读完整的文件,它是here

错误发生在_read函数中。

谢谢!

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();
    }
}

1 个答案:

答案 0 :(得分:3)

  • connect永远不会被召唤。
  

这会导致“错误的文件句柄”错误

进一步说明

  • 突然,_send使用同步 asio::write。为什么?
  • 也应该在那里添加错误处理(捕获或传递error_code&参数)。
  • 只有一个套接字永远不会被重新初始化或分配。将它嵌入共享指针不会改变任何东西¹。

  • 然而这很奇怪:

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

    这可能同时在同一个套接字上进行许多异步连接操作。这是一场数据竞赛,因此Undefined Behaviour,请参阅:documentation

    因此您需要修复它以使用多个套接字或顺序。 事实证明,这很简单:您已经使用async_connect的免费功能版本:

      

    此函数尝试将套接字连接到一系列端点之一。 通过重复调用套接字async_connect成员函数,对序列中的每个端点执行一次,直到成功建立连接为止。

    所以解决方法是只调用一次。

  • 您的绑定不使用占位符,而是使用硬编码错误!

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

    需要更像

    boost::asio::async_connect(_socket, it, boost::bind(&Irc::_connectHandler, this, boost::asio::placeholders::error()));
    
  • 处理传入流量后,
  • 需要使用缓冲区内容,否则会无限重复:

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

拉取请求

添加:

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

以上所有内容都已更改,我已成功连接到IRC频道并接收消息。

enter image description here

¹(特别是除非你确定某些东西挂在了shared_ptr的实例上)