2015-03-02 79 views
0

我试图创建一个通过域套接字接收连接的服务器。我可以启动服务器,并可以看到在文件系统上创建的套接字。但每当我试着通过socat连接到它,我得到以下错误:当连接到由Boost.Asio创建的域套接字时,拒绝权限

2015/03/02 14:00:10 socat[62720] E connect(3, LEN=19 AF=1 "/var/tmp/rpc.sock", 19): Connection refused

这是我的短耳代码(只有.cpp文件)。尽管帖子题目中我使用了Asio的免Boost版本,但我不认为这会是一个问题。

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,因为我认为这将使更多的可读代码,但我发现文档非常模糊。

+0

检查套接字文件的权限,特别是检查'socat'用户是否可以读写它。 – 2015-03-02 15:36:20

+0

所有进程都在我的用户名下。 – ruipacheco 2015-03-02 15:50:04

回答

2

问题很可能是acceptor的使用期限。

acceptorDomainListener构造函数中的自动变量。当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 

要解决这个问题,考虑使其成为DomainListener数据部件延伸出的acceptor的寿命。此外,检查提供给异步操作的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域套接字例子” 到域套接字:

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 
+0

耶稣,我可以吻你。我认真考虑回到libuv的回调地狱。 – ruipacheco 2015-03-03 20:31:37

+0

什么是'std :: remove(const char *)',为什么不需要包含任何内容? – Florian 2015-08-29 21:18:38

+0

@Florian ['std :: remove'](http://en.cppreference.com/w/cpp/io/c/remove)用于删除文件,并且来自于包含的'cstdio'头文件。算法头中的重载提供了一个非常不同的功能。 – 2015-08-29 22:41:53

相关问题