TCPClient boost :: asio :: io_service帖子没有触发

时间:2014-10-10 03:33:10

标签: c++ boost-asio

我遇到了boost :: asio :: io_service.post()没有调用我的方法处理程序的问题。 我有一个简单的客户端和服务器c ++应用程序都在TCPClient类中使用相同的代码。客户端工作正常,但使用accept填充的类的实例不起作用。

我已经完成了整个项目here,但我已将相关的代码放在下面。

在TCPClient :: Write方法中这一行

io_service.post(boost::bind(&TCPClient::DoWrite, this, msg));

被调用,但处理程序(TCPCLient :: DoWrite)在服务器端没有被调用。

我知道IO_Service正在运行,因为同一TCPClient中的async_reads工作正常。

这是我的TCPClient类

.hpp文件

class TCPClient
  : public boost::enable_shared_from_this<TCPClient>
{
    public:
        typedef boost::shared_ptr<TCPClient> pointer;

    private:
        boost::asio::io_service io_service;

        bool m_IsConnected;
        bool m_HeartbeatEnabled;

        boost::asio::ip::tcp::socket m_Socket;
        boost::asio::ip::tcp::endpoint m_Endpoint;

        boost::asio::steady_timer m_HeartBeatTimer;
        boost::asio::streambuf m_Buffer;
        std::string m_Delimiter;
        std::deque<std::string> m_Messages;
        bool m_HeartBeatEnabled;
        int m_HeartBeatTime;

    private:
        void HandleConnect(const boost::system::error_code& error);
        void DoHeartBeat(const boost::system::error_code& error);
        void DoWrite(const std::string &msg);
        void HandleWrite(const boost::system::error_code& error);
        void HandleRead(const boost::system::error_code& error);

    public:
        TCPClient(boost::asio::io_service &io_service);
        TCPClient(bool enableHeartbeat);
        ~TCPClient();
        void Close();
        void ConnectToServer(boost::asio::ip::tcp::endpoint& endpoint);
        void ConnectToServer(const std::string &ip, const std::string &protocol);
        void ConnectToServer(const std::string &ip, unsigned short port);
        void Write(const std::string &msg);
        void StartRead();
        void SetHeartBeatTime(int time);  
        boost::asio::ip::tcp::socket& Socket();
        boost::asio::io_service& Service();
        static pointer Create(boost::asio::io_service& io_service);

    public:
        // signals
        boost::signals2::signal<void(const boost::asio::ip::tcp::endpoint&)>    sConnected;
        boost::signals2::signal<void(const boost::asio::ip::tcp::endpoint&)>    sDisconnected;      
        boost::signals2::signal<void(const std::string&)>                       sMessage;
};

.cpp文件

using boost::asio::ip::tcp;

TCPClient::pointer TCPClient::Create(boost::asio::io_service& io)
{
    return pointer(new TCPClient(io));
}

TCPClient::TCPClient(boost::asio::io_service& io)
    : m_IsConnected(true), m_Socket(io), m_HeartBeatTimer(io), m_Delimiter(), m_HeartBeatTime(10)
{
    m_Delimiter = "\n"; 
    m_HeartbeatEnabled = false;
    // start heartbeat timer (optional)
    if(m_HeartBeatEnabled)
    {
        m_HeartBeatTimer.expires_from_now(boost::chrono::seconds(m_HeartBeatTime));
        m_HeartBeatTimer.async_wait(boost::bind(&TCPClient::DoHeartBeat, this, boost::asio::placeholders::error));
    }
}

TCPClient::TCPClient(bool enableHeartBeat)
    : m_IsConnected(false), m_Socket(io_service), m_HeartBeatTimer(io_service), m_Delimiter(), m_HeartBeatTime(10)
{
    m_Delimiter = "\n"; 
    m_HeartbeatEnabled = enableHeartBeat;
}

TCPClient::TCPClient::~TCPClient()
{
}

void TCPClient::Close()
{
    io_service.stop();
    m_Socket.close();
}

boost::asio::ip::tcp::socket& TCPClient::Socket()
{
    return m_Socket;
}

boost::asio::io_service& TCPClient::Service()
{
    return io_service;
}

void TCPClient::ConnectToServer(const std::string &ip, unsigned short port)
{
    try {
        boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(ip), port);
        ConnectToServer(endpoint);
    }
    catch(const std::exception &e) {
        std::cout << "Error: " << e.what() << std::endl;
    }
}

void TCPClient::ConnectToServer(const std::string &url, const std::string &protocol)
{
    //  You can also explicitly pass a port, like "8080"
    boost::asio::ip::tcp::resolver::query query( url, protocol );
    boost::asio::ip::tcp::resolver resolver( io_service );
    try {
        boost::asio::ip::tcp::resolver::iterator destination = resolver.resolve(query);
        boost::asio::ip::tcp::endpoint endpoint;
        while ( destination != boost::asio::ip::tcp::resolver::iterator() ) 
            endpoint = *destination++;

        ConnectToServer(endpoint);
    }
    catch(const std::exception &e) {
        std::cout << "Error: " << e.what() << std::endl;
    }
}

void TCPClient::ConnectToServer(boost::asio::ip::tcp::endpoint& endpoint)
{
    m_Endpoint = endpoint;

    std::cout << "Trying to connect to port " << endpoint << std::endl;

    // try to connect, then call handle_connect
    m_Socket.async_connect(m_Endpoint,
        boost::bind(&TCPClient::HandleConnect, this, boost::asio::placeholders::error));

    //start processing messages
    io_service.run();
}

void TCPClient::Write(const std::string &msg)
{
    if(!m_IsConnected) return;
    std::cout << "write: " << msg << std::endl;
    // safe way to request the client to write a message
    io_service.post(boost::bind(&TCPClient::DoWrite, this, msg));
}

void TCPClient::StartRead()
{
    if(!m_IsConnected) return;

    // wait for a message to arrive, then call handle_read
    boost::asio::async_read_until(m_Socket, m_Buffer, m_Delimiter,
          boost::bind(&TCPClient::HandleRead, this, boost::asio::placeholders::error));
}


void TCPClient::HandleRead(const boost::system::error_code& error)
{
    if (!error)
    {
        std::string msg;
        std::istream is(&m_Buffer);
        std::getline(is, msg); 

        if(msg.empty()) return;

        //cout << "Server message:" << msg << std::endl;

        // TODO: you could do some message processing here, like breaking it up
        //       into smaller parts, rejecting unknown messages or handling the message protocol

        // create signal to notify listeners
        sMessage(msg);

        // restart heartbeat timer (optional)
        if(m_HeartBeatEnabled)
        {
            m_HeartBeatTimer.expires_from_now(boost::chrono::seconds(m_HeartBeatTime));
            m_HeartBeatTimer.async_wait(boost::bind(&TCPClient::DoHeartBeat, this, boost::asio::placeholders::error));
        }

        // wait for the next message
        StartRead();
    }
    else
    {
        // try to reconnect if external host disconnects
        if(error.value() != 0) {
            m_IsConnected = false;

            // let listeners know
            sDisconnected(m_Endpoint);

            // cancel timers
            m_HeartBeatTimer.cancel();
        }
        //else
            //do_close();
    }
}

void TCPClient::HandleWrite(const boost::system::error_code& error)
{
    if(!error)
    {
        // write next message
        m_Messages.pop_front();
        if (!m_Messages.empty())
        {
            std::cout << "Client message:" << m_Messages.front() << std::endl;

            boost::asio::async_write(m_Socket,
                boost::asio::buffer(m_Messages.front()),
                boost::bind(&TCPClient::HandleWrite, this, boost::asio::placeholders::error));
        }
        if(m_HeartBeatEnabled)
        {
            // restart heartbeat timer (optional)
            m_HeartBeatTimer.expires_from_now(boost::chrono::seconds(m_HeartBeatTime));
            m_HeartBeatTimer.async_wait(boost::bind(&TCPClient::DoHeartBeat, this, boost::asio::placeholders::error));
        }
    }
    else
    {
        std::cout << "HandleWrite Error: " << error << std::endl;
    }
}

void TCPClient::DoWrite(const std::string &msg)
{
    if(!m_IsConnected) return;

    bool write_in_progress = !m_Messages.empty();
    m_Messages.push_back(msg + m_Delimiter);

    if (!write_in_progress)
    {
        std::cout << "Client message2: " << m_Messages.front() << std::endl;

        boost::asio::async_write(m_Socket,
            boost::asio::buffer(m_Messages.front()),
            boost::bind(&TCPClient::HandleWrite, this, boost::asio::placeholders::error));
    }
    else
    {
        std::cout << "DoWrite write_in_progress: " << msg << std::endl;
    }
}

void TCPClient::HandleConnect(const boost::system::error_code& error) 
{   
    if (!error) {
        // we are connected!
        m_IsConnected = true;

        // let listeners know
        sConnected(m_Endpoint);

        // start heartbeat timer (optional)
        if(m_HeartBeatEnabled)
        {
            m_HeartBeatTimer.expires_from_now(boost::chrono::seconds(m_HeartBeatTime));
            m_HeartBeatTimer.async_wait(boost::bind(&TCPClient::DoHeartBeat, this, boost::asio::placeholders::error));
        }

        // await the first message
        StartRead();
    }
    else {
        // there was an error :(
        m_IsConnected = false;

        std::cout << "Server error:" << error.message() << std::endl;
    }
}

void TCPClient::DoHeartBeat(const boost::system::error_code& error)
{
    // here you can regularly send a message to the server to keep the connection alive,
    // I usualy send a PING and then the server replies with a PONG
    if(!error) Write( "PING" );
}

void TCPClient::SetHeartBeatTime(int time)
{
    m_HeartBeatTime = time;
    m_HeartBeatEnabled = true;
    m_HeartBeatTimer.expires_from_now(boost::chrono::seconds(m_HeartBeatTime));
    m_HeartBeatTimer.async_wait(boost::bind(&TCPClient::DoHeartBeat, this, boost::asio::placeholders::error));
}

我使用TCPServer接受连接

.hpp文件

class TCPServer
{
    private:
        boost::asio::io_service io_service;
        boost::asio::ip::tcp::acceptor m_acceptor;

    public:
        TCPServer(int port);
        ~TCPServer();
        void Close();
        void StartAccept();

    private:
        void HandleAccept(TCPClient::pointer new_connection, const boost::system::error_code& error);

    public:
        boost::signals2::signal<void(const TCPClient::pointer&)> sig_NewClient;
};

.cpp文件

TCPServer::TCPServer(int port)
    : m_acceptor(io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))
{
}

TCPServer::TCPServer::~TCPServer()
{
}

void TCPServer::Close()
{
    m_acceptor.close();
    io_service.stop();
}

void TCPServer::StartAccept()
{
    TCPClient::pointer new_connection = TCPClient::Create(io_service);

    m_acceptor.async_accept(new_connection->Socket(),
        boost::bind(&TCPServer::HandleAccept, this, new_connection, boost::asio::placeholders::error));

    io_service.run();

    std::cout << "Run ended for server " << std::endl;
}

void TCPServer::HandleAccept(TCPClient::pointer new_connection, const boost::system::error_code& error)
{
    if (!error)
    {
        sig_NewClient(new_connection);
        StartAccept();
    }
}

我很擅长提升并且不做太多的c ++工作(正常的c#,java等),所以我认为我缺少一些基本的东西,但我找不到问题。

Sudo flow
服务器
创建TCPServer
server - StartAccept()
在新连接上调用生成的TCPClient实例上的StartRead
当收到ello写olle
当收到PING写PONG

客户端
连接到服务器
发送ello
每10秒发送一次PING

客户端接收并写入网络正常 服务器收到罚款,但写入从未进入DoWrite或HandleWrite方法

如需了解更多信息,请告知我们。

提前致谢

2 个答案:

答案 0 :(得分:0)

几乎没有问题,其中一些是我能看到的:

  • 由于您没有io_service::worker,当没有活跃的处理程序时,您的io_service.run()会停止。

  • 您的TCPClient::Write正在尝试post()作业以进行套接字写入,但它会将引用传递给std::string,因此当您TCPClient::DoWrite被调用时,数据已被破坏。

有一些基本的C ++和boost :: asio使用问题,所以我认为值得从更简单的实现开始。

答案 1 :(得分:0)

TCPServer接受调用链违反了io_service要求,导致在当前为同一{{1}上的事件循环提供服务的线程中调用io_service.run()时出现未定义的行为对象。 documentation州:

  

不得从当前正在调用同一io_servicerun()run()run_one()之一的主题调用poll()函数{1}}对象。

poll_one()代码中,当io_service内的线程调用的TCPServer完成处理程序调用{​​{1}}时会违反要求,然后调用HandleAccept() io_service.run() 1}}在同一StartAccept()

io_service.run()

要解决此问题,请不要在完成处理程序中调用io_service。相反,请考虑添加一个启动accept调用链的入口点并运行 .------------------------------------. V | void TCPServer::StartAccept() | { | m_acceptor.async_accept(..., &HandleAccept); --. | io_service.run(); | | } | | .---------------------------------' | V | void TCPServer::HandleAccept(...) | { | if (!error) | { | StartAccept(); ---------------------------------' } } ,但不是异步调用链循环的一部分:

io_service.run()