Solaris でboost を使用して 65536 バイトを超えるサイズのメッセージを送信すると、1 つの奇妙な問題が発生しますasync_write
(ただし、この問題は Linux では発生しません)。スタジオ 12 CC コンパイラ (Solaris 上) および g++ 4.4.3 (Linux 上) でブースト バージョン 1.47 を使用しています。
サイズが 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スレッドが受信者から継続的に読み取ろうとしている理由を誰かが明らかにすることができますか? 誰かがこの問題に遭遇した場合、または解決策を提案する場合はお知らせください。
問題を再現するコードは次のとおりです (ブースト非同期 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;
}
サーバーとクライアントを2つの異なるsolarisホストで実行した方法は次のとおりです-
server 9081 200000
client <server_host> 9081
サーバーとクライアントを実行した後、上記のようにタイトなポーリング ループを示すサーバーで「トラス」を実行します。
PS: boost::asio::async_write をチェックして、65536 バイトを超えるデータを書き込みましたが、async_write が完了する前にメッセージが破棄されないようにしているので、これは関係ありません。