#include "plaqui/server/tcpserver.h"
#include <sigc++/class_slot.h>
+#include <glibmm/timer.h>
#ifdef DEBUG
# include <iostream>
#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
+ // FIXME
+ //cerr << "recvtimeout = " << socket.recvtimeout(1) << endl;
+ //cerr << "sendtimeout = " << socket.sendtimeout(1) << endl;
+ //cerr << "recvtimeout = " << socket.recvtimeout(1) << endl;
+ //cerr << "sendtimeout = " << socket.sendtimeout(1) << endl;
+ 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();
+ socket.listen(MAX_PENDING_CONNECTIONS);
#ifdef DEBUG
- std::cerr << __FILE__ << ": [despues de listen()] escuchando en "
- << socket.localhost() << ":" << socket.localport() << "." << std::endl;
+ cerr << __FILE__ << "(" << __LINE__ << ")"
+ << ": [despues de listen()] escuchando en "
+ << socket.localhost() << ":" << socket.localport() << "." << endl;
#endif // DEBUG
}
-void PlaQui::Server::TCPServer::on_connection_finished(
- PlaQui::Server::Connection* connection) {
+void TCPServer::finish(void) {
#ifdef DEBUG
- std::cerr << __FILE__ << ": on_connection_finished(connection = "
- << connection << ")" << std::endl;
+ cerr << __FILE__ << "(" << __LINE__ << ")"
+ << ": finish(void);" << endl;
#endif // DEBUG
- // TODO: poner lock.
+ 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
+ signal_error().emit(100000 + e.serrno(), string("No se pudo crear "
+ "conexión 'suicida' para terminar el servidor: ")
+ + e.errstr());
+ }
+}
+
+void TCPServer::on_connection_finished(Connection* connection) {
+#ifdef DEBUG
+ cerr << __FILE__ << "(" << __LINE__ << ")"
+ << ": on_connection_finished(connection = "
+ << connection << ")" << endl;
+#endif // DEBUG
+ 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(), e.errstr());
+ continue; // Supongo que puede seguir aceptando conexiones.
+ }
+#ifdef DEBUG
+ cerr << __FILE__ << "(" << __LINE__ << ")"
+ << ": real_run(): connection = " << connection
+ << endl;
+#endif // DEBUG
+ Glib::Mutex::Lock lock(connections_mutex);
+ // XXX connections_mutex.lock();
+ 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
+ // XXX connections_mutex.unlock(); // Si pongo el mutex antes del run(),
+ // muere.
// Conecto la señal para cuando termina una conexión, borrarla.
- conn->signal_finished().connect(
- SigC::bind<PlaQui::Server::Connection*>(
- 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;
+}
+
+} // namespace Server
+
+} // namespace PlaQui
+