在Solaris上发送超过65536字节的数据时,使用boost asyc_write()时出现问题

时间:2012-04-13 17:24:00

标签: c++ boost solaris boost-asio

在Solaris上使用boost async_write发送大小超过65536字节的消息时,我们正在观察一个奇怪的问题(虽然在Linux上没有观察到这个问题)。我们使用boost版本1.47与studio 12 CC编译器(在Solaris上)和g ++ 4.4.3(在Linux上)。

当发送大小超过65536的消息(有时我们观察到大约50K字节大小的问题但是具有65536字节的问题总是可重现的)时,消息被正确地传递给接收器。但是一旦发送消息,发送方的io服务线程就会进入忙等待循环。发件人pid上的“truss”会持续显示以下呼叫。

/6:     recvmsg(57, 0xFFFFFD7FEB9FDDF0, 0)              Err#11 EAGAIN 
/6:     write(13, " 9\0\0\019\0\0\0", 8)                = 8 
/6:     ioctl(13, DP_POLL, 0xFFFFFD7FEB9FE460)          = 1 
/6:     recvmsg(57, 0xFFFFFD7FEB9FDDF0, 0)              Err#11 EAGAIN 
/6:     write(13, " 9\0\0\019\0\0\0", 8)                = 8 
/6:     ioctl(13, DP_POLL, 0xFFFFFD7FEB9FE460)          = 1 
/6:     recvmsg(57, 0xFFFFFD7FEB9FDDF0, 0)              Err#11 EAGAIN 
/6:     write(13, " 9\0\0\019\0\0\0", 8)                = 8 
/6:     ioctl(13, DP_POLL, 0xFFFFFD7FEB9FE460)          = 1 
/6:     recvmsg(57, 0xFFFFFD7FEB9FDDF0, 0)              Err#11 EAGAIN 
/6:     write(13, " 9\0\0\019\0\0\0", 8)                = 8 
/6:     ioctl(13, DP_POLL, 0xFFFFFD7FEB9FE460)          = 1 
/6:     recvmsg(57, 0xFFFFFD7FEB9FDDF0, 0)              Err#11 EAGAIN 
/6:     write(13, " 9\0\0\019\0\0\0", 8)                = 8 
/6:     ioctl(13, DP_POLL, 0xFFFFFD7FEB9FE460)          = 1 

“lsof”表示上面的套接字描述符57是连接到接收器的套接字描述符。

这里的线程6是ioservice线程,其堆栈跟踪是 -

thread t@6     -- ???????? 
              thread_proxy 
              boost::detail::thread_data<>::run 
              des::tunnel::ServiceScheduler::processServiceWork 
              boost::asio::io_service::run 
              boost::asio::detail::task_io_service::run 
              boost::asio::detail::task_io_service::do_one 
              boost::asio::detail::dev_poll_reactor::run 

忙碌等待循环继续,直到我们停止接收器。 此外,如上所述,如果消息的大小超过65536(2 ^ 16)个字节,并且发送方和接收方在2个不同的Solaris主机上运行,​​则问题始终可以重现。如果发送方和接收方都在同一个Solaris主机上运行,​​则问题始终可以通过大小131067(2 ^ 17)重现。

有人可以说明为什么这里的ioservice线程不断尝试从接收器读取虽然它已成功发送消息并且接收器实际上没有在套接字上写任何东西。如果有人遇到此问题或建议任何解决方案,请告诉我。

以下是重现问题的代码(我刚刚更改了boost异步TCP日间服务器示例,以返回指定的字节数而不是白天,并将客户端连接置于读取模式)

#include <ctime>
#include <iostream>
#include <cstdlib>
#include <string>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/asio.hpp>
#include <boost/scoped_array.hpp>

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

std::string make_message(unsigned int message_size)
{
    using namespace std;
    std::string data(message_size, 'A');
   return data;
}

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

    static pointer create(boost::asio::io_service& io_service, unsigned int message_size)
    {
        return pointer(new tcp_connection(io_service, message_size));
    }

    tcp::socket& socket()
    {
        return socket_;
    }

    void handleMessage(const boost::system::error_code& message_error)
    {
        if (message_error) {
            std::cout<<"Error while reading the message from client"<<std::endl;
        }
        else {
           std::cout<<"Read the message from client"<<std::endl;
        }
    }

    void start()
    {
        // Perform async_read on client connection to read data from the client.
        _header.reset(new char[6]);
        _header[5] = '\0';
          boost::asio::async_read(socket_, boost::asio::buffer(_header.get(), 5),
            boost::bind(&tcp_connection::handleMessage, shared_from_this(),
                boost::asio::placeholders::error));

        message_ = make_message(message_size_);
        boost::asio::async_write(socket_, boost::asio::buffer(message_),
            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, int message_size)
        : socket_(io_service), message_size_(message_size)
    {
    }

    void handle_write(const boost::system::error_code& /*error*/,
        size_t bytes_transferred)
    {
        std::cout<<"Bytes written: "<< bytes_transferred << std::endl;
    }

    tcp::socket socket_;
    std::string message_;
    boost::scoped_array<char> _header;
    unsigned int message_size_;
};

class tcp_server
{
public:
    tcp_server(boost::asio::io_service& io_service, unsigned int port,
    unsigned int message_size)
        : acceptor_(io_service, tcp::endpoint(tcp::v4(), port)),
          message_size_(message_size)
    {
        start_accept();
    }

private:
    void start_accept()
    {
        tcp_connection::pointer new_connection =
        tcp_connection::create(acceptor_.get_io_service(), message_size_);

        acceptor_.async_accept(new_connection->socket(),
            boost::bind(&tcp_server::handle_accept, this, new_connection,
                boost::asio::placeholders::error));
    }

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

    tcp::acceptor acceptor_;
    unsigned int message_size_;
};

int main(int argc, char* argv[])
{
    if (argc != 3) {
        std::cerr << "Usage: server port message_size" << std::endl;
        return 1;
    }

    unsigned int port = boost::lexical_cast<unsigned int>(argv[1]);
    unsigned int message_size = boost::lexical_cast<unsigned int>(argv[2]);

    try {
        boost::asio::io_service io_service;
        tcp_server server(io_service, port, message_size);
        io_service.run();
    }
    catch (std::exception& e) {
        std::cerr << e.what() << std::endl;
    }

    return 0;
}

对于客户端,我们可以使用boost同步TCP白天客户端示例(更改为接受端口作为参数)。

#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>

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

int main(int argc, char* argv[])
{
    try {
        if (argc != 3) {
            std::cerr << "Usage: client <host> <port>" << std::endl;
            return 1;
        }

        boost::asio::io_service io_service;

        tcp::resolver resolver(io_service);
        tcp::resolver::query query(argv[1], argv[2]);
        tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
        tcp::resolver::iterator end;

        tcp::socket socket(io_service);
        boost::system::error_code error = boost::asio::error::host_not_found;
        while (error && endpoint_iterator != end) {
            socket.close();
            socket.connect(*endpoint_iterator++, error);
        }
        if (error)
            throw boost::system::system_error(error);

        for (;;) {
            boost::array<char, 128> buf;
            boost::system::error_code error;

            size_t len = socket.read_some(boost::asio::buffer(buf), error);

            if (error == boost::asio::error::eof)
                break; // Connection closed cleanly by peer.
            else if (error)
                throw boost::system::system_error(error); // Some other error.

            std::cout.write(buf.data(), len);
        }
    }
    catch (std::exception& e) {
        std::cerr << e.what() << std::endl;
    }

    return 0;
}

以下是我在两个不同的solaris主机上运行服务器和客户端的方法 -

server 9081 200000
client <server_host> 9081

在运行服务器和客户端之后,在服务器上执行“truss”,这将显示如上所述的紧密轮询循环。

P.S:我已经检查了boost::asio::async_write, writing data larger than 65536 bytes,但这没有关系,因为我确保在async_write完成之前消息不会被销毁。

0 个答案:

没有答案