我的服务器运行正常,直到客户端连接然后它尝试向客户端发送消息。这是向客户端发送消息的功能。当此代码运行时,它会因错误
而崩溃SERVER.exe中0x6351117C(msvcr110d.dll)的未处理异常:0xC0000005:访问冲突读取位置0x00000002。
template <typename T, typename Handler>
void AsyncWrite(const T& t, Handler handler)
{
std::ostringstream archiveStream;
boost::archive::text_oarchive archive(archiveStream);
archive << t;
outboundData = archiveStream.str();
std::ostringstream headerStream;
headerStream << std::setw(headerLength) << std::hex << outboundData.size();
if (!headerStream || headerStream.str().size() != headerLength)
{
boost::system::error_code error(boost::asio::error::invalid_argument);
socket.get_io_service().post(boost::bind(handler, error));
return;
}
outboundHeader = headerStream.str();
std::vector<boost::asio::const_buffer> buffers;
buffers.push_back(boost::asio::buffer(outboundHeader));
buffers.push_back(boost::asio::buffer(outboundData));
boost::asio::async_write(socket, buffers, handler);
}
编辑:不知道这是否重要,但是我遵循这个例子
http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/example/cpp03/serialization/connection.hpp
答案 0 :(得分:2)
这充满了危险:
std::vector<boost::asio::const_buffer> buffers;
buffers.push_back(boost::asio::buffer(outboundHeader));
buffers.push_back(boost::asio::buffer(outboundData));
boost::asio::async_write(socket, buffers, handler);
由于buffers
是具有自动存储持续时间的本地对象,因此在async_write()
完成之前它将超出范围。仅此一点不应成为问题(必要时会复制此对象)。
但是,您的实际数据(保存在outboundHeader
和outboundData
)也来自具有自动存储持续时间的本地对象,因此在async_write()
完成之前它们也会超出范围
您必须确保传递到async_*
函数的对象的生命周期延伸到调用处理程序的位置。
这是一种可能的解决方法,但它要求更改Handler
个对象以接受数据(在您的情况下已编写)作为参数。
// new format of the handler
void Handler(std::shared_ptr<OutboundData> written);
struct OutboundData
{
std::string header;
std::string data;
};
// guarantee the lifetime of the OutboundData block
auto outbound_data = std::make_shared<OutboundData>();
// copy the contents (to send) in...
outbound_data->header = outboundHeader;
outbound_data->data = outboundData;
std::vector<boost::asio::const_buffer> buffers;
buffers.push_back(boost::asio::buffer(outbound_data->header));
buffers.push_back(boost::asio::buffer(outbound_data->header));
// send the data, handler(outbound_data) will be called upon
// completion (success or failure)
boost::asio::async_write(socket, buffers, boost::bind(handler, outbound_data));
另请注意,为了保持稳健性,通常您的处理程序应接受可能的错误条件,因此在发生故障时可以采取适当的措施。这看起来像这样:
void Handler(
const boost::system::error_code& err, // The error code
std::shared_ptr<OutboundData> written); // the data written (or not)
// calling async_write, while binding the appropriate parameter(s)
boost::asio::async_write(
socket,
buffers,
boost::bind(handler, outbound_data, boost::asio::placeholders::error);
答案 1 :(得分:1)
验证包含outboundData
和outboundHeader
的对象的生命周期是否超过async_write
操作的生命周期。
这是在关联的server.cpp示例中完成的,方法是通过connection
管理shared_ptr
,并将shared_ptr
绑定到处理程序。以下是代码的相关摘录:
/// Constructor opens the acceptor and starts waiting for the first incoming
/// connection.
server(...)
: acceptor_(...)
{
// Start an accept operation for a new connection.
connection_ptr new_conn(new connection(acceptor_.get_io_service()));
acceptor_.async_accept(new_conn->socket(),
boost::bind(&server::handle_accept, this,
boost::asio::placeholders::error, new_conn));
}
/// Handle completion of a accept operation.
void handle_accept(const boost::system::error_code& e, connection_ptr conn)
{
if (!e)
{
// Successfully accepted a new connection. Send the list of stocks to the
// client. The connection::async_write() function will automatically
// serialize the data structure for us.
conn->async_write(...,
boost::bind(&server::handle_write, this,
boost::asio::placeholders::error, conn));
}
...
}
/// Handle completion of a write operation.
void handle_write(const boost::system::error_code& e, connection_ptr conn)
{
// Nothing to do. The socket will be closed automatically when the last
// reference to the connection object goes away.
}
connection
包含outboundData
和outboundHeader
,由shared_ptr
构造函数中的server
创建和管理。然后shared_ptr
绑定到server::handle_accept()
,async_accept
的处理程序。在server::handle_accept()
内,连接绑定到server::handle_write()
,connection::async_write()
的处理程序。尽管server::handle_write()
什么都不做,但它在链中是至关重要的,因为它通过绑定参数使connection
对象保持活着。
有人可能会争辩说,如果connection
保证其生命周期超过async_write
操作而不对呼叫者施加要求,那么它就不那么具有侵入性了。一个常见的惯用解决方案是connection
继承自enable_shared_from_this
。当一个类继承自enable_shared_from_this
时,它会提供一个shared_from_this()
成员函数,该函数会将有效的shared_ptr
实例返回给this
。
以下是基于Boost.Asio serialization示例中server
和connection
个对象的完整示例。
#include <string>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/bind/protect.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
class connection
: public boost::enable_shared_from_this<connection>
{
public:
/// @brief Constructor.
connection(boost::asio::io_service& io_service)
: socket_(io_service)
{
std::cout << "connection(): " << this << std::endl;
}
~connection()
{
std::cout << "~connection(): " << this << std::endl;
}
/// @brief Get the underlying socket. Used for making a connection
/// or for accepting an incoming connection.
boost::asio::ip::tcp::socket& socket()
{
return socket_;
}
/// @brief Asynchronously write data to the connection, invoking
/// handler upon completion or failure.
template <typename Handler>
void async_write(std::string data, Handler handler)
{
// Perform processing on data and copy to member variables.
using std::swap;
swap(data_, data);
// Create a buffer sequence.
boost::array<boost::asio::const_buffer, 1> buffers = {{
boost::asio::buffer(data_)
}};
std::cout << "connection::async_write() " << this << std::endl;
// Write to the socket.
boost::asio::async_write(
socket_,
buffers, // Buffer sequence copied, not the underlying buffers.
boost::bind(&connection::handle_write<Handler>,
shared_from_this(), // Keep connection alive throughout operation.
boost::asio::placeholders::error,
handler));
}
private:
/// @brief Invokes user provided handler. This member function
/// allows for the connection object's lifespan to be
/// extended during the binding process.
template <typename Handler>
void handle_write(const boost::system::error_code& error,
Handler handler)
{
std::cout << "connection::handle_write() " << this << std::endl;
handler(error);
}
private:
boost::asio::ip::tcp::socket socket_;
std::string data_;
};
class server
{
public:
/// @brief Constructor opens an acceptor, waiting for incoming connection.
server(boost::asio::io_service& io_service,
unsigned short port)
: acceptor_(io_service,
boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))
{
start_accept();
}
private:
/// @brief Start an accept operation for a new connection.
void start_accept()
{
boost::shared_ptr<connection> new_conn =
boost::make_shared<connection>(
boost::ref(acceptor_.get_io_service()));
acceptor_.async_accept(new_conn->socket(),
boost::bind(&server::handle_accept, this,
boost::asio::placeholders::error, new_conn));
}
/// @brief Handle completion of a accept operation.
void handle_accept(const boost::system::error_code& error,
boost::shared_ptr<connection> conn)
{
if (!error)
{
// Successfully accepted a new connection. Write data to it.
conn->async_write("test data",
boost::protect(
boost::bind(&server::handle_write, this,
boost::asio::placeholders::error)));
}
// Start accepting another connection.
start_accept();
}
void handle_write(const boost::system::error_code& error)
{
std::cout << "server::handle_write()" << std::endl;
}
private:
/// The acceptor object used to accept incoming socket connections.
boost::asio::ip::tcp::acceptor acceptor_;
};
int main(int argc, char* argv[])
{
try
{
// Check command line arguments.
if (argc != 2)
{
std::cerr << "Usage: server <port>" << std::endl;
return 1;
}
unsigned short port = boost::lexical_cast<unsigned short>(argv[1]);
boost::asio::io_service io_service;
server server(io_service, port);
io_service.run();
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
运行程序,并从另一个终端连接导致以下输出:
connection(): 0x8cac18c
connection::async_write() 0x8cac18c
connection(): 0x8cac1e4
connection::handle_write() 0x8cac18c
server::handle_write()
~connection(): 0x8cac18c
注意connection
对象的生命周期至少延伸到async_write
操作的生命周期。修改后的API允许server
无需管理connection
,因为对象将自行管理。请注意,由于嵌套boost::protect
,因此需要boost::bind
。除此之外,还有一些替代方法不会给调用者带来负担,例如将绑定的处理程序打包在元组中,就像在Boost.Asio示例中的connection::async_read()
中所做的那样。