3

I have implemented a TCP server using boost::asio. This server uses basic_stream_socket::read_some function to read data. I know that read_some does not guarantee that supplied buffer will be full before it returns.

In my project I am sending strings separated by a delimiter(if that matters). At client side I am using WinSock::send() function to send data. Now my problem is on server side I am not able to get all the strings which were sent from client side. My suspect is that read_some is receiving some data and discarding leftover data for some reason. Than again in next call its receiving another string.

Is it really possible in TCP/IP ?

I tried to use async_receive but that is eating up all my CPU, also since buffer has to be cleaned up by callback function its causing serious memory leak in my program. (I am using IoService::poll() to call handler. That handler is getting called at a very slow rate compared to calling rate of async_read()).

Again I tried to use free function read but that will not solve my purpose as it blocks for too much time with the buffer size I am supplying.

My previous implementation of the server was with WinSock API where I was able to receive all data using WinSock::recv(). Please give me some leads so that I can receive complete data using boost::asio.

here is my server side thread loop

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() in 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 in TCP Socket

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

Asynch Receive handler

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

Client Side sendData function.

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。但是,在单个读取操作中读取多个字符串的情况下,接收器无法正确处理分隔符,因为这string::insert()将导致char*在到达第一个分隔符时截断。

例如,发送者写入两个字符串"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 事件处理程序指定一个工作线程,并使用 run 而不是 poll。如果您希望该工作线程继续处理所有异步消息而不返回和退出,则将工作对象添加到 io_service 对象。有关示例,请参见此链接。

于 2013-02-12T20:26:59.450 回答