0

async_read_some を使用して、_data という char[] に保存されているポートからデータを読み取ります。そのバッファ サイズは、すべてのリクエストに対して十分な大きさです。

void start() {

    socket_.async_read_some(boost::asio::buffer(data_,BUFFERSIZE),make_custom_alloc_handler(allocator_,boost::bind(&attentionSession::handle_read, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)));

}

void handle_read(const boost::system::error_code& error, size_t bytes_transferred) {

    string ip = socket_.remote_endpoint().address().to_string();
    log->processData(data_,ip,"memory");
    strcpy(data_,"");

}

processData は、新しく割り当てられた別の char* にコピーすることで、リクエストにいくつかの追加情報 (タイムスタンプなど) を追加します。次に、この char[] が writeToMemory(char*) に送信され、その char* が std::string memoryBuffer に追加されます。

void writeCacheToFile() {

    // This function writes buffer data to the log file

    char* temp = new char[memoryBuffer.length() + 1];
    strcpy(temp, memoryBuffer.c_str());
    writeToFile(temp);
    delete[] temp;
    memoryBuffer.clear();

}

void writeToMemory(char* data) {

    int maxSize = 1024;

     // Checks if char* data would 'fit' into the pre-defined maxSize

    if ((strlen((const char*)data) + memoryBuffer.length()) >= maxSize) {
        writeCacheToFile(); // If not the cache memoryBuffer is saved first
    }

    memoryBuffer.append((const char*) data);

    cout << memoryBuffer.length() << endl;

}

それは機能しますが、常にリクエストがある場合(リクエストで攻撃する場合)、物事が台無しになります。上記の writeToMemory() 関数でわかるように、現在の memoryBuffer の長さを出力する行を追加します。これは、std::strings のスレッド セーフと関係があると思われる場所です。

96
188
284
3639
94
190
286
2591
102
198
294
388
484
2591
96
2591
96
190
286
2591

各 (processData() によって処理される) 要求の長さは 96 文字です。ただし、ここでは、memoryBuffer の長さが上下するだけです。一部の長さは、maxSize (1024 文字) よりもさらに大きくなります。

編集:サムは、さらにコードを追加する必要があると指摘しました。これは、io_service を開始する方法です。

boost::asio::io_service ioService;
boost::scoped_ptr<boost::thread> ioServiceThread; 
server_class server (ioService,PORT); // Create server instance
ioServiceThread.reset (new boost::thread ( boost::bind ( &boost::asio::io_service::run, &ioService  ) ) ); 
// Only one threaded io_service (to receive user inputs in main() function)

これは、リクエストを完了した後の async_acceptor の関数です。

typedef boost::shared_ptr<session_class> session_ptr;

void handleAccept(session_ptr thisSession, const boost::system::error_code& error) {
    if (!error) {
      thisSession->start(); // Calls the start() function above
      thisSession.reset(new session(ioService,LOGGING_CLASS));
      acceptor.async_accept(thisSession->socket(),boost::bind(&server_class::handleAccept, this, thisSession, PLACEHOLDER_ERROR));
    }
 }

session_class は、上記の関数 start() および handle_read(x,y) を保持します。LOGGING_CLASS は、ログ ファイルを書き込むためのクラスを提供します (関数 writeCacheToFile() および writeToMemory(char*) を保持します)。log (前述) は、このクラスの一種です。

EOE: 編集の終わり

boost::threads を使用してキャッシング部分 (受信した char* を std::string に追加) を外部委託して修正しようとすると、完全に混乱した memoryBuffer になります。

それは本当にstd::stringsのスレッドセーフか、それとも私が見逃した何かですか?

事前にご協力いただきありがとうございます。:)

ポール

4

3 に答える 3

1

まあ、問題はBoostでもBoost::Asioでもありませんでした。アプリケーションをテストした方法は次のとおりです。

nc (Netcat) を使用して、このコマンドでアプリケーションのパフォーマンスと機能をテストしました

nc localhost 4450 <testfile

ここで、testfile には 36 文字の長さのテスト文字列が含まれていました。Netcat は遅いだけではありませんでした。それがこの問題の原因でした

戦略を変更し、boost::asio アプリケーションを少しコーディングして、同じリクエストをアプリケーションに送信したところ、機能しました。速く、簡単で、何の問題もありません。

私は教訓を学びました: ストレス テストに Netcat を二度と使用しないでください!

于 2011-02-26T22:51:59.747 に答える
1

私はコメントとしてこれと同じ点を指摘しましたが、回答に拡張する価値があると考えました。あなたが持っているコードのスニペットを投稿しても、あまり役に立ちません。全体像を把握することはできません。たとえば、次の概念は私には明確ではありません。

  1. ハンドラーでbytes_transferredパラメーターをチェックしていません。async_read_someこれは非常に重要です。なぜなら、バイトを読み取るように指示しても、nバイトを読み取ったときに返される可能性がn - xあるからx <= nです。ドキュメントに記載されているように、 async_read free 関数のような合成操作の 1 つを使用することを検討する必要があります。
  2. おそらく提供された例に基づいて、非同期読み取り操作にカスタムメモリ割り当てを使用しています。なぜそれが必要なのですか?
  3. バッファの寿命。async_read例:ハンドラが呼び出されるまで、バッファはスコープ内にとどまりますか?
  4. オブジェクトの寿命。例:shared_ptr正しく使っていますか?はio_serviceイベント ループ全体のスコープ内ですか?
  5. io_serviceプロセスごとに 1 つ、またはスレッドごとに 1 つ使用していますか?
  6. なぜスレッドが必要なのですか?通常、最初にシングル スレッド コンテキストで非同期プログラミングを理解する方が簡単です。

これらはすべて、Boost.Asio を正しく使用するための非常に重要な概念です。デバッグの一部は、認識された問題をより小さな再現子に煮詰めることです。これは、スタック オーバーフローと一般的に優れたプログラマーになるための両方に役立ちます。問題を理解するのに役立ち、問題を見つけるのにも役立ちます私たちがコンパイルできる、より小さな再現可能な例を作成することにいくらかの努力を費やすことを強くお勧めします。それが不可能な場合は、シングル スレッドのシナリオが機能することが証明された後にのみ、複数のスレッドを使用することを検討してください。

于 2011-02-26T16:59:39.373 に答える
0

最初に行うことは、async_read_some を呼び出す行をリファクタリングすることです。あまりにも長いため、何が起こっているのかを確認することはほとんど不可能です。

私の推測では、これを叩くと、古い呼び出しが戻る前に、handle_read への新しい呼び出しが発生するでしょう。handle_read はスレッドセーフではありません。他のこととは別に、

strcpy(data_,"");

複数のコピーがすべて data_ にコピーしようとすると、問題が発生します。

handle_read をスレッドセーフにする必要があります。

于 2011-02-23T21:53:45.150 に答える