# 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
--- /dev/null
+/***************************************************************************
+ * 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 <sstream>
+
+#include "client.hxx"
+
+namespace libt2n
+{
+
+
+};
--- /dev/null
+/***************************************************************************
+ * 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 <string>
+
+#include "connection.hxx"
+#include "types.hxx"
+
+namespace libt2n
+{
+
+class client_connection : public connection
+{
+ public:
+ client_connection()
+ : connection()
+ { }
+};
+
+}
+
+#endif
--- /dev/null
+/***************************************************************************
+ * 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 <string>
+#include <sstream>
+
+#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);
+}
+
+
+
+}
--- /dev/null
+/***************************************************************************
+ * 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 <string>
+
+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
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<unsigned int, connection*>::iterator ie=connections.end();
- for(std::map<unsigned int, connection*>::iterator i=connections.begin(); i != ie; i++)
+ 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;
}
-int server::add_connection(connection* newconn)
+int server::add_connection(server_connection* newconn)
{
unsigned int cid=next_id++;
newconn->set_id(cid);
\retval Pointer to connection object
*/
-connection* server::get_connection(unsigned int conn_id)
+server_connection* server::get_connection(unsigned int conn_id)
{
- std::map<unsigned int, connection*>::iterator p=connections.find(conn_id);
+ std::map<unsigned int, server_connection*>::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<unsigned int, connection*>::iterator ie=connections.end();
- for(std::map<unsigned int, connection*>::iterator i=connections.begin(); i != ie; i++)
+ 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->check_timeout();
+
+ for(std::map<unsigned int, server_connection*>::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)
// 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<unsigned int, connection*>::iterator ie=connections.end();
- for(std::map<unsigned int, connection*>::iterator i=connections.begin(); i != ie; i++)
- if (i->second->get_packet(data,conn_id))
+ 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->get_packet(data))
+ {
+ conn_id=i->first;
return true;
+ }
+
+ return false;
}
void server::log(log_level_values level, const char* message)
#include <string>
#include <map>
+#include "connection.hxx"
+#include "types.hxx"
+
namespace libt2n
{
/**
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; }
{ 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;
};
/**
*/
class server
{
- public:
- enum log_level_values { none=0, error=1, debug=2 };
-
private:
int default_timeout;
log_level_values log_level;
unsigned int next_id;
protected:
- std::map<unsigned int, connection*> connections;
+ std::map<unsigned int, server_connection*> connections;
server()
{
next_id=1;
}
- int add_connection(connection* newconn);
+ int add_connection(server_connection* newconn);
public:
virtual ~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;
--- /dev/null
+/***************************************************************************
+ * 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
--- /dev/null
+/***************************************************************************
+ * 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 <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <time.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <sstream>
+
+#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;
+}
+
+}
--- /dev/null
+/***************************************************************************
+ * 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
{
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. */
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());
}
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;
}
void socket_server::fill_connection_buffers()
{
- std::map<unsigned int, connection*>::iterator ie=connections.end();
- for(std::map<unsigned int, connection*>::iterator i=connections.begin(); i != ie; i++)
+ 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->is_closed())
- {
- socket_connection* cp=dynamic_cast<socket_connection*>(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<socket_server*>(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;
}
}
#include <sys/types.h>
#include "server.hxx"
+#include "socket_handler.hxx"
+#include "types.hxx"
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);
};
}
--- /dev/null
+/***************************************************************************
+ * 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