2013-02-12 2 views
3

Я реализовал TCP-сервер, используя boost::asio. Этот сервер использует функцию basic_stream_socket::read_some для чтения данных. Я знаю, что read_some не гарантирует, что поставляемый буфер будет заполнен до его возврата.Данные теряются в TCP/IP с boost :: asio :: read_some?

В моем проекте я отправляю строки, разделенные разделителем (если это имеет значение). На стороне клиента я использую функцию WinSock::send() для отправки данных. Теперь моя проблема на стороне сервера. Я не могу получить все строки, которые были отправлены с клиентской стороны. Подозреваю, что read_some по некоторым причинам получает некоторые данные и отбрасывает оставшиеся данные. Затем снова в следующем вызове его получение другой строки.

Возможно ли это в TCP/IP?

Я попытался использовать async_receive, но это весь мой процессор, так как буфер должен быть очищен функцией обратного вызова, что вызывает серьезную утечку памяти в моей программе. (Я использую IoService::poll() для вызова обработчика. Этот обработчик вызывается с очень низкой скоростью по сравнению со скоростью звонка async_read()).

Снова я попытался использовать бесплатную функцию 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."); 
     } 
    } 
} 

receiveData() в TcpSocket

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; 
} 

receiveDataAsync в TCP сокет

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; 
    } 
} 
+0

Если вы хотите получить помощь, разместите свой код. Включите код сервера, а также код клиента, потому что проблема может быть в том или и в другом. –

+0

Обновлено кодом. –

ответ

5

возможности TCP Boost.Asio являются довольно хорошо использовать, так что я бы не решаются подозревать, что является источником проблемы. В большинстве случаев потери данных проблема возникает в результате применения кода приложения.

В этом случае возникает проблема с кодом приемника. Отправитель строит строки с \0. Однако приемник не может надлежащим образом обрабатывать разделитель в случаях, когда несколько строк считываются в одной операции чтения, так как string::insert() приведет к усечению char*, когда он достигнет первого разделителя.

Например, отправитель пишет две строки "Test string\0" и "Another test string\0". В TCPSocket::receiveData() приемник считывает "Test string\0Another test string\0" в buf. dataString затем заполняется dataString.insert(0, buf). Эта конкретная перегрузка будет скопирована до разделителя, поэтому dataString будет содержать "Test string".Чтобы решить эту проблему, рассмотрите возможность использования перегрузки string::insert(), которая принимает количество вставленных символов: dataString.insert(0, buf, len).

+0

Спасибо за ответ.! Я обязательно попробую это и дам вам знать. –

+0

Да, это была проблема .. спасибо :) –

1

Я раньше не использовал функцию опроса. Я создал рабочий поток, который предназначен для обработки обработчиков ASIO с функцией запуска, которая блокирует. В документации Boost говорится, что каждый поток, который должен быть доступен для обработки обработчиков событий async, должен сначала вызвать метод io_service: run или io_service: poll. Я не уверен, что еще вы делаете с потоком, который вызывает опрос.

Итак, я бы предложил выделить по крайней мере один рабочий поток для обработчиков событий async ASIO и использовать run вместо опроса. Если вы хотите, чтобы рабочий поток продолжал обрабатывать все асинхронные сообщения без возврата и выхода, добавьте рабочий объект в объект io_service. См. Этот пример link.

+0

, но постоянно запускать поток вызывающих вызовов. Я не делаю никакой другой обработки в потоке, который вызывает опрос, кроме вышеупомянутой функции «TCPObject :: receive()». Я не уверен, почему он поглощает столько ресурсов и почему скорость вызова обработчика медленнее. –

Смежные вопросы