这是我的实施:
async_read
处理消息的正确数据和
将等待来自客户A的新数据(为了不阻止客户端A)async_write
将消息发送给客户端B. 问题是,如果客户端A发送消息的速度非常快,async_writes
将在调用前一个async_write处理程序之前进行交错。
有一种简单的方法可以避免这个问题吗?
编辑1: 如果客户端C刚刚在客户端A之后向客户端B发送消息,则应出现相同的问题...
编辑2: 这会有用吗?因为它似乎阻止了,我不知道在哪里......
namespace structure {
class User {
public:
User(boost::asio::io_service& io_service, boost::asio::ssl::context& context) :
m_socket(io_service, context), m_strand(io_service), is_writing(false) {}
ssl_socket& getSocket() {
return m_socket;
}
boost::asio::strand getStrand() {
return m_strand;
}
void push(std::string str) {
m_strand.post(boost::bind(&structure::User::strand_push, this, str));
}
void strand_push(std::string str) {
std::cout << "pushing: " << boost::this_thread::get_id() << std::endl;
m_queue.push(str);
if (!is_writing) {
write();
std::cout << "going to write" << std::endl;
}
std::cout << "Already writing" << std::endl;
}
void write() {
std::cout << "writing" << std::endl;
is_writing = true;
std::string str = m_queue.front();
boost::asio::async_write(m_socket,
boost::asio::buffer(str.c_str(), str.size()),
boost::bind(&structure::User::sent, this)
);
}
void sent() {
std::cout << "sent" << std::endl;
m_queue.pop();
if (!m_queue.empty()) {
write();
return;
}
else
is_writing = false;
std::cout << "done sent" << std::endl;
}
private:
ssl_socket m_socket;
boost::asio::strand m_strand;
std::queue<std::string> m_queue;
bool is_writing;
};
}
#endif
答案 0 :(得分:42)
有一种简单的方法可以避免这个问题吗?
是,为每个客户端维护一个传出队列。检查async_write
完成处理程序中的队列大小,如果非零,则启动另一个async_write
操作。这是一个样本
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <deque>
#include <iostream>
#include <string>
class Connection
{
public:
Connection(
boost::asio::io_service& io_service
) :
_io_service( io_service ),
_strand( _io_service ),
_socket( _io_service ),
_outbox()
{
}
void write(
const std::string& message
)
{
_strand.post(
boost::bind(
&Connection::writeImpl,
this,
message
)
);
}
private:
void writeImpl(
const std::string& message
)
{
_outbox.push_back( message );
if ( _outbox.size() > 1 ) {
// outstanding async_write
return;
}
this->write();
}
void write()
{
const std::string& message = _outbox[0];
boost::asio::async_write(
_socket,
boost::asio::buffer( message.c_str(), message.size() ),
_strand.wrap(
boost::bind(
&Connection::writeHandler,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
)
)
);
}
void writeHandler(
const boost::system::error_code& error,
const size_t bytesTransferred
)
{
_outbox.pop_front();
if ( error ) {
std::cerr << "could not write: " << boost::system::system_error(error).what() << std::endl;
return;
}
if ( !_outbox.empty() ) {
// more messages to send
this->write();
}
}
private:
typedef std::deque<std::string> Outbox;
private:
boost::asio::io_service& _io_service;
boost::asio::io_service::strand _strand;
boost::asio::ip::tcp::socket _socket;
Outbox _outbox;
};
int
main()
{
boost::asio::io_service io_service;
Connection foo( io_service );
}
一些关键点
boost::asio::io_service::strand
保护对Connection::_outbox
Connection::write()
调度,因为它是公开的如果您在问题的示例中使用类似的做法,那么对我来说并不明显,因为所有方法都是公开的。
答案 1 :(得分:7)
只是想改善Sam的好答案。改进点是:
Sound.URL = "File\Ping.mp3"
在完成之前尝试从缓冲区中发送每个字节,这意味着您应该提供 您拥有的所有输入数据 < / strong>写操作,否则由于TCP数据包小于原来的数据,帧开销可能会增加。
async_write
虽然使用起来非常方便,但不是零拷贝。下面的示例演示了 零拷贝 方法:将输入数据块保留在原来的位置,并使用asio::streambuf
的分散/聚集重载来获取序列输入缓冲区(只是指向实际输入数据的指针)。
完整源代码:
async_write