X-Git-Url: https://git.llucax.com/z.facultad/75.42/plaqui.git/blobdiff_plain/1de5f8fa25dcea9fb9de9707d75120f762ce3a87..c82138b139ddc29ded10368db1327a565b688aeb:/Server/src/tcpserver.cpp?ds=sidebyside diff --git a/Server/src/tcpserver.cpp b/Server/src/tcpserver.cpp index c00a8b6..04fd8ba 100644 --- a/Server/src/tcpserver.cpp +++ b/Server/src/tcpserver.cpp @@ -27,64 +27,178 @@ #include "plaqui/server/tcpserver.h" #include +#include #ifdef DEBUG # include #endif // DEBUG -PlaQui::Server::TCPServer::~TCPServer(void) { +using namespace std; + +namespace PlaQui { + +namespace Server { + +TCPServer::~TCPServer(void) { #ifdef DEBUG - std::cerr << __FILE__ << ": destructor." << std::endl; + cerr << __FILE__ << "(" << __LINE__ << ")" + << ": destructor." << endl; #endif // DEBUG + // Mando a terminar todas las conexiones. + connections_mutex.lock(); + for (ConnectionList::iterator con = connections.begin(); + con != connections.end(); con++) { + (*con)->finish(); + } + ConnectionList::size_type count = connections.size(); + connections_mutex.unlock(); + // Espero que terminen realmente. + while (count) { + Glib::usleep(10000); // 10 milisegundos + connections_mutex.lock(); + count = connections.size(); + connections_mutex.unlock(); + } } -PlaQui::Server::TCPServer::TCPServer(int port): +TCPServer::TCPServer(const Connection::Port& port) throw(sockerr): socket(sockbuf::sock_stream) { #ifdef DEBUG - std::cerr << __FILE__ << ": port = " << port << std::endl; + cerr << __FILE__ << "(" << __LINE__ << ")" + << ": port = " << port << endl; #endif // DEBUG + socket.reuseaddr(true); socket.bind(port); #ifdef DEBUG - std::cerr << __FILE__ << ": escuchando en " << socket.localhost() - << ":" << socket.localport() << "." << std::endl; + cerr << __FILE__ << "(" << __LINE__ << ")" + << ": escuchando en " << socket.localhost() + << ":" << socket.localport() << "." << endl; +#endif // DEBUG + socket.listen(MAX_PENDING_CONNECTIONS); +#ifdef DEBUG + cerr << __FILE__ << "(" << __LINE__ << ")" + << ": [despues de listen()] escuchando en " + << socket.localhost() << ":" << socket.localport() << "." << endl; #endif // DEBUG - socket.listen(); +} + +void TCPServer::finish(void) { #ifdef DEBUG - std::cerr << __FILE__ << ": [despues de listen()] escuchando en " - << socket.localhost() << ":" << socket.localport() << "." << std::endl; + cerr << __FILE__ << "(" << __LINE__ << ")" + << ": finish(void);" << endl; #endif // DEBUG + Runnable::finish(); + // Creo una conexión suicida para que el accept() del server retorne + // el control y el server pueda terminar realmente. + try { + sockinetbuf suicida(sockbuf::sock_stream); + suicida.connect(socket.localhost(), socket.localport()); + } catch (const sockerr& e) { + // FIXME mejorar codigos de error. + signal_error().emit(100000 + e.serrno(), string("No se pudo crear " + "conexión 'suicida' para terminar el servidor: ") + + e.errstr()); + } } -void PlaQui::Server::TCPServer::on_connection_finished( - PlaQui::Server::Connection* connection) { +void TCPServer::on_connection_finished(Connection* connection) { #ifdef DEBUG - std::cerr << __FILE__ << ": on_connection_finished(connection = " - << connection << ")" << std::endl; + cerr << __FILE__ << "(" << __LINE__ << ")" + << ": on_connection_finished(connection = " + << connection << ")" << endl; #endif // DEBUG - // TODO: poner lock. + Glib::Mutex::Lock lock(connections_mutex); connections.remove(connection); - // TODO: sacar lock. +#ifdef DEBUG + cerr << __FILE__ << "(" << __LINE__ << ")" + << ": lista de conexiones" << endl; + for (ConnectionList::const_iterator i = connections.begin(); + i != connections.end(); i++) { + cerr << "\t " << *i << endl; + } +#endif // DEBUG } -void PlaQui::Server::TCPServer::real_run(void) { +void TCPServer::real_run(void) throw() { #ifdef DEBUG - std::cerr << __FILE__ << ": real_run" << std::endl; + cerr << __FILE__ << "(" << __LINE__ << ")" + << ": real_run()" << endl; #endif // DEBUG - PlaQui::Server::Connection* conn; - while (!stop) { - // TODO: ver tema de timeout o como salir de un accept(). + Connection* connection; + while (!stop()) { // Forma grasa de salir del accept: crear conexion que salga al toque. - conn = new_connection(socket.accept()); - // TODO: poner lock. - connections.push_back(conn); - // TODO: sacar lock. - // TODO: esto va en Server::new_connection() + try { + connection = new_connection(socket.accept()); + } catch (const sockerr& e) { // No se si el accept() puede fallar. + signal_error().emit(e.serrno(), + string("Error creando conexión nueva: ") + e.errstr()); + continue; // Supongo que puede seguir aceptando conexiones. + } + if (!stop()) { + // Solo avisamos si la conexión que viene no es para matarnos. + _connection_opened(connection->get_host(), connection->get_port()); + } +#ifdef DEBUG + cerr << __FILE__ << "(" << __LINE__ << ")" + << ": real_run(): connection = " << connection + << endl; +#endif // DEBUG + Glib::Mutex::Lock lock(connections_mutex); + connections.push_back(connection); +#ifdef DEBUG + cerr << __FILE__ << "(" << __LINE__ << ")" + << ": real_run(): lista de conexiones" << endl; + for (ConnectionList::const_iterator i = connections.begin(); + i != connections.end(); i++) { + cerr << "\t " << *i << endl; + } +#endif // DEBUG // Conecto la señal para cuando termina una conexión, borrarla. - conn->signal_finished().connect( - SigC::bind( - SigC::slot_class(*this, - &PlaQui::Server::TCPServer::on_connection_finished), - conn)); - conn->run(); + connection->signal_finished().connect(SigC::bind( + SigC::slot_class(*this, &TCPServer::on_connection_finished), + connection)); + connection->run(); + } +} + +bool TCPServer::disconnect(const std::string& host, const Connection::Port& port) { +#ifdef DEBUG + cerr << __FILE__ << "(" << __LINE__ << ")" + << ": disconnect(host = " << host + << ", port = " << port << ")" << endl; +#endif // DEBUG + Glib::Mutex::Lock lock(connections_mutex); + for (ConnectionList::iterator con = connections.begin(); + con != connections.end(); con++) { + if (((*con)->get_host() == host) && ((*con)->get_port() == port)) { + (*con)->finish(); + return true; + } } + return false; } +/// \todo TODO Hay que reemplazarlo por una lista generica. +TCPServer::ConnectionInfoList TCPServer::get_connected(void) { +#ifdef DEBUG + cerr << __FILE__ << "(" << __LINE__ << ")" + << ": get_connected()" << endl; +#endif // DEBUG + TCPServer::ConnectionInfoList cl; + Glib::Mutex::Lock lock(connections_mutex); + for (ConnectionList::const_iterator con = connections.begin(); + con != connections.end(); con++) { + TCPServer::ConnectionInfo ci = + { (*con)->get_host(), (*con)->get_port() }; + cl.push_back(ci); + } + return cl; +} + +TCPServer::SignalConnectionOpened& TCPServer::signal_connection_opened(void) { + return _connection_opened; +} + +} // namespace Server + +} // namespace PlaQui +