为p2p应用程序进行异步网络编程,我遇到了麻烦。
我的应用程序必须是服务器和客户端。当服务器收到一个
请求它必须将其广播到k
其他服务器。我认为boost :: asio示例的HTTP Server 3 Example可以正常工作,其中包含异步客户端(作为类)的实现。
上面提到的客户端类(来自boost :: asio客户端示例)如下:
ClientIO::ClientIO(boost::asio::io_service& io_service, tcp::resolver::iterator endpoint_iterator)
: _io_service(io_service),
strand_(io_service),
resolver_(io_service),
socket_(io_service)
{
tcp::endpoint endpoint = *endpoint_iterator;
socket_.async_connect(endpoint,
boost::bind(&ClientIO::handle_after_connect, this,
boost::asio::placeholders::error, ++endpoint_iterator));
}
void ClientIO::write(G3P mex)
{
_io_service.post(boost::bind(&ClientIO::writeMessage, this, mex));
}
void ClientIO::writeMessage(G3P mex)
{
bool write_in_progress = !messages_queue_.empty();
messages_queue_.push_back(mex);
if (!write_in_progress)
{
char* message=NULL;
boost::system::error_code ec;
if (messages_queue_.front().opcode == DATA)
{
message=(char*)malloc((10800)*sizeof(char));
}
else
message=(char*)malloc(1024*sizeof(char));
boost::asio::streambuf request;
std::ostream request_stream(&request);
serializeMessage(message, messages_queue_.front());
request_stream << message;
boost::asio::async_write(socket_, boost::asio::buffer(message, strlen(message)),
strand_.wrap(
boost::bind(&ClientIO::handle_after_write, this,
boost::asio::placeholders::error)));
free(message);
}
}
void ClientIO::readMessage()
{
boost::asio::async_read(socket_, data_,
boost::bind(&ClientIO::handle_after_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
));
}
void ClientIO::stop()
{
socket_.shutdown(tcp::socket::shutdown_both);
socket_.close();
}
void ClientIO::handle_after_connect(const boost::system::error_code& error,
tcp::resolver::iterator endpoint_iterator)
{
if (error)
{
if (endpoint_iterator != tcp::resolver::iterator())
{
socket_.close();
tcp::endpoint endpoint = *endpoint_iterator;
socket_.async_connect(endpoint,
boost::bind(&ClientIO::handle_after_connect,this,
boost::asio::placeholders::error, ++endpoint_iterator));
}
}
else
{
}
}
void ClientIO::handle_after_read(const boost::system::error_code& error, std::size_t bytes_transferred)
{
if (bytes_transferred > 0)
{
std::istream response_stream(&data_);
std::string mex="";
std::getline(response_stream, mex);
deserializeMessage(&reply_,mex);
if (reply_.opcode == REPL)
{
cout << "ack received" << endl;
}
}
if (error)
{
ERROR_MSG(error.message());
}
}
void ClientIO::handle_after_write(const boost::system::error_code& error)
{
if (error)
{
// ERROR_MSG("Error in write: " << error.message());
}
else
{
messages_queue_.pop_front();
if (!messages_queue_.empty())
{
cout << "[w] handle after write" << endl;
char* message;
if (messages_queue_.front().opcode == DATA)
{
message=(char*)malloc((10800)*sizeof(char));
}
else
message=(char*)malloc(1024*sizeof(char));
boost::asio::streambuf request;
std::ostream request_stream(&request);
serializeMessage(message, messages_queue_.front());
request_stream << message;
boost::asio::async_write(socket_, boost::asio::buffer(message, strlen(message)),
strand_.wrap(
boost::bind(&ClientIO::handle_after_write, this,
boost::asio::placeholders::error)));
}
boost::asio::async_read_until(socket_, data_,"\r\n",
strand_.wrap(
boost::bind(&ClientIO::handle_after_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred)));
}
}
ClientIO::~ClientIO()
{
cout << "service stopped" << endl;
}
}
当服务器收到新请求时,它会启动一个新的DataManagement类表单连接,经过一些计算后,写一个队列 使用上面的类到其他服务器(这里只有一个),每次写入必须对应一个ack
client --write-> server ---write->\
|--server1
server <--ACK----</
为了实现这一点,我创建了一个io_service实例(io_service_test)作为类变量,在DataManagement构造函数中使用以下代码实例化它:
DataManagement::DataManagement(){
tcp::resolver resolver(io_service_test);
tcp::resolver::query query(remotehost, remoteport);
tcp::resolver::iterator iterator = resolver.resolve(query);
cluster = new cluster_head::ClusterIO(io_service_test,iterator);
io_service_test.run_one();
}
然后,在计算之后,发送数据:
void DataManagement::sendTuple( . . . ){
. . .
io_service_test.reset();
io_service_test.run();
for (size_t i=0; i<ready_queue.size() ;i++)
{
cluster->write(fragTuple);
}
}
对应方是以相同方式修改的相同http proxy3示例(没有客户端类)。问题是,有时一切都运行良好,有时它会失败,我得到一个堆栈跟踪,有时它永远不会停止,甚至分段错误。 我认为问题是关闭io_service管理和类方法的生命,但我无法弄清楚。
答案 0 :(得分:1)
简要回顾一下代码,我看到了以下问题。
ClientIO::writeMessage
方法向收件人发送错误信息,因为它
boost::asio::async_write
,它不发送任何数据,只是将请求发送到内部ASIO的请求队列,即消息将在某个时间发送。 boost::asio::buffer
不会复制消息。它只存储对它的引用。free(message
)。即,当执行排队的写请求时,可以覆盖分配给消息的内存。ClientIO::handle_after_write
中的内存泄漏。 消息已分配但未释放。boost::asio::async_read
的{{1}}方法未被ClientIO::readMessage
电话打包。为了避免问题,需要使用#1和#2来使用类似ASIO缓冲区示例的strand_.wrap
类。
要解决问题,必须按照与shared_const_buffer
来电相同的方式使用strand_.wrap
电话。