0

情况如下:

我有一个 Peer 类,它存储一个boost::asio::ip::tcp::socket,和一个 Network 类,它有一组 Peers(向量/地图/其他)。

目前,Peer::listen()启动异步读取,将事件的完成绑定到Peer::handle_incoming_message. 这意味着 Peer 必须寻找asio::error::eof以了解远程对等方是否已断开连接。

Peer 无法调用Network::remove_peer(),因为我希望此方法破坏 Peer 对象(实际上没有尝试过,但我猜remove_peer如果调用它的 Peer 对象已被破坏,则返回会崩溃或发生其他情况)。所以现在我所做的就是将 Peer 对象的 state 设置为PEER_STATE_DEAD,并定期检查 Network 集合中的所有 Peers 并删除所有具有 state 的 Peer PEER_STATE_DEAD

我希望 Network 有一个handle_incoming_message([...], Peer* p)方法,它实际上只会检查是否eof被引发,如果没有,调用p->receive()将从给定的缓冲区中读取async_read

因此,理想情况下,Peer 类将有一个 ListenHandler 对象,该对象将Network::handle_incoming_message([...], this)在构造时(或不久之后)初始化。问题是,我似乎无法使用此设置进行编译。

简化代码:

网络:

class Network: 
{
[...]
  void handle_incoming_message(const system::error_code& error,
                               Peer* emitter);
[...]
};

void Network::add_peer(shared_ptr<Peer> p)
{
  peers.push_back(p);
  p->start_listening(bind(&Network::handle_incoming_message, this,
                          asio::placeholders::error, p.get() ));
}

同行:

class Peer
{
public:
  typedef function<void (const system::error_code&, Peer*)> Handler;
  void start_listening(Peer::Handler _listen_handler);
  void listen();
[...]
private:
  Handler listen_handler;
  char last_message[MESSAGE_SIZE];
[...]
};

void Peer::start_listening(Peer::Handler _listen_handler)
{
  listen_handler = _listen_handler;
  set_state(PEER_STATE_CONNECTED);
  listen();
}

void Peer::listen()
{
  memset(last_message, 0, MESSAGE_SIZE);

  socket->async_read_some(asio::buffer(last_message), listen_handler);
}

void Peer::receive()
{
    cout << get_address() << ": " << last_message << endl;
    listen();
}

G++:

g++ -c -Wall -D DEBUG_ON -Iinclude -I/usr/include src/Peer.cpp
In file included from /usr/include/boost/asio.hpp:30:0,
                 from include/Peer.hpp:4,
                 from src/Peer.cpp:3:
/usr/include/boost/asio/basic_stream_socket.hpp: In instantiation of
    ‘void boost::asio::basic_stream_socket<Protocol, StreamSocketService>::async_read_some(const MutableBufferSequence&, const ReadHandler&)
    [with
      MutableBufferSequence = boost::asio::mutable_buffers_1;
      ReadHandler = boost::function<void(const boost::system::error_code&, Peer*)>;
      Protocol = boost::asio::ip::tcp;
      StreamSocketService = boost::asio::stream_socket_service<boost::asio::ip::tcp>]’:
src/Peer.cpp:30:69:   required from here
/usr/include/boost/asio/basic_stream_socket.hpp:785:57: error: invalid conversion from ‘long unsigned int’ to ‘Peer*’ [-fpermissive]
In file included from /usr/include/boost/function/detail/maybe_include.hpp:23:0,
                 from /usr/include/boost/function/detail/function_iterate.hpp:14,
                 from /usr/include/boost/preprocessor/iteration/detail/iter/forward1.hpp:57,
                 from /usr/include/boost/function.hpp:64,
                 from include/Peer.hpp:5,
                 from src/Peer.cpp:3:
/usr/include/boost/function/function_template.hpp:754:17: error:
    initializing argument 2 of ‘boost::function2<R, T1, T2>::result_type boost::function2<R, T1, T2>::operator()(T0, T1) const
    [with
      R = void;
      T0 = const boost::system::error_code&;
      T1 = Peer*;
      boost::function2<R, T1, T2>::result_type = void]’ [-fpermissive]

我有点理解它正在尝试将 ReadHandler 构造函数与(error_code,bytes_transferred)一起使用。

我读过这个问题,似乎 bind 是“足够聪明,不会通过 [stuff]”,但这是否意味着没有办法(inhales)将读取处理程序绑定到来自另一个接受其他类的非静态方法参数比error_code,bytes_transferred?

(我尝试将 Handler typedef 更改为 void(error_code, bytes_transferred, Peer*) 但它也不起作用)

或者,如果其中任何一个看起来像是糟糕的设计,我愿意接受有关如何更好地处理事情的建议。如果问题是由于没有调用 tobind()内部调用 to async_read(),我想我可以进行 Network call async_read()...

4

1 回答 1

1

简而言之,将boost::function类型更改为只接受一个参数。绑定函数只有一个未知值(用 表示boost::asio::placeholders::error),因为对等方已经绑定:

typedef boost::function<void (const boost::system::error_code&, Peer*)> Handler;

变成:

typedef boost::function<void (const boost::system::error_code&)> Handler;

初始化async_read_some操作时,使用boost::bind来适配Handler符合Read Handler要求的类型。

socket->async_read_some(asio::buffer(last_message), listen_handler);

变成:

socket->async_read_some(asio::buffer(last_message),
  boost::bind(listen_handler, boost::asio::placeholders::error));

有关更多详细信息,该错误是因为该boost::function类型不符合 Boost.Asio 的Read Handler的类型要求。读取处理程序需要:

  • 可复制构造。
  • 接受类型的左值const boost::system::error_code作为其第一个参数。
  • 接受类型的左值const std::size_t作为其第二个参数。

直接使用时没有出现的原因boost::bind是从返回的未指定函子类型boost::bind有一个operator()接受两个未指定类型的参数。如果在绑定过程中提供了占位符,则这些参数仅转发到绑定函数。否则,额外的参数会被默默地忽略/丢弃。

因此,Boost.Asio 将始终boost::system::error_code使用and调用处理程序std::size_t。在原始代码中,处理程序类型为:

boost::function<void (const boost::system::error_code&, Peer*)>

此类型不符合读取处理程序类型要求。Boost.Asio 尝试使用第二个参数 as 调用处理程序const std::size_t,导致编译器错误无法在 和 之间进行std::size_t (long unsigned int)转换Peer*

invalid conversion from ‘long unsigned int’ to ‘Peer*’

Handler通过用包装boost::bind,它可以调整Handler函子以满足读取处理程序的要求。

boost::bind(listen_handler, boost::asio::placeholders::error);

尽管 Boost.Asio 将使用 and 调用生成的仿函数boost::system::error_codestd::size_t但仿函数将仅调用Handler提供boost::system::error_code,因为std::size_t将被丢弃。该博客说明了参数的传递和丢弃。


这是一个最小的完整示例:

#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/function.hpp>

class Peer {};

class Network
{
public:
  void handle_incoming_message(const boost::system::error_code& error,
                               Peer* peer)
  {
    std::cout << error.message() << "\n"
              << "Peer is: " << peer << std::endl;
  };
};

typedef boost::function<void (const boost::system::error_code&)> Handler;


int main()
{
  Network network;
  Peer peer;
  std::cout << "Peer is: " << &peer << std::endl;

  // Create the handler, that accepts a single argument.
  Handler handler = 
      boost::bind(&Network::handle_incoming_message, &network,
                  boost::asio::placeholders::error,
                  &peer);

  // Create io_service and socket.
  boost::asio::io_service io_service;
  boost::asio::ip::tcp::socket socket(io_service);

  // Initialize operation, adapting Handler to meet Read Handler requirements.
  socket.async_read_some(boost::asio::null_buffers(),
    boost::bind(handler,
                boost::asio::placeholders::error));

  io_service.run();
}

和输出:

对等体是:0xbfb2a6d2
错误的文件描述符
对等体是:0xbfb2a6d2
于 2013-08-08T18:25:14.313 回答