From a11e19b7adab2d5b937573701959562f06087ac5 Mon Sep 17 00:00:00 2001 From: Gerd v. Egidy Date: Tue, 10 Oct 2006 15:01:15 +0000 Subject: [PATCH] libt2n: (gerd) refactored connection classes --- src/Makefile.am | 6 +- src/client.cpp | 28 +++++ src/client.hxx | 40 ++++++++ src/connection.cpp | 72 +++++++++++++ src/connection.hxx | 64 ++++++++++++ src/server.cpp | 72 ++++++-------- src/server.hxx | 39 ++----- src/socket_client.hxx | 46 +++++++++ src/socket_handler.cpp | 236 +++++++++++++++++++++++++++++++++++++++++++ src/socket_handler.hxx | 64 ++++++++++++ src/socket_server.cpp | 262 ++++-------------------------------------------- src/socket_server.hxx | 37 ++++--- src/types.hxx | 30 ++++++ 13 files changed, 667 insertions(+), 329 deletions(-) create mode 100644 src/client.cpp create mode 100644 src/client.hxx create mode 100644 src/connection.cpp create mode 100644 src/connection.hxx create mode 100644 src/socket_client.hxx create mode 100644 src/socket_handler.cpp create mode 100644 src/socket_handler.hxx create mode 100644 src/types.hxx diff --git a/src/Makefile.am b/src/Makefile.am index 5dcf1ea..e410013 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,6 +5,8 @@ INCLUDES= $(all_includes) # the library search path. lib_LTLIBRARIES = libt2n.la libt2n_la_LDFLAGS = -module -libt2n_la_SOURCES = server.cpp socket_server.cpp +libt2n_la_SOURCES = server.cpp socket_server.cpp client.cpp connection.cpp \ + socket_handler.cpp -include_HEADERS = server.hxx socket_server.hxx t2n_exception.hxx +include_HEADERS = server.hxx socket_server.hxx t2n_exception.hxx client.hxx \ + socket_client.hxx connection.hxx types.hxx socket_handler.hxx diff --git a/src/client.cpp b/src/client.cpp new file mode 100644 index 0000000..88a24f5 --- /dev/null +++ b/src/client.cpp @@ -0,0 +1,28 @@ +/*************************************************************************** + * Copyright (C) 2006 by Gerd v. Egidy * + * gve@intra2net.com * + * * + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include + +#include "client.hxx" + +namespace libt2n +{ + + +}; diff --git a/src/client.hxx b/src/client.hxx new file mode 100644 index 0000000..28cded2 --- /dev/null +++ b/src/client.hxx @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2006 by Gerd v. Egidy * + * gve@intra2net.com * + * * + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef __LIBT2N_CLIENT +#define __LIBT2N_CLIENT + +#include + +#include "connection.hxx" +#include "types.hxx" + +namespace libt2n +{ + +class client_connection : public connection +{ + public: + client_connection() + : connection() + { } +}; + +} + +#endif diff --git a/src/connection.cpp b/src/connection.cpp new file mode 100644 index 0000000..0b445ef --- /dev/null +++ b/src/connection.cpp @@ -0,0 +1,72 @@ +/*************************************************************************** + * Copyright (C) 2006 by Gerd v. Egidy * + * gve@intra2net.com * + * * + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include + +#include "connection.hxx" + +namespace libt2n +{ + +connection::packet_size_indicator connection::bytes_available() +{ + // max packet size is unsigned int + + // no size information -> no packet + if (buffer.size() < sizeof(unsigned int)) + return false; + + packet_size_indicator psize=*((packet_size_indicator*)(buffer.data())); + + // enough data for one packet in buffer? + if (buffer.size() < sizeof(unsigned int)+psize) + return false; + + // ok, full packet there + return true; +} + +bool connection::get_packet(std::string& data) +{ + packet_size_indicator psize; + + if ((psize=bytes_available())) + { + data.assign(buffer,sizeof(unsigned int),psize); + buffer.erase(0,sizeof(unsigned int)+psize); + return true; + } + else + return false; +} + +void connection::write(const std::string& data) +{ + // prepend packet size to data + packet_size_indicator psize=data.size(); + std::string send_data(data); + send_data.insert(0,(char*)psize,sizeof(packet_size_indicator)); + + real_write(send_data); +} + + + +} diff --git a/src/connection.hxx b/src/connection.hxx new file mode 100644 index 0000000..9142c52 --- /dev/null +++ b/src/connection.hxx @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2006 by Gerd v. Egidy * + * gve@intra2net.com * + * * + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef __LIBT2N_CONNECTION +#define __LIBT2N_CONNECTION + +#include + +namespace libt2n +{ + +class connection +{ + private: + bool closed; + + protected: + connection() + { closed=false; } + + std::string buffer; + + typedef unsigned int packet_size_indicator; + + packet_size_indicator bytes_available(); + + virtual void real_write(const std::string& data)=0; + + public: + ~connection() + { close(); } + + bool is_closed() + { return closed; } + + virtual void close() + { closed=true; } + + virtual void fill_buffer(long long usec_timeout=-1)=0; + bool get_packet(std::string& data); + bool packet_available() + { return bytes_available(); } + + void write(const std::string& data); +}; + +} + +#endif diff --git a/src/server.cpp b/src/server.cpp index 3424cd8..3972ef4 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -24,52 +24,25 @@ namespace libt2n { -void connection::check_timeout() +void server_connection::check_timeout() { if (timeout != -1 && last_action_time+timeout >= time(NULL)) this->close(); } -void connection::reset_timeout() +void server_connection::reset_timeout() { last_action_time=time(NULL); } -std::string connection::get_id_string() -{ - std::ostringstream os; - os << get_id(); - return os.str(); -} - -bool connection::get_packet(std::string& data, unsigned int& conn_id) -{ - // max packet size is unsigned int - - // no size information -> no packet - if (buffer.size() < sizeof(unsigned int)) - return false; - - unsigned int psize=*((unsigned int*)(buffer.data())); - - // enough data for one packet in buffer? - if (buffer.size() < sizeof(unsigned int)+psize) - return false; - - data.assign(buffer,sizeof(unsigned int),psize); - buffer.erase(0,sizeof(unsigned int)+psize); - - return true; -} - server::~server() { - std::map::iterator ie=connections.end(); - for(std::map::iterator i=connections.begin(); i != ie; i++) + std::map::iterator ie=connections.end(); + for(std::map::iterator i=connections.begin(); i != ie; i++) delete i->second; } -int server::add_connection(connection* newconn) +int server::add_connection(server_connection* newconn) { unsigned int cid=next_id++; newconn->set_id(cid); @@ -85,20 +58,34 @@ int server::add_connection(connection* newconn) \retval Pointer to connection object */ -connection* server::get_connection(unsigned int conn_id) +server_connection* server::get_connection(unsigned int conn_id) { - std::map::iterator p=connections.find(conn_id); + std::map::iterator p=connections.find(conn_id); if (p==connections.end()) return NULL; else return p->second; } -void server::check_timeout() +void server::cleanup() { - std::map::iterator ie=connections.end(); - for(std::map::iterator i=connections.begin(); i != ie; i++) + std::map::iterator ie=connections.end(); + for(std::map::iterator i=connections.begin(); i != ie; i++) i->second->check_timeout(); + + for(std::map::iterator i=connections.begin(); i != ie;) + { + if (i->second->is_closed() && !i->second->packet_available()) + { + // closed and no usable data in buffer -> remove + delete i->second; + connections.erase(i); + i=connections.begin(); + ie=connections.end(); + } + else + i++; + } } bool server::get_packet(std::string& data, unsigned int& conn_id) @@ -106,10 +93,15 @@ bool server::get_packet(std::string& data, unsigned int& conn_id) // todo: this is somehow unfair: the first connections in the map get checked more // often than the others and can thus block them out - std::map::iterator ie=connections.end(); - for(std::map::iterator i=connections.begin(); i != ie; i++) - if (i->second->get_packet(data,conn_id)) + std::map::iterator ie=connections.end(); + for(std::map::iterator i=connections.begin(); i != ie; i++) + if (i->second->get_packet(data)) + { + conn_id=i->first; return true; + } + + return false; } void server::log(log_level_values level, const char* message) diff --git a/src/server.hxx b/src/server.hxx index 40113b8..7e496d9 100644 --- a/src/server.hxx +++ b/src/server.hxx @@ -23,6 +23,9 @@ #include #include +#include "connection.hxx" +#include "types.hxx" + namespace libt2n { @@ -31,41 +34,31 @@ class server; /** Basic connection class */ -class connection +class server_connection : public connection { private: int timeout; int last_action_time; - bool closed; unsigned int connection_id; protected: - connection(int _timeout) + server_connection(int _timeout) + : connection() { set_timeout(_timeout); reset_timeout(); - closed=false; connection_id=0; my_server=0; } server *my_server; - std::string buffer; - - typedef unsigned int packet_size_indicator; public: - ~connection() - { this->close(); } - void check_timeout(); void reset_timeout(); void set_timeout(int _timeout) { timeout=_timeout; } - bool is_closed() - { return closed; } - void set_server(server* _my_server) { my_server=_my_server; } @@ -73,13 +66,6 @@ class connection { connection_id=_connection_id; } unsigned int get_id() { return connection_id; } - std::string get_id_string(); - - virtual void close() - { closed=true; } - - bool get_packet(std::string& data, unsigned int& conn_id); - virtual void write(const std::string& data)=0; }; /** @@ -87,9 +73,6 @@ class connection */ class server { - public: - enum log_level_values { none=0, error=1, debug=2 }; - private: int default_timeout; log_level_values log_level; @@ -98,7 +81,7 @@ class server unsigned int next_id; protected: - std::map connections; + std::map connections; server() { @@ -107,7 +90,7 @@ class server next_id=1; } - int add_connection(connection* newconn); + int add_connection(server_connection* newconn); public: virtual ~server(); @@ -123,11 +106,13 @@ class server logstream=_logstream; } - connection* get_connection(unsigned int conn_id); + server_connection* get_connection(unsigned int conn_id); virtual void fill_buffer(long long usec_timeout=-1)=0; - void check_timeout(); + void cleanup(); + bool get_packet(std::string& data) + { unsigned int x; return get_packet(data,x); } bool get_packet(std::string& data, unsigned int& conn_id); virtual void fill_connection_buffers(void)=0; diff --git a/src/socket_client.hxx b/src/socket_client.hxx new file mode 100644 index 0000000..117aa6d --- /dev/null +++ b/src/socket_client.hxx @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2006 by Gerd v. Egidy * + * gve@intra2net.com * + * * + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef __LIBT2N_SOCKET_CLIENT +#define __LIBT2N_SOCKET_CLIENT + +#include "client.hxx" +#include "socket_handler.hxx" + +namespace libt2n +{ + +class socket_client_connection : public client_connection, socket_handler +{ + private: + void real_write(const std::string& data) + { socket_handler::write(data); } + + public: + socket_client_connection(int port, const char* ip="0.0.0.0"); + socket_client_connection(const char* path, mode_t filemode, const char* user="", const char* group=""); + + void fill_buffer(long long usec_timeout=-1) + { socket_handler::fill_buffer(buffer,usec_timeout); } + + void close(); +}; + +} + +#endif diff --git a/src/socket_handler.cpp b/src/socket_handler.cpp new file mode 100644 index 0000000..4fa27d0 --- /dev/null +++ b/src/socket_handler.cpp @@ -0,0 +1,236 @@ +/*************************************************************************** + * Copyright (C) 2006 by Gerd v. Egidy * + * gve@intra2net.com * + * * + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "socket_handler.hxx" +#include "t2n_exception.hxx" + +using namespace std; + +namespace libt2n +{ + +void socket_handler::set_socket_options(int sock) +{ + int i=1; + + /* fast reuse enable */ + if (setsockopt(sock,SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0) + { + string err="error setting socket option: "; + err+=strerror(errno); + log(error, err); + throw t2n_communication_error(err); + } + + /* keepalive enable */ + if (setsockopt(sock,SOL_SOCKET, SO_KEEPALIVE, &i, sizeof(i)) < 0) + { + string err="error setting socket option: "; + err+=strerror(errno); + log(error, err); + throw t2n_communication_error(err); + } + + /* close on exec */ + int fdflags; + fdflags=fcntl(sock,F_GETFD, 0); + if (fdflags < 0) + { + string err="fcntl error on socket: "; + err+=strerror(errno); + log(error, err); + throw t2n_communication_error(err); + } + fdflags |= FD_CLOEXEC; + if (fcntl(sock,F_SETFD,fdflags) < 0) + { + string err="fcntl error on socket: "; + err+=strerror(errno); + log(error, err); + throw t2n_communication_error(err); + } + + /* non-blocking mode */ + int flflags; + flflags=fcntl(sock,F_GETFL,0); + if (flflags < 0) + { + string err="fcntl error on socket: "; + err+=strerror(errno); + log(error, err); + throw t2n_communication_error(err); + } + flflags |= O_NONBLOCK; + if (fcntl(sock,F_SETFL,flflags) < 0) + { + string err="fcntl error on socket: "; + err+=strerror(errno); + log(error, err); + throw t2n_communication_error(err); + } +} + +void socket_handler::close() +{ + ::close(sock); +} + +bool socket_handler::data_waiting(long long usec_timeout) +{ + // just our socket + fd_set active_fd_set; + FD_ZERO (&active_fd_set); + FD_SET (sock, &active_fd_set); + + /* set timeout */ + struct timeval tval; + struct timeval *timeout_ptr; + + if (usec_timeout == -1) + timeout_ptr = NULL; + else + { + timeout_ptr = &tval; + + // timeout von long long usec in int sec + int usec umrechnen + tval.tv_sec = usec_timeout / 1000000; + tval.tv_usec = usec_timeout % 1000000; + } + + if(select (FD_SETSIZE, &active_fd_set, NULL, NULL, timeout_ptr) > 0) + return true; + else + return false; +} + +bool socket_handler::fill_buffer(std::string& buffer, long long usec_timeout) +{ + // fast path for timeout==-1 + if (usec_timeout==-1 || data_waiting(usec_timeout)) + return fill_buffer(buffer); + else + return false; +} + +bool socket_handler::fill_buffer(std::string& buffer) +{ + bool try_again=false; + + char socket_buffer[recv_buffer_size]; + + int nbytes = read (sock, socket_buffer, recv_buffer_size); + if (nbytes < 0) + { + if (errno == EAGAIN) + return false; // no data was waiting + else if (errno == EINTR) + { + // interrupted, try again + try_again=true; + } + else + { + log(error,string("error reading from socket : ")+strerror(errno)); + // TODO: exception? + return false; + } + } + + // End-of-file + if (nbytes == 0 && !try_again) + { + close(); + return false; + } + + // Data read -> store it + if (nbytes > 0) + buffer.assign(socket_buffer,nbytes); + + // more data waiting -> recurse + if (data_waiting()) + fill_buffer(buffer); + + if (nbytes > 0) + return true; + else + return false; +} + +void socket_handler::write(const std::string& data) +{ + int offset = 0; + while (offset < data.size()) + { + unsigned int write_size=write_block_size; + + if (offset+write_size > data.size()) + write_size = data.size()-offset; + + int rtn; + while ((rtn=::write(sock, data.data()+offset, write_size)) && + rtn == -1 && (errno == EAGAIN || errno == EINTR)) + { + usleep (80000); + log(debug,"resuming write() call after EAGAIN or EINTR"); + } + + if (rtn == -1) + { + log(error,string("write() returned ")+strerror(errno)); + // TODO: exception? + return; + } + else if (rtn != write_size) + { + ostringstream msg; + msg << "write() wrote " << rtn << " bytes, should have been " + << write_size << " (complete: " << data.size() << ")"; + + log(error,msg.str()); + + // TODO: exception? + return; + } + + offset += write_size; + } + + return; +} + +} diff --git a/src/socket_handler.hxx b/src/socket_handler.hxx new file mode 100644 index 0000000..e249cf5 --- /dev/null +++ b/src/socket_handler.hxx @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2006 by Gerd v. Egidy * + * gve@intra2net.com * + * * + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef __LIBT2N_SOCKET_HANDLER +#define __LIBT2N_SOCKET_HANDLER + +#include "types.hxx" + +namespace libt2n +{ + +class socket_handler +{ + private: + static const unsigned int recv_buffer_size=2048; + static const unsigned int write_block_size=4096; + + socket_type_value socket_type; + + bool data_waiting(long long usec_timeout=-1); + + protected: + int sock; + + socket_handler(int _sock, socket_type_value _socket_type) + { sock=_sock; socket_type=_socket_type; } + + void set_socket_options(int sock); + + void log(log_level_values level, const std::string& message) + { log(level,message.c_str()); } + virtual void log(log_level_values level, const char* message) + { return; } + + public: + socket_type_value get_type() + { return socket_type; } + + bool fill_buffer(std::string& buffer, long long usec_timeout); + bool fill_buffer(std::string& buffer); + + void close(); + + void write(const std::string& data); +}; + +} + +#endif diff --git a/src/socket_server.cpp b/src/socket_server.cpp index f0e573b..df9da5d 100644 --- a/src/socket_server.cpp +++ b/src/socket_server.cpp @@ -44,16 +44,14 @@ namespace libt2n { socket_server::socket_server(int port, const char* ip) - : server() + : server(), socket_handler(0,tcp_s) { - socket_type=tcp_s; - + // TODO } socket_server::socket_server(const char* path, mode_t filemode, const char* user, const char* group) - : server() + : server(), socket_handler(0,unix_s) { - socket_type=unix_s; unix_path=path; /* Create the socket. */ @@ -132,72 +130,9 @@ socket_server::socket_server(const char* path, mode_t filemode, const char* user FD_SET (sock, &connection_set); } -void socket_server::set_socket_options(int sock) -{ - int i=1; - - /* fast reuse enable */ - if (setsockopt(sock,SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0) - { - string err="error setting socket option: "; - err+=strerror(errno); - log(error, err); - throw t2n_server_error(err); - } - - /* keepalive enable */ - if (setsockopt(sock,SOL_SOCKET, SO_KEEPALIVE, &i, sizeof(i)) < 0) - { - string err="error setting socket option: "; - err+=strerror(errno); - log(error, err); - throw t2n_server_error(err); - } - - /* close on exec */ - int fdflags; - fdflags=fcntl(sock,F_GETFD, 0); - if (fdflags < 0) - { - string err="fcntl error on socket: "; - err+=strerror(errno); - log(error, err); - throw t2n_server_error(err); - } - fdflags |= FD_CLOEXEC; - if (fcntl(sock,F_SETFD,fdflags) < 0) - { - string err="fcntl error on socket: "; - err+=strerror(errno); - log(error, err); - throw t2n_server_error(err); - } - - /* non-blocking mode */ - int flflags; - flflags=fcntl(sock,F_GETFL,0); - if (flflags < 0) - { - string err="fcntl error on socket: "; - err+=strerror(errno); - log(error, err); - throw t2n_server_error(err); - } - flflags |= O_NONBLOCK; - if (fcntl(sock,F_SETFL,flflags) < 0) - { - string err="fcntl error on socket: "; - err+=strerror(errno); - log(error, err); - throw t2n_server_error(err); - } -} - socket_server::~socket_server() { - close(sock); - - if (socket_type==unix_s) + if (get_type()==unix_s) unlink(unix_path.c_str()); } @@ -224,56 +159,10 @@ void socket_server::new_connection() FD_SET (newsock, &connection_set); - int i=1; - - /* keepalive enable */ - if (setsockopt(newsock,SOL_SOCKET, SO_KEEPALIVE, &i, sizeof(i)) < 0) - { - string err="error setting socket option: "; - err+=strerror(errno); - log(error, err); - throw t2n_server_error(err); - } - - /* close on exec */ - int fdflags; - fdflags=fcntl(newsock,F_GETFD, 0); - if (fdflags < 0) - { - string err="fcntl error on socket: "; - err+=strerror(errno); - log(error, err); - throw t2n_server_error(err); - } - fdflags |= FD_CLOEXEC; - if (fcntl(newsock,F_SETFD,fdflags) < 0) - { - string err="fcntl error on socket: "; - err+=strerror(errno); - log(error, err); - throw t2n_server_error(err); - } - - /* non-blocking mode */ - int flflags; - flflags=fcntl(newsock,F_GETFL,0); - if (flflags < 0) - { - string err="fcntl error on socket: "; - err+=strerror(errno); - log(error, err); - throw t2n_server_error(err); - } - flflags |= O_NONBLOCK; - if (fcntl(newsock,F_SETFL,flflags) < 0) - { - string err="fcntl error on socket: "; - err+=strerror(errno); - log(error, err); - throw t2n_server_error(err); - } + socket_server_connection *nc=new socket_server_connection(newsock, get_type(), get_default_timeout()); + nc->set_socket_options(newsock); - add_connection(new socket_connection(newsock, get_default_timeout())); + add_connection(nc); return; } @@ -334,144 +223,29 @@ void socket_server::fill_buffer(long long usec_timeout) void socket_server::fill_connection_buffers() { - std::map::iterator ie=connections.end(); - for(std::map::iterator i=connections.begin(); i != ie; i++) + std::map::iterator ie=connections.end(); + for(std::map::iterator i=connections.begin(); i != ie; i++) if (!i->second->is_closed()) - { - socket_connection* cp=dynamic_cast(i->second); - cp->fill_buffer(connection_set); - } + i->second->fill_buffer(); } -socket_connection::socket_connection(int _sock, int _timeout) - : connection(_timeout) +void socket_server::remove_connection_socket(int sock) { - sock=_sock; + FD_CLR(sock, &connection_set); } -void socket_connection::close() +void socket_server_connection::close() { - -} - -void socket_connection::fill_buffer(fd_set &cur_fdset) -{ - bool try_again=false; - - if (is_closed() || !FD_ISSET (sock, &cur_fdset)) - return; // no data pending or connection closed - - // data pending -> go and get it - char socket_buffer[recv_buffer_size]; - - int nbytes = read (sock, socket_buffer, recv_buffer_size); - if (nbytes < 0) + if (!is_closed()) { - if (errno == EAGAIN) - { - if (my_server) - my_server->log(server::error,"read error: no data (EAGAIN) for connection "+get_id_string()); - return; - } - else if (errno == EINTR) - { - // interrupted, try again - try_again=true; - } - else - { - if (my_server) - my_server->log(server::error,"error reading from socket of connection "+get_id_string()+": "+strerror(errno)); - return; - } + socket_handler::close(); + server_connection::close(); } - // End-of-file - if (nbytes == 0 && !try_again) + if (my_server) { - close(); - return; + dynamic_cast(my_server)->remove_connection_socket(sock); } - - // Data read -> store it - if (nbytes > 0) - buffer.assign(socket_buffer,nbytes); - - // more data waiting? - fd_set active_fd_set; - struct timeval tval; - - FD_ZERO (&active_fd_set); - FD_SET (sock, &active_fd_set); - - /* no waiting */ - tval.tv_sec=0; - tval.tv_usec=0; - - if (select (FD_SETSIZE, &active_fd_set, NULL, NULL, &tval) >0) - { - /* more data waiting -> recurse */ - fill_buffer(active_fd_set); - } - - reset_timeout(); - - return; -} - -void socket_connection::write(const std::string& data) -{ - static const unsigned int write_block_size=4096; - - if (is_closed()) - return; - - // prepend packet size to data - packet_size_indicator psize=data.size(); - string send_data(data); - send_data.insert(0,(char*)psize,sizeof(packet_size_indicator)); - - int offset = 0; - while (offset < send_data.size()) - { - unsigned int write_size=write_block_size; - - if (offset+write_size > send_data.size()) - write_size = send_data.size()-offset; - - int rtn; - while ((rtn=::write(sock, send_data.data()+offset, write_size)) && - rtn == -1 && (errno == EAGAIN || errno == EINTR)) - { - usleep (80000); - if (my_server) - my_server->log(server::debug,"resuming write() call after EAGAIN or EINTR for connection "+get_id_string()); - } - - if (rtn == -1) - { - if (my_server) - my_server->log(server::error,"write() error on connection "+get_id_string()+": "+strerror(errno)); - } - else if (rtn != write_size) - { - if (my_server) - { - ostringstream msg; - msg << "write() error on connection " << get_id() - << ": wrote " << rtn << " bytes, should have been " - << write_size << " (complete: " << send_data.size() << ")"; - - my_server->log(server::error,msg.str()); - } - } - - offset += write_size; - } - - reset_timeout(); - - return; } } diff --git a/src/socket_server.hxx b/src/socket_server.hxx index 6056136..558b058 100644 --- a/src/socket_server.hxx +++ b/src/socket_server.hxx @@ -22,6 +22,8 @@ #include #include "server.hxx" +#include "socket_handler.hxx" +#include "types.hxx" namespace libt2n { @@ -29,52 +31,55 @@ namespace libt2n /** Socket based server class */ -class socket_server : public server +class socket_server : public socket_handler, server { - public: - enum socket_type_value { tcp_s, unix_s }; - private: - int sock; fd_set connection_set; - socket_type_value socket_type; std::string unix_path; - void set_socket_options(int sock); void new_connection(); + protected: + void log(log_level_values level, const std::string& message) + { log(level,message.c_str()); } + void log(log_level_values level, const char* message) + { server::log(level,message); } + public: socket_server(int port, const char* ip="0.0.0.0"); socket_server(const char* path, mode_t filemode, const char* user="", const char* group=""); ~socket_server(); - socket_type_value get_type() - { return socket_type; } - void fill_buffer(long long usec_timeout=-1); void fill_connection_buffers(); + + void remove_connection_socket(int sock); }; /** Socket based connection class */ -class socket_connection : public connection +class socket_server_connection : public socket_handler, server_connection { friend class socket_server; private: - static const int recv_buffer_size=2048; + socket_server_connection(int _sock, socket_type_value _stype, int _timeout) + : server_connection(_timeout), socket_handler(_sock,_stype) + { } - int sock; + void log(log_level_values level, const char* message) + { if(my_server) my_server->log(level,message); } - socket_connection(int _sock, int _timeout); + void real_write(const std::string& data) + { socket_handler::write(data); } public: + void fill_buffer(long long usec_timeout=-1) + { socket_handler::fill_buffer(buffer,usec_timeout); } void close(); - void fill_buffer(fd_set &cur_fdset); - void write(const std::string& data); }; } diff --git a/src/types.hxx b/src/types.hxx new file mode 100644 index 0000000..6ccad82 --- /dev/null +++ b/src/types.hxx @@ -0,0 +1,30 @@ +/*************************************************************************** + * Copyright (C) 2006 by Gerd v. Egidy * + * gve@intra2net.com * + * * + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef __LIBT2N_TYPES +#define __LIBT2N_TYPES + +namespace libt2n +{ + +enum log_level_values { none=0, error=1, debug=2 }; +enum socket_type_value { tcp_s, unix_s }; + +} + +#endif -- 1.7.1