3

を使用してTCPサーバーを実装しましたboost::asio。このサーバーはbasic_stream_socket::read_some関数を使用してデータを読み取ります。read_some私はそれが戻る前に供給されたバッファがいっぱいになることを保証しないことを知っています。

私のプロジェクトでは、区切り文字で区切られた文字列を送信しています(重要な場合)。クライアント側では、WinSock::send()関数を使用してデータを送信しています。今、私の問題はサーバー側にあり、クライアント側から送信されたすべての文字列を取得できません。read_someなんらかのデータを受信し、何らかの理由で残りのデータを破棄しているのではないかと思います。次の呼び出しでは、別の文字列を受信します。

TCP / IPで本当に可能ですか?

私は使用しようとしましasync_receiveたが、それは私のCPUをすべて使い果たしています。また、コールバック関数によってバッファをクリーンアップする必要があるため、プログラムで深刻なメモリリークが発生します。(私はIoService::poll()ハンドラーを呼び出すために使用しています。そのハンドラーは、の呼び出し速度と比較して非常に遅い速度で呼び出されていますasync_read())。

繰り返しますが、free関数を使用しようとしましreadたが、提供しているバッファーサイズで長時間ブロックされるため、目的が解決しません。

以前のサーバーの実装では、WinSock APIを使用してすべてのデータを受信できましたWinSock::recv()。を使用して完全なデータを受信できるように、いくつかのリードを教えてくださいboost::asio

これが私のサーバー側のスレッドループです

void 
TCPObject::receive()
{
    if (!_asyncModeEnabled)
    {
        std::string recvString;
        if ( !_tcpSocket->receiveData( _maxBufferSize, recvString ) )
        {
            LOG_ERROR("Error Occurred while receiving data on socket.");
        }
        else
            _parseAndPopulateQueue ( recvString );
    }
    else
    {
        if ( !_tcpSocket->receiveDataAsync( _maxBufferSize ) )
        {
            LOG_ERROR("Error Occurred while receiving data on socket.");
        }
    }
}

TCPSocketのreceiveData()

bool 
TCPSocket::receiveData( unsigned int bufferSize, std::string& dataString )
{
   boost::system::error_code error;
   char *buf = new char[bufferSize + 1];
   size_t len = _tcpSocket->read_some( boost::asio::buffer((void*)buf, bufferSize),        error);
   if(error)
   {
       LOG_ERROR("Error in receiving data.");
       LOG_ERROR( error.message() );
       _tcpSocket->close();
       delete [] buf; 
       return false;
   }
   buf[len] ='\0';
   dataString.insert( 0, buf );
   delete [] buf;
   return true;
}

TCPソケットのreceiveDataAsync

bool 
TCPSocket::receiveDataAsync( unsigned int bufferSize )
{
    char *buf = new char[bufferSize + 1];

    try
    {
        _tcpSocket->async_read_some( boost::asio::buffer( (void*)buf, bufferSize ), 
                                     boost::bind(&TCPSocket::_handleAsyncReceive, 
                                                    this,
                                                    buf,
                                                    boost::asio::placeholders::error,
                                                    boost::asio::placeholders::bytes_transferred) );
        //! Asks io_service to execute callback
        _ioService->poll();
    }
    catch (std::exception& e)
    {
        LOG_ERROR("Error Receiving Data Asynchronously");
        LOG_ERROR( e.what() );
        delete [] buf;
        return false;
    }

    //we dont delete buf here as it will be deleted by callback _handleAsyncReceive
    return true;
}

非同期受信ハンドラー

void 
TCPSocket::_handleAsyncReceive(char *buf, const boost::system::error_code& ec, size_t size)
{
    if(ec)
    {
        LOG_ERROR ("Error occurred while sending data Asynchronously.");
        LOG_ERROR ( ec.message() );
    }
    else if ( size > 0 )
    {
        buf[size] = '\0';
        emit _asyncDataReceivedSignal( QString::fromLocal8Bit( buf ) );
    }
    delete [] buf;
}

クライアント側のsendData関数。

sendData(std::string data)
{
    if(!_connected)
    {
        return;
    }

    const char *pBuffer = data.c_str();

    int bytes = data.length() + 1;

    int i = 0,j;
    while (i < bytes)
    {
        j = send(_connectSocket, pBuffer+i, bytes-i, 0);

        if(j == SOCKET_ERROR)
        {
            _connected = false;
            if(!_bNetworkErrNotified)
            {
                _bNetworkErrNotified=true;
                emit networkErrorSignal(j);
            }
            LOG_ERROR( "Unable to send Network Packet" );
            break;
        }
        i += j;
    }
}
4

2 に答える 2

5

Boost.AsioのTCP機能はかなりよく使われているので、それが問題の原因であるとは思えません。データ損失のほとんどの場合、問題はアプリケーションコードの結果です。

この場合、受信機コードに問題があります。送信者は文字列を。で区切ります\0。ただし、1回の読み取り操作で複数の文字列が読み取られる場合、レシーバーは区切り文字を適切に処理できません。これは、最初の区切り文字に到達しstring::insert()たときに切り捨てが発生するためです。char*

たとえば、送信者は2つの文字列"Test string\0"とを書き込みます"Another test string\0"TCPSocket::receiveData()で、受信者はに読み込み"Test string\0Another test string\0"ますbufdataString次に、が入力されdataString.insert(0, buf)ます。この特定のオーバーロードは区切り文字までコピーされるため、dataStringが含まれます"Test string"。これを解決するにstring::insert()は、挿入する文字数をとるオーバーロードの使用を検討してくださいdataString.insert(0, buf, len)

于 2013-02-12T15:29:06.877 に答える
1

私はこれまでポーリング機能を使用したことがありません。私が行ったことは、ブロックするrun関数を使用してASIOハンドラーを処理するための専用のワーカースレッドを作成することです。Boostのドキュメントによると、非同期イベントハンドラーを処理するために使用できるようになる各スレッドは、最初にio_service:runまたはio_service:pollメソッドを呼び出す必要があります。pollを呼び出すスレッドで他に何をしているのかわかりません。

したがって、非同期ASIOイベントハンドラー専用に少なくとも1つのワーカースレッドを割り当て、pollの代わりにrunを使用することをお勧めします。そのワーカースレッドが戻ったり終了したりせずにすべての非同期メッセージを処理し続けるようにしたい場合は、作業オブジェクトをio_serviceオブジェクトに追加します。例については、このリンクを参照してください。

于 2013-02-12T20:26:59.450 に答える