asio:存储要广播的消息的最佳方式

时间:2012-02-17 07:54:35

标签: c++ containers boost-asio

我想创建一个字符缓冲区,使用sprintf写入它,然后将它传递给async_write()的多个调用(即将它分发给一组客户端)。我的问题是用于此的最佳数据结构是什么?如果存在妥协,那么我认为定义“最佳”的优先级将是:

  1. 更少的CPU周期
  2. 代码清晰度
  3. 减少内存使用量
  4. 以下是我目前的工作原理:

    function broadcast(){
      char buf[512];
      sprintf(buf,"Hello %s","World!");
      boost::shared_ptr<std::string> msg(new std::string(buf));
      msg->append(1,0);   //NUL byte at the end
    
      for(std::vector< boost::shared_ptr<client_session> >::iterator i=clients.begin();
        i!=clients.end();++i) i->write(buf);
    }
    

    然后:

    void client_session::write(boost::shared_ptr<std::string> msg){
      if(!socket->is_open())return;
      boost::asio::async_write(*socket,
        boost::asio::buffer(*msg),
        boost::bind(&client_session::handle_write, shared_from_this(),_1,_2,msg)
        );
    }
    

    注意:

    • 典型的消息大小将小于64字节; 512缓冲区大小只是偏执狂。
    • 我传递一个NUL字节来标记每条消息的结尾;这是协议的一部分。
    • msg必须超出我的第一个代码段(asio要求),因此使用共享指针。

    我认为我可以在所有标准上做得更好。我想知道使用boost :: shared_array?或者直接从我的char buf [512]创建一个asio :: buffer(用智能指针包装)?但阅读关于这些和其他选择的文档让我对所有可能性感到不知所措。

    另外,在我当前的代码中,我将msg作为参数传递给handle_write(),以确保在达到handle_write()之前不释放智能指针。那是必需的不是吗?

    更新:如果您认为整体情况更好,我愿意用sprintf或类似内容替换std::stringstream。问题的关键是我需要撰写一条消息然后广播它,我想有效地做到这一点。

    UPDATE#2(2012年2月26日):我很欣赏人们发布答案的麻烦,但我觉得他们都没有真正回答过这个问题。没有人发布代码显示更好的方法,也没有任何数字来支持它们。事实上,我的印象是人们认为当前的方法和它一样好。

3 个答案:

答案 0 :(得分:3)

首先,请注意您将原始缓冲区而不是消息传递给write函数,我认为您不打算这样做吗?

如果您打算发送纯文本邮件,则可以先使用std::stringstd::stringstream开头,无需传递固定大小的数组。

如果你需要做更多的二进制/字节格式化,我肯定会开始用字符向量替换固定大小的数组。在这种情况下,我也不会先进行将其转换为字符串的往返,而是直接从字节向量构造asio缓冲区。如果您不必使用预定义协议,则更好的解决方案是使用Protocol BuffersThrift或任何可行的替代方案。这样您就不必担心字节序,重复,可变长度项,向后兼容性......等。

shared_ptr技巧确实是必要的,你需要存储缓冲区引用的数据,直到消耗缓冲区为止。不要忘记有些替代方案可以更清晰,比如将它存储在client_session对象本身中。但是,如果这是可行的,则取决于如何构造消息传递对象;)。

答案 1 :(得分:1)

您可以在client_session对象中存储std::list<boost::shared_ptr<std::string> >,并让client_session::write()对其进行push_back()。我认为这巧妙地避免了boost.asio的功能。

答案 2 :(得分:1)

因为我需要向许多客户发送相同的消息。实施会有点复杂。

我建议将消息​​准备为boost::shared_ptr<std::string>(建议使用@KillianDS)以避免额外的内存使用和从char buf[512];进行复制(在任何情况下都不安全,您无法确定你的程序将来会发展,如果这种能力在所有情况下都足够了)。

然后将此消息推送到内部std::queue内部的每个客户端。如果队列为空并且没有待处理的文档(对于此特定客户端,请使用布尔标志来检查) - 从队列中弹出消息并将async_write弹出到套接字,将shared_ptr作为参数传递给完成handler(传递给async_write的仿函数)。调用完成处理程序后,您可以从队列中获取下一条消息。 shared_ptr引用计数器将使消息保持活动状态,直到最后一个客户端将其足够地发送到套接字。

此外,我建议限制最大队列大小,以便在网络速度不足时减慢消息创建速度。

修改

通常sprintf在安全成本方面更有效。如果性能受到批评std::stringstream是瓶颈,您仍然可以sprintf使用std::string

std::string buf(512, '\0');
sprintf(&buf[0],"Hello %s","World!");

请注意,std::string不保证将数据存储在连续的内存块中,与std::vector相反(如果C ++ 11更改,请更正我)。实际上,std::string的所有流行实现都使用连续的内存。或者,您可以在上面的示例中使用std::vector