C++ asio:存储要广播的消息的最佳方式
我想创建一个字符缓冲区,使用sprintf写入,然后将其传递给多个async_write()调用(即,将其分发给一组客户端)。我的问题是,用于此目的的最佳数据结构是什么?如果存在妥协,那么我想定义“最佳”的优先事项是:C++ asio:存储要广播的消息的最佳方式,c++,containers,boost-asio,C++,Containers,Boost Asio,我想创建一个字符缓冲区,使用sprintf写入,然后将其传递给多个async_write()调用(即,将其分发给一组客户端)。我的问题是,用于此目的的最佳数据结构是什么?如果存在妥协,那么我想定义“最佳”的优先事项是: 更少的CPU周期 代码清晰度 更少的内存使用 以下是我目前拥有的,似乎有效的: function broadcast(){ char buf[512]; sprintf(buf,"Hello %s","World!"); boost::shared_ptr<st
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);
}
函数广播(){
char-buf[512];
sprintf(buf,“你好%s”,“世界!”);
boost::shared_ptr msg(新std::string(buf));
msg->append(1,0);//末尾有NUL字节
对于(std::vector::迭代器i=clients.begin();
i!=clients.end();++i)i->write(buf);
}
然后:
void client_session::write(boost::shared_ptr msg){
如果(!socket->is_open())返回;
boost::asio::异步写入(*socket,
boost::asio::buffer(*msg),
boost::bind(&client_session::handle_write,shared_from_this(),_1,_2,msg)
);
}
注:
- 典型的消息大小将小于64字节;512缓冲区大小简直是妄想症
- 我传递一个NUL字节来标记每条消息的结尾;这是协议的一部分
必须在我的第一个代码段(asio要求)之外运行,因此使用了共享指针msg
std::stringstream
或类似的东西来代替sprintf
。问题的关键是,我需要撰写一条消息,然后进行广播,我想有效地做到这一点
更新#2(2012年2月26日):我很感激人们在发布答案时遇到的麻烦,但我觉得没有人真正回答了这个问题。没有人发布代码来显示更好的方法,也没有人给出任何数字来支持它们。事实上,我得到的印象是,人们认为当前的方法和它得到的一样好。首先,请注意,您正在将原始缓冲区而不是消息传递给write函数,我认为您不是有意这样做的 如果您计划发送纯文本消息,您可以简单地使用
std::string
和std::stringstream
开始,而无需传递固定大小的数组
如果您需要进行更多的二进制/字节格式化,我肯定会首先用字符向量替换固定大小的数组。在本例中,我也不会首先将其转换为字符串,而是直接从字节向量构造asio缓冲区。如果您不必使用预定义的协议,更好的解决方案是使用类似或任何可行的替代方案。这样,您就不必担心诸如结尾、重复、可变长度项、向后兼容性等问题
shared\u ptr
技巧确实是必要的,您确实需要将缓冲区引用的数据存储在某个地方,直到缓冲区被使用为止。别忘了还有一些更清晰的替代方案,比如只将其存储在client\u会话
对象本身中。但是,这是否可行取决于消息传递对象的构造方式;) 您可以在客户端会话对象中存储std::list
,并让client\u会话::write()
在其上执行push\u-back()
。我认为这是巧妙地避免了boost.asio的功能。正如我所了解的,您需要向许多客户端发送相同的消息。实现过程会稍微复杂一些
我建议您准备一条消息作为boost::shared_ptr
(建议使用@KillianDS),以避免额外的内存使用和从char buf[512]复制代码>(在任何情况下都不安全,您无法确定您的程序在未来将如何发展,以及在所有情况下该容量是否足够)
然后将此消息推送到每个客户端内部std::queue
。如果队列为空且没有挂起的写入(对于此特定客户端,请使用布尔标志检查此情况)-将消息从队列中弹出并async\u write
将其作为完成处理程序的参数传递到套接字shared\u ptr
(传递到async\u write
)中。调用完成处理程序后,您可以从队列中获取下一条消息shared_ptr
reference计数器将使消息保持活动状态,直到最后一个客户端将其成功发送到套接字
此外,我建议限制最大队列大小,以在网络速度不足时减慢消息创建速度
编辑
通常,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
。谢谢@KillianDS。关于你的第一点,我展示的代码是有效的,也是他们在教程中如何做到的:(关于你的最后一点,他们将std::string存储在类对象中;bu
std::string buf(512, '\0');
sprintf(&buf[0],"Hello %s","World!");