使用boost :: asio :: read_some在TCP/IP中丢弃数据?

Abh*_*sal 3 sockets network-programming boost-asio

我已经使用了TCP服务器boost::asio.此服务器使用basic_stream_socket::read_some函数来读取数据.我知道这read_some并不能保证在返回之前提供的缓冲区已满.

在我的项目中,我发送的是由分隔符分隔的字符串(如果这很重要).在客户端我使用WinSock::send()函数发送数据.现在我的问题是在服务器端我无法获得从客户端发送的所有字符串.我怀疑是read_some因为某种原因接收了一些数据并丢弃了剩余的数据.然后再在下一次调用它接收另一个字符串.

在TCP/IP中真的可能吗?

我试图使用async_receive但是这会占用我所有的CPU,因为缓冲区必须通过回调函数清理它导致我的程序中出现严重的内存泄漏.(我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.");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

Asynch接收处理程序

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;
}
Run Code Online (Sandbox Code Playgroud)

客户端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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Tan*_*ury 5

Boost.Asio的TCP功能使用得很好,所以我会犹豫是否怀疑它是问题的根源.在大多数数据丢失的情况下,问题是应用程序代码的结果.

在这种情况下,接收器代码存在问题.发件人用字符串分隔字符串\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).