使用队列增强asio异步读取和写入套接字

时间:2017-05-07 11:26:56

标签: c++ boost boost-asio

我正在开发一个简单的TCP服务器,它可以将它的消息读写到线程安全队列中。然后,应用程序可以使用这些队列来安全地读取和写入套接字,即使是来自不同的线程。

我面临的问题是我不能async_read。我的队列具有pop操作,该操作返回要处理的下一个元素,但如果没有可用的则阻塞。所以一旦我打电话给pop,async_read回调当然不会被解雇。有没有办法可以将这样的队列集成到boost asio中,还是我必须完全重写?

以下是我为展示我遇到的问题而做的一个简短示例。建立TCP连接后,我创建一个新线程,该线程将在该tcp_connection下运行应用程序。之后我想开始async_readasync_write。我一直在打破这个问题几个小时,我真的不知道如何解决这个问题。

class tcp_connection : public std::enable_shared_from_this<tcp_connection>
{
public:
    static std::shared_ptr<tcp_connection> create(boost::asio::io_service &io_service) {
        return std::shared_ptr<tcp_connection>(new tcp_connection(io_service));
    }

    boost::asio::ip::tcp::socket& get_socket()
    {
        return this->socket;
    }

    void app_start()
    {
        while(1)
        {
            // Pop is a blocking call.
            auto inbound_message = this->inbound_messages.pop();
            std::cout << "Got message in app thread: " << inbound_message << ". Sending it back to client." << std::endl;
            this->outbound_messages.push(inbound_message);
        }
    }

    void start() {
        this->app_thread = std::thread(&tcp_connection::app_start, shared_from_this());

        boost::asio::async_read_until(this->socket, this->input_stream, "\r\n",
            strand.wrap(boost::bind(&tcp_connection::handle_read, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)));

        // Start async writing here. The message to send are in the outbound_message queue. But a Pop operation blocks
        // empty() is also available to check whether the queue is empty.
        // So how can I async write without blocking the read.
        // block...
        auto message = this->outbound_messages.pop();
        boost::asio::async_write(this->socket, boost::asio::buffer(message),
            strand.wrap(boost::bind(&tcp_connection::handle_write, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)));
    }

    void handle_read(const boost::system::error_code& e, size_t bytes_read)
    {
        std::cout << "handle_read called" << std::endl;
        if (e)
        {
            std::cout << "Error handle_read: " << e.message() << std::endl;
            return;
        }
        if (bytes_read != 0)
        {
            std::istream istream(&this->input_stream);
            std::string message;
            message.resize(bytes_read);
            istream.read(&message[0], bytes_read);
            std::cout << "Got message: " << message << std::endl;
            this->inbound_messages.push(message);
        }
        boost::asio::async_read_until(this->socket, this->input_stream, "\r\n",
            strand.wrap(boost::bind(&tcp_connection::handle_read, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)));
    }

    void handle_write(const boost::system::error_code& e, size_t /*bytes_transferred*/)
    {
        if (e)
        {
            std::cout << "Error handle_write: " << e.message() << std::endl;
            return;
        }

        // block...
        auto message = this->outbound_messages.pop();
        boost::asio::async_write(this->socket, boost::asio::buffer(message),
            strand.wrap(boost::bind(&tcp_connection::handle_write, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)));
    }



private:
    tcp_connection(boost::asio::io_service& io_service) : socket(io_service), strand(io_service)
    {
    }

    boost::asio::ip::tcp::socket socket;
    boost::asio::strand strand;
    boost::asio::streambuf input_stream;

    std::thread app_thread;

    concurrent_queue<std::string> inbound_messages;
    concurrent_queue<std::string> outbound_messages;
};

class tcp_server
{
public:
    tcp_server(boost::asio::io_service& io_service)
        : acceptor(io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 9001))
    {
        start_accept();
    }

private:
    void start_accept()
    {
        std::shared_ptr<tcp_connection> new_connection =
            tcp_connection::create(acceptor.get_io_service());

        acceptor.async_accept(new_connection->get_socket(),
            boost::bind(&tlcp_tcp_server::handle_accept, this, new_connection, boost::asio::placeholders::error));
    }

    void handle_accept(std::shared_ptr<tcp_connection> new_connection,
                       const boost::system::error_code& error)
    {
        if (!error)
        {
            new_connection->start();
        }

        start_accept();
    }

    boost::asio::ip::tcp::acceptor acceptor;
};

1 个答案:

答案 0 :(得分:2)

在我看来,好像你想要一个async_pop方法,它采用错误消息占位符和回调处理程序。当您收到消息时,请检查是否有未完成的处理程序,如果是,请弹出消息,取消注册处理程序并调用它。类似地,当注册async_pop时,如果已经有消息等待,则弹出消息并将调用发送给处理程序而不注册它。

您可能希望从类型pop_operation或类似的多态基础派生派生async_pop类。