boost :: asio, error de lectura asincrónico
-
10-07-2019 - |
Pregunta
Por alguna razón, esto resulta en una infracción de acceso, sin embargo, al no tener documentación / ayuda detallada sobre esto, no estoy seguro de dónde lo estoy haciendo mal. Debido a lo que he visto en el sitio de impulso, esto debería ser correcto e imprimir el contenido de cada llamada de asio :: write del cliente a una nueva línea. El cliente parece funcionar bien. Aunque en ese momento el servidor falla, pero aún no ha enviado nada.
La infracción de acceso se produce en basic_stream_socket.hpp en la línea 275. La causa parece ser que el objeto (boost :: asio :: stream_socket_service) no está inicializado (el valor de este puntero es 0xfeeefeee), pero no lo hago ' No veo por qué no lo es.
La salida del programa:
Iniciar servidor
Servidor :: startAccept ()
Servidor :: handleAccept ()
Conexión aceptada
Conexión :: startRead ()
Servidor :: startAccept ()
Conexión :: handleRead ()
ERROR DE LECTURA: la operación de E / S se ha cancelado porque salió un subproceso o una solicitud de aplicación
Conexión :: startRead ()
El código
#include "precompiled.h"
#include "db.h"
class Connection
: public boost::enable_shared_from_this<Connection>
{
public:
typedef boost::shared_ptr<Connection> Pointer;
static Pointer create(boost::asio::io_service& ioService)
{
return Pointer(new Connection(ioService));
}
ip::tcp::socket& getSocket()
{
return socket;
}
void startRead()
{
std::cout << "Connection::startRead()" << std::endl;
socket.async_read_some(boost::asio::buffer(readBuffer),
boost::bind(&Connection::handleRead,this,_1,_2));
}
private:
Connection(asio::io_service& ioService)
: socket(ioService)
{
}
void handleWrite(const boost::system::error_code&,size_t)
{
}
void handleRead(const boost::system::error_code&error,size_t len)
{
std::cout << "Connection::handleRead()" << std::endl;
if(error)
{
std::cout << "READ ERROR: ";
std::cout << boost::system::system_error(error).what();
std::cout << std::endl;
}
else
{
std::cout << "read: ";
std::cout.write(readBuffer.data(),len);
std::cout << std::endl;
}
startRead();
}
boost::array<char, 256> readBuffer;
ip::tcp::socket socket;
};
class Server
{
public:
Server(asio::io_service& ioService)
:acceptor(ioService, ip::tcp::endpoint(ip::tcp::v4(), getPort()))
{
startAccept();
}
private:
void startAccept()
{
std::cout << "RServer::startAccept()" << std::endl;
Connection::Pointer newConn =
Connection::create(acceptor.io_service());
acceptor.async_accept(newConn->getSocket(),
boost::bind(&Server::handleAccept, this, newConn,
asio::placeholders::error));
}
void handleAccept(Connection::Pointer newConn,
const boost::system::error_code& error)
{
std::cout << "Server::handleAccept()" << std::endl;
if(error)
{
std::cout << "CONNECTION ERROR: ";
std::cout << boost::system::system_error(error).what();
std::cout << std::endl;
}
else
{
std::cout << "Connection accepted" << std::endl;
startAccept();
newConn->startRead();
}
}
ip::tcp::acceptor acceptor;
};
int main()
{
std::cout << "Start server" << std::endl;
asio::io_service ioService;
RemoteAdminServer server(ioService);
boost::system::error_code error;
ioService.run(error);
}
Solución
Debería cambiar este fragmento de código:
void startRead()
{
std::cout << "Connection::startRead()" << std::endl;
socket.async_read_some(boost::asio::buffer(readBuffer),
boost::bind(&Connection::handleRead,this,_1,_2));
}
a:
void startRead()
{
std::cout << "Connection::startRead()" << std::endl;
socket.async_read_some(boost::asio::buffer(readBuffer),
boost::bind(&Connection::handleRead,this->shared_from_this(),_1,_2));
}
Observe que pasé un puntero compartido a bind
. Esto mantendrá su instancia de Connection
alrededor hasta que se invoque el controlador. De lo contrario, el recuento de uso llega a cero en Server :: startAccept
y el objeto se elimina. Luego, cuando se invoca el controlador, la memoria no es válida y experimenta el temido "comportamiento indefinido".
Otros consejos
Creo que su ejecución existe porque no le queda trabajo en la cola de trabajos.
Debe evitar que la ejecución salga y destruya su objeto de servicio.
Prueba esto:
boost :: asio :: io_service :: work
o simplemente haz un
do { ioService.run( error ); } while( !error );