std::string read_packet(const long long &usec_timeout);
bool check_hello(const std::string& hellostr);
+ // TODO: Deny access to copy constructor or use boost::shared_ptr
std::auto_ptr<t2n_exception> constructorException;
public:
s.add_callback(new_connection,bind(&command_server::send_hello, boost::ref(*this), _1));
}
+/**
+ * Destructor
+ */
+command_server::~command_server()
+{
+}
+
/// send a hello message to a new connection
void command_server::send_hello(unsigned int conn_id)
{
void command_server::handle(long long usec_timeout, long long* usec_timeout_remaining)
{
guard_handle++;
+
try
{
if (s.fill_buffer(usec_timeout,usec_timeout_remaining))
{
std::string packet;
- unsigned int conn_id;
+ unsigned int conn_id = 0;
while (s.get_packet(packet,conn_id))
{
server_connection* conn=s.get_connection(conn_id);
if (!conn)
EXCEPTIONSTREAM(error,logic_error,"illegal connection id " << conn_id << " received");
-
try
{ handle_packet(packet,conn); }
catch (t2n_transfer_error &e)
public:
command_server(server& _s);
- // TODO: No (virtual) destructor
+ ~command_server();
void handle(long long usec_timeout=-1, long long* usec_timeout_remaining=NULL);
connection::~connection()
{
- // we want the connection_closed callbacks to be called before
- // FIXME: Functios is virtual
- close();
+ // Run close() manually since it's a virtual function
+ // and we are in the destructor.
+ if (!is_closed())
+ {
+ closed=true;
+ do_callbacks(connection_closed);
+ }
do_callbacks(connection_deleted);
}
{
server_connection::server_connection(int _timeout)
- : connection()
+ : connection_id(0)
+ , my_server(NULL)
+ , connection()
{
set_timeout(_timeout);
reset_timeout();
- connection_id=0;
- my_server=0;
+}
+
+/**
+ * Destructor
+ */
+server_connection::~server_connection()
+{
}
/// get pointer to logging stream, returns NULL if no logging needed
std::map<unsigned int, server_connection*>::iterator ie=connections.end();
for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; i++)
delete i->second;
+
+ connections.clear();
+}
+
+/**
+ * Close all open connections
+ */
+void server::close()
+{
+ std::map<unsigned int, server_connection*>::iterator ie=connections.end();
+ for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; ++i)
+ i->second->close();
}
/** @brief add a callback
}
/// add a new connection to the server
-int server::add_connection(server_connection* newconn)
+unsigned int server::add_connection(server_connection* newconn)
{
unsigned int cid=next_id++;
newconn->set_id(cid);
server *my_server;
server_connection(int _timeout);
+ virtual ~server_connection();
std::ostream* get_logstream(log_level_values level);
virtual bool fill_connection_buffers(void)=0;
- int add_connection(server_connection* newconn);
+ unsigned int add_connection(server_connection* newconn);
void do_callbacks(callback_event_type event, unsigned int conn_id);
*/
virtual bool fill_buffer(long long usec_timeout=-1, long long* usec_timeout_remaining=NULL)=0;
+ void close();
+
void cleanup();
/** @brief get a complete data packet from any client. The packet is removed from the
{
lastErrorMsg=e.what();
LOGSTREAM(debug,"tcp connect error: " << lastErrorMsg);
+ // FIXME: Don't call virtual function in constructor. Currently not dangerous but bad design.
close();
}
{
lastErrorMsg=e.what();
LOGSTREAM(debug,"unix connect error: " << lastErrorMsg);
- // FIXME: Calls virtual function close in constructor
+ // FIXME: Don't call virtual function in constructor. Currently not dangerous
close();
}
*/
socket_client_connection::~socket_client_connection()
{
- close();
+ // Destructor of socket_handler will close the socket!
}
bool fill_buffer(long long usec_timeout=-1, long long *usec_timeout_remaining=NULL)
{ return socket_handler::fill_buffer(buffer,usec_timeout,usec_timeout_remaining); }
- void close();
+ virtual void close();
void reconnect();
{
}
+/**
+ * Destructor. Closes open socket
+ */
+socket_handler::~socket_handler()
+{
+ if (sock != -1)
+ {
+ shutdown(sock,SHUT_RDWR);
+ ::close(sock);
+
+ sock = -1;
+ }
+}
+
+/// close the underlying socket connection. Don't call directly, use the version provided
+/// by the connection class you are using.
+void socket_handler::close()
+{
+ LOGSTREAM(debug,"close connection");
+ // graceful shutdown
+ shutdown(sock,SHUT_RDWR);
+ ::close(sock);
+
+ sock = -1;
+}
/// set options like fast reuse and keepalive every socket should have
void socket_handler::set_socket_options(int sock)
EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
}
-/// close the underlying socket connection. Don't call directly, use the version provided
-/// by the connection class you are using.
-void socket_handler::close()
-{
- LOGSTREAM(debug,"close connection");
- // graceful shutdown
- shutdown(sock,SHUT_RDWR);
- ::close(sock);
-}
-
/// is the underlying socket connection still open?
bool socket_handler::is_closed()
{
long long write_timeout;
socket_handler(int _sock, socket_type_value _socket_type);
- // TODO: No destructor?
+ ~socket_handler();
void set_socket_options(int sock);
start_listening();
}
+/**
+ * Destructor
+ */
socket_server::~socket_server()
{
- socket_handler::close();
+ // close all client connections
+ server::close();
+
+ // server socket will be closed by destructor of socket_handler
if (get_type()==unix_s)
unlink(unix_path.c_str());
+
+ // disconnect connection<->server pointer
+ std::map<unsigned int, server_connection*>::iterator it, it_end = connections.end();
+ for (it = connections.begin(); it != it_end; ++it)
+ {
+ socket_server_connection *conn = dynamic_cast<socket_server_connection*>(it->second);
+ if (conn)
+ conn->my_server = NULL;
+ }
}
/// start listening on a new server socket (called by the constructors)
FD_CLR(sock, &connection_set);
}
+/**
+ * Destructor
+ */
+socket_server_connection::~socket_server_connection()
+{
+ // Only notify parent server about going down.
+ // The real socket will be closed by the destructor of the base classes.
+ if (my_server && sock != -1)
+ {
+ socket_server *srv = dynamic_cast<socket_server*>(my_server);
+ if (srv)
+ srv->remove_connection_socket(sock);
+ }
+}
+
/// close this connection. complete data waiting in the buffer can still be retrieved.
void socket_server_connection::close()
{
- if (!server_connection::is_closed())
+ if (my_server && sock != -1)
{
- socket_handler::close();
- server_connection::close();
+ socket_server *srv = dynamic_cast<socket_server*>(my_server);
+ if (srv)
+ srv->remove_connection_socket(sock);
}
- if (my_server)
+ if (!server_connection::is_closed())
{
- dynamic_cast<socket_server*>(my_server)->remove_connection_socket(sock);
+ socket_handler::close();
+ server_connection::close();
}
}
: server_connection(_timeout), socket_handler(_sock,_stype)
{ }
+ ~socket_server_connection();
+
std::ostream* get_logstream(log_level_values level)
{ return server_connection::get_logstream(level); }
bool fill_buffer(long long usec_timeout=-1,long long* usec_timeout_remaining=NULL)
{ return socket_handler::fill_buffer(buffer,usec_timeout,usec_timeout_remaining); }
- void close();
+ virtual void close();
};
}
default:
// parent
{
+ // don't kill us on broken pipe
+ signal(SIGPIPE, SIG_IGN);
+
// wait till server is up
sleep(1);
socket_client_connection sc("./socket");
catch(...)
{ throw; }
-
- // FIXME: This test won't work by design:
- // The server closes the file descriptor
- // and then reopens the server socket.
- // Unfortunately the same socket # will be assigned
- // and so the second write succeedes, too.
-// CPPUNIT_ASSERT_EQUAL(string("error reading from socket : Connection reset by peer"),errormsg);
- CPPUNIT_ASSERT_EQUAL(string(""),errormsg);
+ CPPUNIT_ASSERT_EQUAL(string("write() returned Bad file descriptor"),errormsg);
}
}
}
// call handle, eventually reentrant
if (global_server)
- global_server->handle(1000);
+ global_server->handle(10000);
return ret;
}
CPPUNIT_TEST_SUITE_END();
- pid_t child_pid;
-
public:
void setUp()
void ReentrantServer()
{
- switch(child_pid=fork())
+ switch(fork())
{
case -1:
{
fork();
fork();
- for (int i=0; i < 100; i++)
+ try
{
- socket_client_connection sc("./socket");
- command_client cc(&sc);
-
- result_container rc;
- cc.send_command(new testfunc_cmd("hello"),rc);
-
- string ret=dynamic_cast<testfunc_res*>(rc.get_result())->get_data();
-
- CPPUNIT_ASSERT_EQUAL(string("hello, testfunc() was here"),ret);
+ for (int i=0; i < 100; i++)
+ {
+ socket_client_connection sc("./socket");
+ command_client cc(&sc);
+
+ result_container rc;
+ cc.send_command(new testfunc_cmd("hello"),rc);
+
+ testfunc_res *res = dynamic_cast<testfunc_res*>(rc.get_result());
+ if (res)
+ {
+ string ret = res->get_data();
+ if (ret != "hello, testfunc() was here")
+ std::cout << "ERROR reentrant server testfunc_res failed, res: " << ret << "\n";
+ }
+ else
+ {
+ std::cout << "ERROR result from reentrant server empty\n";
+ }
+ }
+ } catch (exception &e)
+ {
+ cerr << "caught exception: " << e.what() << endl;
}
// don't call atexit and stuff
default:
// parent
{
+ // don't kill us on broken pipe
+ signal(SIGPIPE, SIG_IGN);
+
socket_server ss("./socket");
command_server cs(ss);
global_server=&cs;
// max 10 sec
- long long maxtime=5000000;
+ long long maxtime=1000000;
while(maxtime > 0)
cs.handle(maxtime,&maxtime);
-
+
+ // max 10 sec
+ maxtime=1000000;
+ while(maxtime > 0)
+ cs.handle(maxtime,&maxtime);
+
+ // max 10 sec
+ maxtime=1000000;
+ while(maxtime > 0)
+ cs.handle(maxtime,&maxtime);
+
global_server = NULL;
}
public:
void setUp()
- { }
+ {
+ }
void tearDown()
{
socket_server ss("./socket");
command_server cs(ss);
- // max 10 sec
- for (int i=0; i < 10; i++)
+ // max 60 sec - we need atleast 28 handle calls to transfer the buffer
+ for (int i=0; i < 60; i++) {
cs.handle(1000000);
+ }
// don't call atexit and stuff
_exit(0);
// bail out as soon as we get something
ss.fill_buffer(-1);
+ ss.fill_buffer(-1);
// don't call atexit and stuff
_exit(0);
}