boost :: asio :: streambuf :: consume - 注入垃圾字符

时间:2017-10-27 17:56:28

标签: c++ boost-asio

当我失去连接时,在我的服务器代码中,我尝试永远重新连接循环。重新连接后,我会向我连接的组件发送登录消息。该组件然后发回一个看起来像“MyResponse”的登录响应

初始连接正常。然而,在我重新连接之后,我在预期的消息之前得到了垃圾,看起来像是:“ýMyResponse”

谷歌搜索后。我在Stack Overflow上看到很多关于boost :: asio :: streambuf的问题,它用于boost :: asio中的异步套接字。特别是关于重用他的缓冲区。我遵循了那里的建议并在断开连接时调用了消耗。换句话说,我在调用shutdown并关闭我的套接字之后调用boost :: asio :: streambuf :: consume,之后在recv上调用了一个错误以响应对recv_until的调用。

我还使用wireshark来确保垃圾字符没有被发送,而不是。

经过大量调试后,似乎消耗的调用是注入一个字符而不是清除所有字符。

这是一个最小的例子:

#include <boost/asio.hpp>

#include <iostream>
#include <sstream>
#include <string>

int main()
{
    boost::asio::streambuf buffer;

    std::cout << "buffer size " << buffer.size() << std::endl;

    buffer.consume(std::numeric_limits<size_t>::max());

    std::cout << "buffer size " << buffer.size() << std::endl;

    std::istream is(&buffer);
    std::string contents;
    is >> contents;

    std::cout << "Contents: "  << contents << std::endl;

    std::cout << "buffer size " << buffer.size() << std::endl;

    return 0;
}

输出:

buffer size 0
buffer size 1
Contents: ²
buffer size 0

预期产出:

buffer size 0
buffer size 0
Contents:
buffer size 0

如果我不使用消费,在我的服务器代码中,在重新连接后的第一条消息之前,我会收到一些断开连接的消息。

如果我使用消费,我会得到一个垃圾字符。

请参阅:

Working with boost::asio::streambuf

Read until a string delimiter in boost::asio::streambuf

boost asio async_read: the read message adds to itself

2 个答案:

答案 0 :(得分:1)

ý字符表示调试堆memory fence

enter image description here

这意味着streambuf指针已损坏。

我没有全面了解您的程序,但我注意到您没有正确地同步套接字上的写入/读取。

发送方

例如,查看MyClass::Send,它会附加到现有 m_sendBuffer,但会使用async_write再次发送所有 。这是

  • 多余,因为m_sendBuffer中已存在任何意味着async_write已经启动的内容
  • 未定义的行为,因为the docs说:

      

    此操作是根据对流的async_write_some函数的零次或多次调用实现的,并且称为组合操作。程序必须确保流不执行其他写操作(例如async_write,流的async_write_some函数或执行写操作的任何其他组合操作),直到此操作完成。

  • 数据竞赛:m_sendBuffer正在修改,而之前的写入操作可能是航班

解决此问题的常用方法是拥有一个传出缓冲区队列,而不是一个缓冲区,并连续发送它们,例如: boost asio async_write : how to not interleaving async_write calls?

接收方

在这里,我没有积极发现任何其他问题。发送方面的相同问题仍然适用。

一旦您有了未定义的行为,您就会发现任何行为,因此也会影响接收行为。

最重要的是,读取循环导致m_socket上存在数据争用:Send在服务线程上运行async_read循环时从ProcessingThreadProc触发m_socket )。

tcp::socket

的线程安全

io_service类不是线程安全的。因为您有一个工作线程以及运行m_socket的线程(因此也就是完成处理程序),所以如果没有同步,您将无法访问strand

您应该使用m_socket序列化共享对象m_socket上的操作。在这种情况下,对m_strand.post([this] { auto callback = boost::bind(&MyClass::OnSend, this, boost::asio::placeholders::error); async_write(m_socket, m_sendBuffer, m_strand.wrap(callback)); }); 的访问在该链上运行的完成处理程序中已经是安全的。所有其他访问都应发布到链,例如:

m_numPostedSocketIO
  

实际上,可以使用strand来同步对m_numPostedSocketIO变量的访问,但这会删除读/写操作的全双工能力。此外,您不需要boost::asio::error::operation_aborted计数器:

关闭套接字

请注意,关闭套接字确实已取消所有挂起的异步IO操作。他们将填写错误代码m_socket

这意味着您可以简化等待挂起IO操作完成的部分。您只需关闭套接字即可。注意:

  • 如果您有多个服务线程,则有一个序列化操作的链
  • 在这种情况下,
  • 阻止对m_strand.post([this] { m_socket.close(); }); // e.g. 的不同步访问,在帖子上发布:

        apply plugin: 'kotlin-kapt'
    android {
    ...
    sourceSets {
            androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
            main.java.srcDirs += [file("$buildDir/generated/source/kapt/main")]
        }
    ...
    }
        ...
    
            compile "com.google.dagger:dagger:2.12" 
        ...
            kapt "android.arch.persistence.room:compiler:1.0.0-beta1"
            kapt "com.google.dagger:dagger-compiler:2.12"
            kapt "com.android.databinding:compiler:2.3.3"
    

答案 1 :(得分:1)

boost :: asio :: streambuf :: consume overflows。

使用

buffer.consume(buffer.consume(buffer.size());

而不是

buffer.consume(std::numeric_limits<size_t>::max());

报告错误以提升邮件列表。