std :: string在将其用作(boost。)async_read_some中的存储时搞砸了

时间:2011-02-23 21:30:17

标签: c++ boost-asio netcat

我正在使用async_read_some从保存在名为_data的char []中的端口读取数据。它的缓冲区大小足以满足每个请求:

void start() {

    socket_.async_read_some(boost::asio::buffer(data_,BUFFERSIZE),make_custom_alloc_handler(allocator_,boost::bind(&attentionSession::handle_read, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)));

}

void handle_read(const boost::system::error_code& error, size_t bytes_transferred) {

    string ip = socket_.remote_endpoint().address().to_string();
    log->processData(data_,ip,"memory");
    strcpy(data_,"");

}

processData通过将其复制到另一个新分配的char *,向请求添加一些其他信息(如时间戳等)。然后将此char []发送到writeToMemory(char *)以将char *附加到std :: string memoryBuffer:

void writeCacheToFile() {

    // This function writes buffer data to the log file

    char* temp = new char[memoryBuffer.length() + 1];
    strcpy(temp, memoryBuffer.c_str());
    writeToFile(temp);
    delete[] temp;
    memoryBuffer.clear();

}

void writeToMemory(char* data) {

    int maxSize = 1024;

     // Checks if char* data would 'fit' into the pre-defined maxSize

    if ((strlen((const char*)data) + memoryBuffer.length()) >= maxSize) {
        writeCacheToFile(); // If not the cache memoryBuffer is saved first
    }

    memoryBuffer.append((const char*) data);

    cout << memoryBuffer.length() << endl;

}

它可以工作,但是如果有不断的请求(用请求轰炸它),事情会搞砸。正如你在writeToMemory()函数中看到的那样,我将添加一行来打印出当前的memoryBuffer长度,这就是我认为它与std :: strings的线程安全有关的地方:

96
188
284
3639
94
190
286
2591
102
198
294
388
484
2591
96
2591
96
190
286
2591

每个(由processData()处理)请求的长度为96个字符。但是这里memoryBuffer的长度只是上升和下降 - 有些长度甚至比maxSize(1024个字符)还要大。

编辑: Sam指出我应该添加更多代码。这就是我启动io_service的方式:

boost::asio::io_service ioService;
boost::scoped_ptr<boost::thread> ioServiceThread; 
server_class server (ioService,PORT); // Create server instance
ioServiceThread.reset (new boost::thread ( boost::bind ( &boost::asio::io_service::run, &ioService  ) ) ); 
// Only one threaded io_service (to receive user inputs in main() function)

这是完成请求后的async_acceptor函数:

typedef boost::shared_ptr<session_class> session_ptr;

void handleAccept(session_ptr thisSession, const boost::system::error_code& error) {
    if (!error) {
      thisSession->start(); // Calls the start() function above
      thisSession.reset(new session(ioService,LOGGING_CLASS));
      acceptor.async_accept(thisSession->socket(),boost::bind(&server_class::handleAccept, this, thisSession, PLACEHOLDER_ERROR));
    }
 }

session_class包含上面提到的函数start()和handle_read(x,y)。 LOGGING_CLASS提供了写日志文件的类(包含函数writeCacheToFile()和writeToMemory(char *))。 log(如上所述)是这类的一种。

EOE:编辑结束

如果我尝试使用boost :: threads来修复外包缓存部分(将收到的char *附加到std :: string),最终会出现一个完全混乱的memoryBuffer

它真的是std :: strings的线程安全还是我错过的其他东西?

提前感谢您的帮助! :)

3 个答案:

答案 0 :(得分:1)

我提出同样的观点作为评论,但我认为值得扩展到答案。发布您所拥有的代码片段并不是非常有用,它们并没有给我们全面的了解。例如,我不清楚以下概念:

  1. 您没有检查bytes_transferred处理程序中的async_read_some参数。这是非常重要的,因为即使你告诉它读n个字节,can在读n - x字节x <= n时也会返回。作为documentation states,您应该考虑使用其中一个合并的操作,例如async_read free function
  2. 您正在使用custom memory allocations进行异步读取操作,可能基于example provided。你为什么需要那个?
  3. 缓冲生命周期。例如:在调用async_read处理程序之前,缓冲区是否保持在范围内?
  4. 对象生命周期。例如:你正确使用shared_ptr吗? io_service是否在整个事件循环的范围内?
  5. 你是每个进程使用一个io_service还是每个进程使用一个?
  6. 为什么你需要线程?通常,首先在单线程上下文中更容易理解异步编程。
  7. 使用Boost.Asio时,所有这些都是非常重要的概念。调试的一部分是将感知到的问题归结为更小的再现器。这对于Stack Overflow和一般的好程序员都很有用。它将帮助您了解问题,并帮助我们帮助您找到它。我强烈建议你花一些精力来制作一个我们可以编译的可重复的小例子。如果这不可能,请考虑在证明单线程方案有效后才使用多个线程。

答案 1 :(得分:1)

嗯,问题不是Boost也不是Boost :: Asio。这是我测试我的应用程序的方法:

我使用nc(Netcat)使用此命令测试应用程序的性能和功能

nc localhost 4450 <testfile

其中testfile包含36个字符长的测试字符串。 Netcat不仅速度慢 - 而是这个问题的起源

在我改变策略并编写了一个boost :: asio应用程序以将相同的请求发送到我的应用程序之后。快速,简单,没有任何问题。

我吸取了教训:永远不要再使用Netcat进行压力测试了!

答案 2 :(得分:0)

我要做的第一件事是重构你调用async_read_some的行。很长时间才能看到正在发生的事情。

我的猜测是,当你锤击它时,你会在旧的调用返回之前调用handle_read。 handle_read不是线程安全的。除了别的什么,

strcpy(data_,"");
当多个副本都试图复制到数据_时,

会给你带来麻烦。

你需要使handle_read成为线程安全的。