2013-02-12 137 views
3

我已经使用boost::asio实现了一个TCP服务器。该服务器使用basic_stream_socket::read_some函数读取数据。我知道read_some并不能保证提供的缓冲区在返回之前就已满。使用boost :: asio :: read_some在TCP/IP中丢弃数据?

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

TCP/IP真的有可能吗?

我试图使用async_receive,但这是吃掉了我所有的CPU,也因为缓冲区必须通过回调函数清除它,导致我的程序中严重的内存泄漏。 (我正在使用IoService::poll()来呼叫处理程序,与呼叫率async_read()相比,该处理程序的呼叫速度很慢)。

我再次尝试使用免费函数read,但这不会解决我的目的,因为它阻塞了我提供的缓冲区大小的太多时间。

我之前的服务器实现是使用WinSock API,我可以使用WinSock::recv()接收所有数据。 请给我一些线索,以便我可以使用boost::asio接收完整的数据。

这里是我的TCPSocket服务器端螺纹环

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的()

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

如果您需要帮助,请发布您的代码。包括服务器代码以及客户端代码,因为问题可能在一个或两个中。 – 2013-02-12 14:01:32

+0

用代码更新。 – 2013-02-12 14:38:06

回答

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)

+0

谢谢你的回复..! 我一定会试试这个,让你知道。 – 2013-02-12 17:06:54

+0

是的,这是问题..谢谢:) – 2013-02-13 05:10:01

1

我之前没有使用过轮询功能。我所做的就是创建一个工作线程,专用于使用run函数来处理ASIO处理程序,该工作块将阻塞。 Boost文档指出,要处理异步事件处理程序的每个线程都必须首先调用io_service:run或io_service:poll方法。我不确定你还在用调用poll的线程做什么。

所以,我建议为异步ASIO事件处理程序使用至少一个工作线程,并使用run而不是poll。如果您希望该工作线程继续处理所有异步消息而不返回和退出,请将工作对象添加到io_service对象。例如,请参阅此link

+0

但运行阻止呼叫者线程永久..我没有做任何其他处理线程中调用轮询除了上面提到的'TCPObject :: receive()'函数。我不确定为什么它吃掉了那么多的资源,以及为什么处理程序调用速度更慢。 – 2013-02-13 05:14:31

相关问题