namespace libt2n
{
+/** @brief a (generic) connection from client to server. Abstract.
+ */
class client_connection : public connection
{
public:
return psize;
}
+/** @brief read a complete data packet from the buffer. The packet is removed from the
+ connection buffer.
+ @param[out] data the data package
+ @retval true if packet found
+*/
bool connection::get_packet(std::string& data)
{
packet_size_indicator psize;
return false;
}
+/// send a blob to the peer
void connection::write(const std::string& data)
{
// prepend packet size to data
namespace libt2n
{
+/** @brief a connection between client and server. abstact.
+*/
class connection
{
private:
virtual ~connection()
{ close(); }
+ /// is this connection closed or not
bool is_closed()
{ return closed; }
+ /// close this connection
virtual void close()
{ closed=true; }
+ /** @brief look for new data and store it in the local buffer
+ @param usec_timeout wait until new data is found, max timeout usecs.
+ -1: wait endless
+ NULL: no timeout
+ @retval true if new data was found (does not mean that the received data
+ is a complete packet though)
+ */
virtual bool fill_buffer(long long usec_timeout=-1)=0;
bool get_packet(std::string& data);
+
+ /// returns true if a complete data packet is in the buffer. retrieve it with get_packet().
bool packet_available()
{ return bytes_available(); }
}
/**
- Gets a connection by id
+ @brief Gets a connection by id
- \param conn_id Connection ID
+ @param conn_id Connection ID
- \retval Pointer to connection object
+ @retval Pointer to connection object
*/
server_connection* server::get_connection(unsigned int conn_id)
{
return p->second;
}
+/// check for timeouts, remove closed connections. don't forget to call this from time to time.
void server::cleanup()
{
std::map<unsigned int, server_connection*>::iterator ie=connections.end();
class server;
/**
- Basic connection class
+ @brief connection on a server
+
+ on a server every connection to a client is represented as server_connection.
+ a server_connection is abstract, derived classes like socket_server_connection are used.
*/
class server_connection : public connection
{
+ friend class server;
+
private:
int timeout;
int last_action_time;
unsigned int connection_id;
+ void set_server(server* _my_server)
+ { my_server=_my_server; }
+
+ void set_id(unsigned int _connection_id)
+ { connection_id=_connection_id; }
+
protected:
server_connection(int _timeout)
: connection()
server *my_server;
public:
+ /// check if timeout is expired, close connection if so
void check_timeout();
+
+ /// reset the timeout, e.g. if something is received
void reset_timeout();
+
void set_timeout(int _timeout)
{ timeout=_timeout; }
- void set_server(server* _my_server)
- { my_server=_my_server; }
-
- void set_id(unsigned int _connection_id)
- { connection_id=_connection_id; }
+ /// get the id of this connection within the server object
unsigned int get_id()
{ return connection_id; }
};
/**
- Basic server class
+ @brief server base class
+
+ constitutes a server. is abstract, use derived classes like socket_server.
*/
class server
{
next_id=1;
}
+ virtual bool fill_connection_buffers(void)=0;
+
int add_connection(server_connection* newconn);
public:
virtual ~server();
+ /// set the default timeout for new client connections
void set_default_timeout(int _default_timeout)
{ default_timeout=_default_timeout; }
+
+ /// get the current default timeout for client connections
int get_default_timeout(void)
{ return default_timeout; }
+ /// activate logging to the given stream. everything above the given level is logged.
void set_logging(std::ostream *_logstream, log_level_values _log_level)
{
log_level=_log_level;
server_connection* get_connection(unsigned int conn_id);
- virtual void fill_buffer(long long usec_timeout=-1)=0;
+ /** @brief look for new data on all open connections, accept new connections
+ @param usec_timeout wait until new data is found, max timeout usecs.
+ -1: wait endless
+ NULL: no timeout
+ @retval true if new data was found (does not mean that the received data
+ is a complete packet though)
+ */
+ virtual bool fill_buffer(long long usec_timeout=-1)=0;
+
void cleanup();
+ /** @brief get a complete data packet from any client. The packet is removed from the
+ connection buffer.
+ @param[out] data the data package
+ @retval true if packet found
+ */
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;
+ /** @brief get a complete data packet from any client. The packet is removed from the
+ connection buffer.
+ @param[out] data the data package
+ @param[out] conn_id the connection id we got this packet from
+ @retval true if packet found
+ */
+ bool get_packet(std::string& data, unsigned int& conn_id);
+ /// write a message to the log if logging is enabled
void log(log_level_values level, const std::string& message)
{ log(level,message.c_str()); }
+ /// write a message to the log if logging is enabled
void log(log_level_values level, const char* message);
};
namespace libt2n
{
+/** @brief a connection from client to server using sockets.
+ Use this class to connect from a client to a server.
+ */
class socket_client_connection : public client_connection, public socket_handler
{
static const int max_retries_default=3;
socket_client_connection(const std::string& _server, int _port, int _max_retries=max_retries_default);
socket_client_connection(const std::string& _path, int _max_retries=max_retries_default);
+ /** @brief read data from the socket and copy it into buffer
+ @param usec_timeout wait until new data is found, max timeout usecs.
+ -1: wait endless
+ NULL: no timeout
+ @retval true if new data was found (does not mean that the received data
+ is a complete packet though)
+ */
bool fill_buffer(long long usec_timeout=-1)
{ return socket_handler::fill_buffer(buffer,usec_timeout); }
namespace libt2n
{
+/// set options like fast reuse and keepalive every socket should have
void socket_handler::set_socket_options(int sock)
{
int i=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()
{
// graceful shutdown
::close(sock);
}
+/// is the underlying socket connection still open?
bool socket_handler::is_closed()
{
int r=fcntl(sock,F_GETFL);
return !(r & O_ACCMODE);
}
+/** @brief check if new data is waiting on the raw socket
+ @param usec_timeout wait until new data is found, max timeout usecs.
+ -1: wait endless
+ NULL: no timeout
+*/
bool socket_handler::data_waiting(long long usec_timeout)
{
// just our socket
return false;
}
+/** @brief read data from the raw socket and copy it into the provided buffer
+ @param buffer the buffer where to append the new data
+ @param usec_timeout wait until new data is found, max timeout usecs.
+ -1: wait endless
+ NULL: no timeout
+*/
bool socket_handler::fill_buffer(std::string& buffer, long long usec_timeout)
{
// fast path for timeout==0
return false;
}
+/** @brief read data from the raw socket and copy it into the provided buffer. Returns
+ instantly if no data is waiting.
+ @param buffer the buffer where to append the new data
+*/
bool socket_handler::fill_buffer(std::string& buffer)
{
bool try_again=false;
return false;
}
+/// writes raw data to the socket. Don't use directly, use the write() function provided by the
+/// connection because it encapsulates the data.
void socket_handler::socket_write(const std::string& data)
{
int offset = 0;
namespace libt2n
{
+/** @brief handles socket based communication.
+ Don't use directly, use socket_server or socket_client_connection instead.
+*/
class socket_handler
{
private:
virtual void log(log_level_values level, const char* message)
{ return; }
- public:
- socket_type_value get_type()
- { return socket_type; }
+ void socket_write(const std::string& data);
+
+ virtual void close();
bool fill_buffer(std::string& buffer, long long usec_timeout);
bool fill_buffer(std::string& buffer);
- virtual void close();
+ public:
+ /// is this a tcp or udp socket connection
+ socket_type_value get_type()
+ { return socket_type; }
bool is_closed();
-
- void socket_write(const std::string& data);
};
}
namespace libt2n
{
+/** @brief create a new tcp-based server
+ @param port tcp port you want to listen on
+ @param ip the local ip you want to listen on. "0.0.0.0" means all local ips (default).
+*/
socket_server::socket_server(int port, const std::string& ip)
: server(), socket_handler(0,tcp_s)
{
// TODO
}
+/** @brief create a new unix-socked-based server
+ @param path path of the socket
+ @param filemode permissions you want to open the socket with
+ @param user local username for the socket
+ @param group local groupname for the socket
+*/
socket_server::socket_server(const std::string& path, mode_t filemode, const std::string& user, const std::string& group)
: server(), socket_handler(0,unix_s)
{
return;
}
-void socket_server::fill_buffer(long long usec_timeout)
+bool socket_server::fill_buffer(long long usec_timeout)
{
fd_set used_fdset=connection_set;
}
// check all connections for pending data
- fill_connection_buffers();
+ return fill_connection_buffers();
}
- return;
+ return false;
}
-void socket_server::fill_connection_buffers()
+bool socket_server::fill_connection_buffers()
{
+ bool data_found;
+
std::map<unsigned int, server_connection*>::iterator ie=connections.end();
for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; i++)
if (!i->second->server_connection::is_closed())
- i->second->fill_buffer(0);
+ if (i->second->fill_buffer(0))
+ data_found=true;
+
+ return data_found;
}
void socket_server::remove_connection_socket(int sock)
namespace libt2n
{
-/**
- Socket based server class
+class socket_server_connection;
+
+/** @brief Socket based server class
+
+ Use this class to instantiate a server listening for client connections.
+ Call fill_buffer() to read data from the network and get_packet() to retrieve
+ this data. Don't forget to call cleanup() from time to time to remove closed
+ connections and close idle ones.
*/
class socket_server : public socket_handler, public server
{
+ friend class socket_server_connection;
+
private:
fd_set connection_set;
std::string unix_path;
void new_connection();
+ bool fill_connection_buffers();
+ void remove_connection_socket(int sock);
+
protected:
void log(log_level_values level, const std::string& message)
{ log(level,message.c_str()); }
~socket_server();
- void fill_buffer(long long usec_timeout=-1);
- void fill_connection_buffers();
-
- void remove_connection_socket(int sock);
+ bool fill_buffer(long long usec_timeout=-1);
};
-/**
- Socket based connection class
+/** @brief Socket based connection
+
+ This class is used within a socket_server to represent the connection to each client.
*/
class socket_server_connection : public socket_handler, public server_connection
{
namespace libt2n
{
+/// possible levels for logging
enum log_level_values { none=0, error=1, debug=2 };
+
+/// possible types of a socket (tcp and unix)
enum socket_type_value { tcp_s, unix_s };
}