0

ブースト asio の提供を検討しています

クライアントは、続くバイトの長さを示す 1 バイトのヘッダーを送信します。

関連するサーバー コード:

  enum {max_length=1};     

  void handle_read(const boost::system::error_code & error, const size_t & bytes_transferred){
if (! error){
  ++ctr;
  std::string inc_data_str(this->inc_data.begin(),this->inc_data.end());
  std::cout<<"received string: "<<inc_data_str<<" with size "<<inc_data_str.size()
           <<" bytes_transferred: "<<bytes_transferred<<" ctr: "<<ctr<<std::endl;
  int size_inc_next = boost::lexical_cast<int>(inc_data_str);
  int offset = 0;
  //std::cout<<"incoming integer of size "<<size_inc_next<<" processed from string: "<<inc_data_str<<std::endl;                    
  std::vector<char> next_inc_data(size_inc_next+offset);
  boost::asio::read(this->socket,boost::asio::buffer(next_inc_data),boost::asio::transfer_exactly(size_inc_next+offset));
  std::string int_recvd(next_inc_data.begin(),next_inc_data.begin()+size_inc_next);
  //std::cout<<boost::posix_time::microsec_clock::local_time()<<std::endl;                                                         
  //std::cout<<"received integer: "<<int_recvd<<" from string "<<int_recvd<<" of size "<<int_recvd.size()<<std::endl;              
  this->process_connection();
 } // ! error
} // handle_read

void process_connection(){
                                                   boost::asio::async_read(this->socket,boost::asio::buffer(this->inc_data),boost::asio::transfer_exactly(max_length),
                    boost::bind(&Connection::handle_read,shared_from_this(),boost::asio::placeholders::error,
                                    boost::asio::placeholders::bytes_transferred));
}

関連するクライアント コード:

  void on_write(const boost::system::error_code & error_code){
if (! error_code){
  std::string transfer_data("15");
  std::vector<char> v_td(transfer_data.begin(),transfer_data.end());
  ++ctr;
  for (std::vector<char>::iterator iter = v_td.begin(); iter != v_td.end(); ++iter) std::cout<<*iter;
  std::cout<<" ctr: "<<ctr;
  std::endl(std::cout);
        boost::asio::async_write(this->socket,boost::asio::buffer(v_td),boost::asio::transfer_exactly(2),
                           boost::bind(&Client::on_write,shared_from_this(),
                                       boost::asio::placeholders::error));
  }
 }

サーバー プロセスの予期される印刷出力の例:

  received string: 1 with size 1 bytes_transferred: 1 ctr: 159685

クライアント プロセスの期待される印刷出力の例:

  15 ctr: 356293

このような予想される出力はしばらくの間生成されますが、たとえば 356293 回のクライアント反復 (この ctr 番号は、プロセスの試行が繰り返されるため肉眼では非決定論的です) の後、サーバーは次のエラーで中断します。

 received string:  with size 1 bytes_transferred: 1 ctr: 159686
 terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::bad_lexical_cast> >'
 what():  bad lexical cast: source type value could not be interpreted as target

中止 (コアダンプ)

受信した文字列は「空白」であることに注意してください。場合によっては、代替メッセージで中断することもあります:

 received string: X with size 1 bytes_transferred: 1 ctr: 159686

ここで何が起こっているのか、なぜ、どのように整理すればよいのでしょうか?

strace の後にさらに編集:

クライアント トレース:

 sendmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"15", 2}], msg_controllen=0, msg_flags=0}, MSG_NOSIGNAL) = 2
 epoll_wait(4, {}, 128, 0)               = 0
 write(1, "15 ctr: 204441\n", 1515 ctr: 204441)        = 15
 sendmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"15", 2}], msg_controllen=0, msg_flags=0},    MSG_NOSIGNAL) = 2
 epoll_wait(4, {}, 128, 0)               = 0
 write(1, "15 ctr: 204442\n", 1515 ctr: 204442)        = 15
 sendmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"15", 2}], msg_controllen=0, msg_flags=0}, MSG_NOSIGNAL) = -1 EAGAIN (Resource temporarily \
 unavailable)
 epoll_wait(4, {{EPOLLOUT, {u32=167539936, u64=167539936}}}, 128, -1) = 1
 sendmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"\0\0", 2}], msg_controllen=0,   msg_flags=0}, MSG_NOSIGNAL) = 2
 write(1, "15 ctr: 204443\n", 1515 ctr: 204443)        = 15
 sendmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"15", 2}], msg_controllen=0, msg_flags=0},     MSG_NOSIGNAL) = 2
 epoll_wait(4, {}, 128, 0)               = 0
 write(1, "15 ctr: 204444\n", 1515 ctr: 204444)        = 15
 sendmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"15", 2}], msg_controllen=0, msg_flags=0}, MSG_NOSIGNAL) = 2
 epoll_wait(4, {}, 128, 0)               = 0
 write(1, "15 ctr: 204445\n", 1515 ctr: 204445)        = 15

サーバー トレース:

 write(1, "received string: 1 with size 1 b"..., 64received string: 1 with size 1   bytes_transferred: 1 ctr: 204441) = 64
 write(1, "incoming integer of size 1 proce"..., 52incoming integer of size 1 processed from string: 1) = 52
 recvmsg(7, {msg_name(0)=NULL, msg_iov(1)=[{"5", 1}], msg_controllen=0, msg_flags=0},0) = 1
 write(1, "received integer: 5 from string "..., 44received integer: 5 from string 5 of size 1) = 44
 recvmsg(7, {msg_name(0)=NULL, msg_iov(1)=[{"1", 1}], msg_controllen=0, msg_flags=0},0) = 1
 epoll_wait(4, {}, 128, 0)               = 0
 write(1, "received string: 1 with size 1 b"..., 64received string: 1 with size 1 bytes_transferred: 1 ctr: 204442) = 64
 write(1, "incoming integer of size 1 proce"..., 52incoming integer of size 1 processed from string: 1) = 52
 recvmsg(7, {msg_name(0)=NULL, msg_iov(1)=[{"5", 1}], msg_controllen=0, msg_flags=0}, 0) = 1
 write(1, "received integer: 5 from string "..., 44received integer: 5 from string 5 of size 1) = 44
 recvmsg(7, {msg_name(0)=NULL, msg_iov(1)=[{"\0", 1}], msg_controllen=0, msg_flags=0}, 0) = 1
 epoll_wait(4, {}, 128, 0)               = 0
 write(1, "received string: \0 with size 1 b"..., 64received string: ^@ with size 1 bytes_transferred: 1 ctr: 204443) = 64
 futex(0xb76640fc, FUTEX_WAKE_PRIVATE, 2147483647) = 0
 write(1, "inc_data_str\n", 13inc_data_str)          = 13

クライアント プロセスでは、誤った "\0\0" 送信前の epoll_wait が他の epoll_wait 呼び出しとは異なります (u32=...., u64=....) ... それが何を意味するのかわかりませんけれど

不可解な部分を要約すると、strace はヌルが転送されていることを示していますが、次の行の strace は、標準出力への書き込みシステム呼び出しをリテラル「15」で示しています。これは、それが transfer_data ベクトルにあったものであることを意味します。

再編集:

最後に、

  boost::this_thread::sleep(boost::posix_time::microseconds(200));

クライアントの on_write 関数の write ステートメントの直前。

これにより、問題は発生しませんでした。では、これは asio オブジェクトでどのような同時実行性の問題になるのでしょうか? ソケットですか?

4

2 に答える 2

4

バッファの有効期間が原因で、クライアントが壊れています

void 
on_write(
    const boost::system::error_code& error_code
)
{
    if ( !error_code ) {
        std::string transfer_data("15");
        std::vector<char> v_td(transfer_data.begin(), transfer_data.end());
        //                 ^
        //                  \------ goes out of scope before async_write() returns

        boost::asio::async_write(
            this->socket,
            boost::asio::buffer(v_td),
            boost::asio::transfer_exactly(2),
            boost::bind( 
                &Client::on_write,
                shared_from_this(),
                boost::asio::placeholders::error
            )
        );
     }
 }

async_write()完了ハンドラーが呼び出されるまで、に指定されたバッファーが有効なままであることを確認する必要があります。

buffers書き込まれるデータを含む 1 つ以上のバッファー。buffers オブジェクトは必要に応じてコピーできますが、基になるメモリ ブロックの所有権は呼び出し元によって保持され、ハンドラーが呼び出されるまでそれらが有効であることを保証する必要があります。ハンドラ


于 2013-04-26T16:43:29.453 に答える