让我们假设一个客户端拥有两个不同的大对象(就字节大小而言)并对其进行序列化,然后发送序列化的对象
使用boost::asio通过TCP / IP网络连接到服务器。

  • 对于客户端实现,我正在使用boost::asio::write将二进制数据(const char*)发送到服务器。
  • 对于服务器端实现,我使用read_some而不是boost::asio::ip::tcp::iostream来提高效率。我在服务器端构建了以下recv函数。第二个参数std::stringstream &is在函数末尾保存了一个较大的接收数据(> 65536字节)。

  • 当客户端调用两个顺序的boost::asio::write以便分别发送两个不同的二进制对象时,服务器端也依次调用两个相应的recv
    但是,第一个recv函数吸收所有两个传入的大数据,而第二个调用则什么也没收到;-(。
    我不知道为什么会这样以及如何解决。

    由于两个不同的对象中的每个对象都有其自己的(反)序列化功能,因此我想分别发送每个数据。实际上,由于必须通过网络发送20多个对象(而不仅仅是2个)。
    void recv (
        boost::asio::ip::tcp::socket &socket,
        std::stringstream &is) {
    
        boost::array<char, 65536> buf;
    
        for (;;) {
            boost::system::error_code error;
            size_t len = socket.read_some(boost::asio::buffer(buf), error);
            std::cout << " read "<< len << " bytes" << std::endl;  // called multiple times for debugging!
    
            if (error == boost::asio::error::eof)
              break;
            else if (error)
              throw boost::system::system_error(error); // Some other error.
    
            std::stringstream buf_ss;
            buf_ss.write(buf.data(), len);
            is << buf_ss.str();
        }
    }
    

    客户端主文件:
    int main () {
        ... // some 2 different big objects are constructed.
        std::stringstream ss1, ss2;
        ... // serializing bigObj1 -> ss1 and bigObj2-> ss2, where each object is serialized into a string. This is due to the dependency of our using some external library
        const char * big_obj_bin1 = reinterpret_cast<const char*>(ss1.str().c_str());
        const char * big_obj_bin2 = reinterpret_cast<const char*>(ss2.str().c_str());
    
        boost::system::error_code ignored_error;
        boost::asio::write(socket, boost::asio::buffer(big_obj_bin1, ss1.str().size()), ignored_error);
        boost::asio::write(socket, boost::asio::buffer(big_obj_bin2, ss2.str().size()), ignored_error);
    
        ... // do something
        return 0;
    }
    

    服务器主文件:
    int main () {
        ... // socket is generated. (communication established)
        std::stringstream ss1, ss2;
        recv(socket,ss1); // this guy absorbs all of incoming data
        recv(socket,ss2); // this guy receives 0 bytes ;-(
        ... // deserialization to two bib objects
        return 0;
    }
    

    最佳答案

    recv(socket,ss1); // this guy absorbs all of incoming data
    

    当然,它吸收了一切。您明确地用编码的recv进行无限循环,直到eof为止。这是流的结尾,这意味着“无论何时在远程端关闭套接字”。

    因此,协议(protocol)中缺少的基本要素是框架。解决该问题的最常见方法是:
  • 在发送数据之前先发送数据长度,这样服务器就知道要读取多少
  • 发送“特殊序列”来定界帧。在文本中,常见的特殊分隔符为'\0'。但是,对于二进制数据,很难(很难)找到有效载荷中自然不会出现的定界符。

    当然,如果您知道有效负载的其他特征,则可以使用它。例如。如果您的有效负载已压缩,则您将不会定期找到512个相同字节的块(它们将被压缩)。另外,您可以采取消除歧义的方式对二进制数据进行编码。 yEnc Base122等。想起(请参阅Binary Data in JSON String. Something better than Base64以获取灵感)。

  • 笔记:

    不管那个
  • 手写阅读循环很笨拙。接下来,非常不需要也将块复制到字符串流中。如果您仍要进行所有复制,则直接将boost::asio::[async_]readboost::asio::streambuf一起使用。
  • 这是清晰的UB:
    const char * big_obj_bin1 = reinterpret_cast<const char*>(ss1.str().c_str());
    const char * big_obj_bin2 = reinterpret_cast<const char*>(ss2.str().c_str());
    
    str() returns a temporary copy of the buffer-不仅浪费,而且意味着const char*在初始化后即成为悬挂
  • 关于c++ - 我们如何依次从boost::asio::tcp::ip::read_some调用中接收多个数据?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48998084/

    10-11 22:46
    查看更多