连接到 Boost.Asio 创建的域套接字时权限被拒绝
Permission refused when connecting to domain socket created by Boost.Asio
我正在尝试创建一个通过域套接字接收连接的服务器。我可以启动服务器,并且可以看到正在文件系统上创建的套接字。但是每当我尝试通过 socat 连接到它时,我都会收到以下错误:
2015/03/02 14:00:10 socat[62720] E connect(3, LEN=19 AF=1 "/var/tmp/rpc.sock", 19): Connection refused
这是我的 Asio 代码(只有 .cpp 文件)。尽管标题为 post,但我使用的是 Asio 的 Boost-free 版本,但我认为这不会成为问题。
namespace myapp {
DomainListener::DomainListener(const string& addr) : socket{this->service}, Listener{addr} {
remove(this->address.c_str());
stream_protocol::endpoint ep(this->address);
stream_protocol::acceptor acceptor(this->service, ep);
acceptor.async_accept(this->socket, ep, bind(&DomainListener::accept_callback, this, _1));
}
DomainListener::~DomainListener() {
this->service.stop();
remove(this->address.c_str());
}
void DomainListener::accept_callback(const error_code& ec) noexcept {
this->socket.async_read_some(asio::buffer(this->data), bind(&DomainListener::read_data, this, _1, _2));
}
void DomainListener::read_data(const error_code& ec, size_t length) noexcept {
//std::cerr << "AAA" << std::endl;
//std::cerr << this->data[0] << std::endl;
//std::cerr << "BBB" << std::endl;
}
}
Listener::Listener(const string& addr) : work{asio::io_service::work(this->service)} {
this->address = addr;
}
void Listener::listen() {
this->service.run();
}
Listener::~Listener() {
}
在使用这些 类 的代码中,每当我想开始监听连接的套接字时,我都会调用 listen()
。
我已经设法让它与 libuv 一起工作并更改为 Asio,因为我认为它会使代码更具可读性,但我发现文档非常含糊。
问题很可能是 acceptor
的生命周期。
acceptor
是DomainListener
构造函数中的一个自动变量。当 DomainListener
构造函数完成时,acceptor
被销毁,导致接受器关闭并取消未完成的操作,例如 async_accept
操作。取消的操作将提供错误代码 asio::error::operation_aborted
,并计划在 io_service
内延迟调用。因此,在尝试连接到域套接字时可能没有活动的侦听器。有关 IO 对象销毁影响的更多详细信息,请参阅 this 答案。
DomainListener::DomainListener(const string&) : /* ... */
{
// ...
stream_protocol::acceptor acceptor(...);
acceptor.async_accept(..., bind(accept_callback, ...));
} // acceptor destroyed, and accept_callback likely cancelled
要解决此问题,请考虑通过使 acceptor
成为 DomainListener
的数据成员来延长其生命周期。此外,检查提供给异步操作的 error_code
可以更深入地了解异步调用链。
这是一个完整的最小示例 demonstrating 使用 Asio 的域套接字。
#include <cstdio>
#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
/// @brief server demonstrates using domain sockets to accept
/// and read from a connection.
class server
{
public:
server(
boost::asio::io_service& io_service,
const std::string& file)
: io_service_(io_service),
acceptor_(io_service_,
boost::asio::local::stream_protocol::endpoint(file)),
client_(io_service_)
{
std::cout << "start accepting connection" << std::endl;
acceptor_.async_accept(client_,
boost::bind(&server::handle_accept, this,
boost::asio::placeholders::error));
}
private:
void handle_accept(const boost::system::error_code& error)
{
std::cout << "handle_accept: " << error.message() << std::endl;
if (error) return;
std::cout << "start reading" << std::endl;
client_.async_read_some(boost::asio::buffer(buffer_),
boost::bind(&server::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_read(
const boost::system::error_code& error,
std::size_t bytes_transferred)
{
std::cout << "handle_read: " << error.message() << std::endl;
if (error) return;
std::cout << "read: ";
std::cout.write(buffer_.begin(), bytes_transferred);
std::cout.flush();
}
private:
boost::asio::io_service& io_service_;
boost::asio::local::stream_protocol::acceptor acceptor_;
boost::asio::local::stream_protocol::socket client_;
std::array<char, 1024> buffer_;
};
int main(int argc, char* argv[])
{
if (argc != 2)
{
std::cerr << "Usage: <file>\n";
return 1;
}
// Remove file on startup and exit.
std::string file(argv[1]);
struct file_remover
{
file_remover(std::string file): file_(file) { std::remove(file.c_str()); }
~file_remover() { std::remove(file_.c_str()); }
std::string file_;
} remover(file);
// Create and run the server.
boost::asio::io_service io_service;
server s(io_service, file);
io_service.run();
}
Coliru 没有安装 socat,所以下面的命令使用 OpenBSD netcat 将 "asio domain socket example" 写入域套接字:
export SOCKFILE=$PWD/example.sock
./a.out $SOCKFILE &
sleep 1
echo "asio domain socket example" | nc -U $SOCKFILE
输出:
start accepting connection
handle_accept: Success
start reading
handle_read: Success
read: asio domain socket example
我正在尝试创建一个通过域套接字接收连接的服务器。我可以启动服务器,并且可以看到正在文件系统上创建的套接字。但是每当我尝试通过 socat 连接到它时,我都会收到以下错误:
2015/03/02 14:00:10 socat[62720] E connect(3, LEN=19 AF=1 "/var/tmp/rpc.sock", 19): Connection refused
这是我的 Asio 代码(只有 .cpp 文件)。尽管标题为 post,但我使用的是 Asio 的 Boost-free 版本,但我认为这不会成为问题。
namespace myapp {
DomainListener::DomainListener(const string& addr) : socket{this->service}, Listener{addr} {
remove(this->address.c_str());
stream_protocol::endpoint ep(this->address);
stream_protocol::acceptor acceptor(this->service, ep);
acceptor.async_accept(this->socket, ep, bind(&DomainListener::accept_callback, this, _1));
}
DomainListener::~DomainListener() {
this->service.stop();
remove(this->address.c_str());
}
void DomainListener::accept_callback(const error_code& ec) noexcept {
this->socket.async_read_some(asio::buffer(this->data), bind(&DomainListener::read_data, this, _1, _2));
}
void DomainListener::read_data(const error_code& ec, size_t length) noexcept {
//std::cerr << "AAA" << std::endl;
//std::cerr << this->data[0] << std::endl;
//std::cerr << "BBB" << std::endl;
}
}
Listener::Listener(const string& addr) : work{asio::io_service::work(this->service)} {
this->address = addr;
}
void Listener::listen() {
this->service.run();
}
Listener::~Listener() {
}
在使用这些 类 的代码中,每当我想开始监听连接的套接字时,我都会调用 listen()
。
我已经设法让它与 libuv 一起工作并更改为 Asio,因为我认为它会使代码更具可读性,但我发现文档非常含糊。
问题很可能是 acceptor
的生命周期。
acceptor
是DomainListener
构造函数中的一个自动变量。当 DomainListener
构造函数完成时,acceptor
被销毁,导致接受器关闭并取消未完成的操作,例如 async_accept
操作。取消的操作将提供错误代码 asio::error::operation_aborted
,并计划在 io_service
内延迟调用。因此,在尝试连接到域套接字时可能没有活动的侦听器。有关 IO 对象销毁影响的更多详细信息,请参阅 this 答案。
DomainListener::DomainListener(const string&) : /* ... */
{
// ...
stream_protocol::acceptor acceptor(...);
acceptor.async_accept(..., bind(accept_callback, ...));
} // acceptor destroyed, and accept_callback likely cancelled
要解决此问题,请考虑通过使 acceptor
成为 DomainListener
的数据成员来延长其生命周期。此外,检查提供给异步操作的 error_code
可以更深入地了解异步调用链。
这是一个完整的最小示例 demonstrating 使用 Asio 的域套接字。
#include <cstdio>
#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
/// @brief server demonstrates using domain sockets to accept
/// and read from a connection.
class server
{
public:
server(
boost::asio::io_service& io_service,
const std::string& file)
: io_service_(io_service),
acceptor_(io_service_,
boost::asio::local::stream_protocol::endpoint(file)),
client_(io_service_)
{
std::cout << "start accepting connection" << std::endl;
acceptor_.async_accept(client_,
boost::bind(&server::handle_accept, this,
boost::asio::placeholders::error));
}
private:
void handle_accept(const boost::system::error_code& error)
{
std::cout << "handle_accept: " << error.message() << std::endl;
if (error) return;
std::cout << "start reading" << std::endl;
client_.async_read_some(boost::asio::buffer(buffer_),
boost::bind(&server::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_read(
const boost::system::error_code& error,
std::size_t bytes_transferred)
{
std::cout << "handle_read: " << error.message() << std::endl;
if (error) return;
std::cout << "read: ";
std::cout.write(buffer_.begin(), bytes_transferred);
std::cout.flush();
}
private:
boost::asio::io_service& io_service_;
boost::asio::local::stream_protocol::acceptor acceptor_;
boost::asio::local::stream_protocol::socket client_;
std::array<char, 1024> buffer_;
};
int main(int argc, char* argv[])
{
if (argc != 2)
{
std::cerr << "Usage: <file>\n";
return 1;
}
// Remove file on startup and exit.
std::string file(argv[1]);
struct file_remover
{
file_remover(std::string file): file_(file) { std::remove(file.c_str()); }
~file_remover() { std::remove(file_.c_str()); }
std::string file_;
} remover(file);
// Create and run the server.
boost::asio::io_service io_service;
server s(io_service, file);
io_service.run();
}
Coliru 没有安装 socat,所以下面的命令使用 OpenBSD netcat 将 "asio domain socket example" 写入域套接字:
export SOCKFILE=$PWD/example.sock
./a.out $SOCKFILE &
sleep 1
echo "asio domain socket example" | nc -U $SOCKFILE
输出:
start accepting connection
handle_accept: Success
start reading
handle_read: Success
read: asio domain socket example