Boost asio异步操作坏文件描述符
Posted
技术标签:
【中文标题】Boost asio异步操作坏文件描述符【英文标题】:Boost asio async operation bad file descriptor 【发布时间】:2016-05-04 22:57:29 【问题描述】:我正在为 IRC 机器人使用 boost asio,而我的一个异步操作导致文件描述符错误。我试图将套接字放在 shared_ptr 中,但仍然出现“错误文件描述符”错误。我不知道它有什么问题。
这是文件,我省略了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();
【问题讨论】:
看东西livecoding.tv/sehe @sehe : 很好,我会把它放在我的书签里,不幸的是我的笔记本电脑上没有 Flashplayer。 应该已经支持 html5 (lemme check)。 roadmap.livecoding.tv 表示 2016 年第 4 周 i.imgur.com/xpk55U1.png 可能是我没有打开的选项? 【参考方案1】: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());
拉取请求
https://github.com/Bl4ckb0ne/irc-boost/pull/1补充:
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频道并接收消息了。
¹(尤其是除非您确保某些东西挂在 shared_ptr 的实例上)
【讨论】:
最后,我在 PR 中的审查结果:github.com/Bl4ckb0ne/irc-boost/pull/1 - 您可以在此处查看我对这些更改进行编码的录制直播:livecoding.tv/video/reviewing-a-toy-irc-bot-in-boost-asio 嗨,我只是想知道如何运行程序。我可以通过从 github 下载源代码来构建程序,正如@sehe 提到的那样。我需要知道步骤... . 创建的可执行文件运行良好...以上是关于Boost asio异步操作坏文件描述符的主要内容,如果未能解决你的问题,请参考以下文章