From 83e8023c5e46635753a609329cf9805a3520001e Mon Sep 17 00:00:00 2001 From: Stijn Buys Date: Wed, 13 Feb 2008 22:53:01 +0000 Subject: network subsystem initial import --- src/net/Makefile.am | 6 ++ src/net/net.h | 23 ++++++ src/net/tcpclient.cc | 119 ++++++++++++++++++++++++++++++ src/net/tcpclient.h | 59 +++++++++++++++ src/net/tcpconnection.cc | 188 +++++++++++++++++++++++++++++++++++++++++++++++ src/net/tcpconnection.h | 73 ++++++++++++++++++ src/net/tcpserver.cc | 131 +++++++++++++++++++++++++++++++++ src/net/tcpserver.h | 51 +++++++++++++ 8 files changed, 650 insertions(+) create mode 100644 src/net/Makefile.am create mode 100644 src/net/net.h create mode 100644 src/net/tcpclient.cc create mode 100644 src/net/tcpclient.h create mode 100644 src/net/tcpconnection.cc create mode 100644 src/net/tcpconnection.h create mode 100644 src/net/tcpserver.cc create mode 100644 src/net/tcpserver.h diff --git a/src/net/Makefile.am b/src/net/Makefile.am new file mode 100644 index 0000000..219163b --- /dev/null +++ b/src/net/Makefile.am @@ -0,0 +1,6 @@ +INCLUDES = -I$(top_srcdir)/src +METASOURCES = AUTO +noinst_LTLIBRARIES = libnet.la +libnet_la_LDFLAGS = -avoid-version -no-undefined +libnet_la_SOURCES = tcpclient.cc tcpconnection.cc tcpserver.cc +libnet_la_LIBADD = $(top_builddir)/src/sys/libsys.la diff --git a/src/net/net.h b/src/net/net.h new file mode 100644 index 0000000..ab6594d --- /dev/null +++ b/src/net/net.h @@ -0,0 +1,23 @@ +/* + net/net.h + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#ifndef __INCLUDED_NET_H__ +#define __INCLUDED_NET_H__ + +/// this namespace contains the network subsystem +namespace net +{ + +/// maximum lenght of a (compressed) network message block +const unsigned int FRAMESIZE = 1152; + +/// maximum number of pending client connections, hard limit +const unsigned int MAXPENDINGCONNECTIONS = 32; + +} + +#endif // __INCLUDED_NET_H__ + diff --git a/src/net/tcpclient.cc b/src/net/tcpclient.cc new file mode 100644 index 0000000..da5ff27 --- /dev/null +++ b/src/net/tcpclient.cc @@ -0,0 +1,119 @@ +/* + net/tcpclient.cc + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#include +#include +#include + +#include + +#include "sys/sys.h" +#include "net/net.h" +#include "net/tcpclient.h" + +namespace net +{ + +TCPClient::TCPClient(int tcpclientfd) +{ + tcpclient_fd = tcpclientfd; + tcpclient_error = false; +} + +TCPClient::~TCPClient() +{ + if (tcpclient_fd != -1) + close(tcpclient_fd); + +} + +bool TCPClient::error() const +{ + return tcpclient_error; +} + +bool TCPClient::valid() const +{ + return (tcpclient_fd != -1); +} + +bool TCPClient::invalid() const +{ + return (tcpclient_fd == -1); +} + +int TCPClient::fd() const +{ + return (tcpclient_fd); +} +void TCPClient::abort() +{ + tcpclient_error= true; +} + +void TCPClient::receive(std::string &msg) +{ + if (error() || invalid()) + return; + + char recvbuf[FRAMESIZE]; // maximum block sizeq + size_t msglen = sizeof(recvbuf); + ssize_t bytes_received; + + memset(recvbuf, '\0', sizeof(recvbuf)); + bytes_received = ::recv(tcpclient_fd, recvbuf, msglen, 0); + if (bytes_received == 0) { + // FIXME handle disconnect gracefully + con_print << "Client " << fd() << " disconnected." << std::endl; + disconnect(); + abort(); + return; + } else if (bytes_received < 0) { + con_warn << "Client " << fd() << " receive() error!" << std::endl; + // FIXME redirect error message + perror("recv"); + disconnect(); + abort(); + return; + } + msg = recvbuf; +} + +void TCPClient::send(std::string const &msg) +{ + if (error() || invalid()) + return; + + if (msg.size() > FRAMESIZE) { + con_warn << "Network message exceeds " << FRAMESIZE << " bytes!" << std::endl; + return; + } + + ssize_t bytes_sent = 0; + size_t total_sent = 0; + std::string sendbuf(msg); + + while (total_sent < msg.size()) { + bytes_sent = ::send(tcpclient_fd, sendbuf.c_str(), sendbuf.size(), 0); + if (bytes_sent < 0) { + con_warn << "Client " << fd() << " send() error!" << std::endl; + // FIXME redirect error message + perror("send"); + abort(); + return; + } + total_sent += bytes_sent; + + sendbuf.erase(sendbuf.size() - bytes_sent, bytes_sent); + } + + return; +} + +void TCPClient::disconnect() +{} + +} diff --git a/src/net/tcpclient.h b/src/net/tcpclient.h new file mode 100644 index 0000000..0fcb4b6 --- /dev/null +++ b/src/net/tcpclient.h @@ -0,0 +1,59 @@ +/* + net/tcpclient.h + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#ifndef __INCLUDED_NET_TCPCLIENT_H__ +#define __INCLUDED_NET_TCPCLIENT_H__ + +#include + +namespace net +{ + +/// A TCP client, connected to a file descriptor +class TCPClient +{ +public: + /// A new TCP client, connected to a file descriptor + TCPClient(int tcpclientfd); + + /// Delete the TCP client + /// If the file descriptor is still open, it will be closed. + virtual ~TCPClient(); + + /// Returns the error state + bool error() const; + + /// Returns true if the TCP client has a valid file descriptor + bool valid() const; + + /// Returns true if the TCP client has an invalid file descriptor + bool invalid() const; + + /// Returns the file descriptor the TCP client is connected to + int fd() const; + + /// Sets the error state + void abort(); + + /// Sends outgoing data + void send(std::string const &msg); + + /// Called by receive() when the client has disconnected + /// @see receive + virtual void disconnect(); + +protected: + /// receives incoming data + void receive(std::string &msg); + +private: + int tcpclient_fd; + bool tcpclient_error; +}; + +} + +#endif //__INCLUDED_CORE_TCPCLIENT_H__ diff --git a/src/net/tcpconnection.cc b/src/net/tcpconnection.cc new file mode 100644 index 0000000..9eaba1f --- /dev/null +++ b/src/net/tcpconnection.cc @@ -0,0 +1,188 @@ +/* + net/tcpconnection.h + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "sys/sys.h" +#include "net/net.h" +#include "net/tcpconnection.h" + +namespace net +{ + +TCPConnection::TCPConnection() +{ + tcpconnection_error = false; + tcpconnection_fd = -1; + tcpconnection_port = 0; +} + +TCPConnection::~TCPConnection() +{ + disconnect(); +} + +std::string TCPConnection::host() const +{ + return tcpconnection_host; +} + +int TCPConnection::port() const +{ + return tcpconnection_port; +} + +bool TCPConnection::error() const +{ + return tcpconnection_error; +} + +bool TCPConnection::valid() const +{ + return (tcpconnection_fd != -1); +} + +bool TCPConnection::invalid() const +{ + return (tcpconnection_fd == -1); +} + +int TCPConnection::fd() const +{ + return (tcpconnection_fd); +} + + +bool TCPConnection::connected() +{ + return ((tcpconnection_fd != -1) && !tcpconnection_error); +} + + +void TCPConnection::abort() +{ + tcpconnection_error= true; +} + +void TCPConnection::disconnect() +{ + if (tcpconnection_fd != -1) + close(tcpconnection_fd); + tcpconnection_fd = -1; + tcpconnection_error = false; + tcpconnection_host.clear(); + tcpconnection_port = 0; + con_debug << "Network disconnected from server" << std::endl; +} + +void TCPConnection::connect(std::string to_host, int to_port) +{ + if (valid()) + return; + + // resolve serverhostname + struct hostent *serverhostent; + serverhostent = gethostbyname(to_host.c_str()); + if (!serverhostent) { + con_warn << "Network can't resolve " << to_host.c_str() << "!" << std::endl; + abort(); + return; + } + + con_print << "Connecting to " << inet_ntoa(*((struct in_addr *)serverhostent->h_addr)) << ":" << to_port << "..." << std::endl; + + // Get a socket file descriptor + tcpconnection_fd = socket(PF_INET, SOCK_STREAM, 0); + if (tcpconnection_fd == -1) { + con_error << "Network socket() failed!" << std::endl; + abort(); + return; + } + + // make the connection + struct sockaddr_in server_addr; + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(to_port); + server_addr.sin_addr.s_addr = inet_addr(inet_ntoa(*((struct in_addr *)serverhostent->h_addr))); + memset(server_addr.sin_zero, '\0', sizeof server_addr.sin_zero); + + if (::connect(tcpconnection_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) != 0) { + con_error << "Network connect() failed!" << std::endl; + abort(); + return; + } + + tcpconnection_host = to_host; + tcpconnection_port = to_port; + tcpconnection_error = false; + con_print << "Connected to server." << std::endl; + +} + +void TCPConnection::receive(std::string &msg) +{ + if (error() || invalid()) + return; + + char recvbuf[FRAMESIZE]; // maximum block sizeq + size_t msglen = sizeof(recvbuf); + ssize_t bytes_received; + + memset(recvbuf, '\0', sizeof(recvbuf)); + bytes_received = ::recv(tcpconnection_fd, recvbuf, msglen, 0); + if (bytes_received == 0) { + con_print << "Server disconnected."; + abort(); + return; + } else if (bytes_received < 0) { + con_error << "Network receive() error!" << std::endl; + perror("recv"); + abort(); + return; + } + msg = recvbuf; +} + +void TCPConnection::send(std::string const &msg) +{ + if (error() || invalid()) + return; + + if (msg.size() > FRAMESIZE) { + con_warn << "Network message exceeds " << FRAMESIZE << " bytes!" << std::endl; + return; + } + + ssize_t bytes_sent = 0; + size_t total_sent = 0; + std::string sendbuf(msg); + + while (total_sent < msg.size()) { + bytes_sent = ::send(tcpconnection_fd, sendbuf.c_str(), sendbuf.size(), 0); + if (bytes_sent < 0) { + con_error << "Network send() error!" << std::endl; + perror("send"); + abort(); + return; + } + total_sent += bytes_sent; + + // assert (bytes_sent <= sendbuf.size()); + sendbuf.erase(sendbuf.size() - bytes_sent, bytes_sent); + } + + return; +} + +} diff --git a/src/net/tcpconnection.h b/src/net/tcpconnection.h new file mode 100644 index 0000000..7e71ed0 --- /dev/null +++ b/src/net/tcpconnection.h @@ -0,0 +1,73 @@ +/* + net/tcpconnection.h + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#ifndef __INCLUDED_NET_TCPCONNECTION_H__ +#define __INCLUDED_NET_TCPCONNECTION_H__ + +#include + +namespace net +{ + +/// A TCP client, connected to a file descriptor +/// Handles the connection from a client to a server +class TCPConnection +{ +public: + /// A new TCP client + TCPConnection(); + + /// Delete the TCP connection + /// If the file descriptor is still open, it will be closed. + virtual ~TCPConnection(); + + /// returns true if connected + bool connected(); + + /// the remote hostname the client is connected to + std::string host() const; + + /// the remote port the client is connected to + int port() const; + + /// the error state of the TCP client + bool error() const; + + /// returns true if the TCP connection has a valid file descriptor + bool valid() const; + + /// true if the TCP connection has an invalid file descriptor + bool invalid() const; + + /// return the file descriptor of the TCP connection + int fd() const; + + /// connect to a remote host + virtual void connect(std::string to_host, int to_port); + + /// disconnect from a remote host + virtual void disconnect(); + + /// set the TCP connection to the error state + void abort(); + + /// send outgoing data + void send(std::string const &msg); + +protected: + /// receive incoming data + void receive(std::string &msg); + +private: + int tcpconnection_fd; + bool tcpconnection_error; + std::string tcpconnection_host; + int tcpconnection_port; +}; + +} + +#endif // __INCLUDED_NET_TCPCONNECTION_H__ diff --git a/src/net/tcpserver.cc b/src/net/tcpserver.cc new file mode 100644 index 0000000..f194a63 --- /dev/null +++ b/src/net/tcpserver.cc @@ -0,0 +1,131 @@ +/* + net/tcpserver.cc + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#include "sys/sys.h" +#include "net/tcpserver.h" +#include "net/net.h" + +namespace net +{ + +TCPServer::TCPServer(std::string const host, unsigned int const port) +{ + tcpserver_fd = -1; + tcpserver_error = true; + + con_debug << "TCPServer: starting." << std::endl; + + // initialize socket + tcpserver_fd = ::socket(PF_INET, SOCK_STREAM, 0); + if (tcpserver_fd == -1) { + // FIXME error handling + con_error << "Network can't create socket!" << std::endl; + perror("socket"); + return; + } + + // set socket options + socklen_t yes = 1; + if (::setsockopt(tcpserver_fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(socklen_t)) == -1) { + // FIXME error handling + std::cerr << "Network can't set socket options!" << std::endl; + perror("setsockopt"); + return; + } + + // Get the local adress to bind to + struct sockaddr_in listen_addr; + listen_addr.sin_family = AF_INET; + listen_addr.sin_port = htons(port); + //listen_addr.sin_addr.s_addr = htonl(INADDR_ANY); + listen_addr.sin_addr.s_addr = inet_addr(host.c_str()); + memset(listen_addr.sin_zero, '\0', sizeof(listen_addr.sin_zero)); + + // bind the local address to the socket ( note the typecast) + if (::bind(tcpserver_fd, (struct sockaddr *) &listen_addr, sizeof(struct sockaddr)) == -1) { + // FIXME error handling + con_error << "Network can't bind to local address!" << std::endl; + perror("bind"); + return; + } + + // listen + if (::listen(tcpserver_fd, MAXPENDINGCONNECTIONS) == -1) { + // FIXME error handling + con_error << "Network failed to listen on socket!" << std::endl; + perror("listen"); + return; + } + + tcpserver_error = false; + con_print << "Listening on " << inet_ntoa(listen_addr.sin_addr) << + ":" << ntohs(listen_addr.sin_port) << std::endl; +} + +TCPServer::~TCPServer() +{ + if (tcpserver_fd != -1) + ::close(tcpserver_fd); + con_debug << "TCPServer: terminated." << std::endl; +} + +bool TCPServer::valid() const +{ + return (tcpserver_fd != -1); +} + +bool TCPServer::invalid() const +{ + return (tcpserver_fd == -1); +} + +bool TCPServer::error() const +{ + return tcpserver_error; +} + +int TCPServer::fd() const +{ + return tcpserver_fd; +} + +void TCPServer::abort() +{ + tcpserver_error = true; +} + +void TCPServer::accept() +{ + con_debug << "TCPServer: accept()" << std::endl; + struct sockaddr_in client_addr; + socklen_t addrlen = sizeof(struct sockaddr_in); + + int clientfd = ::accept(tcpserver_fd, (struct sockaddr *) &client_addr, &addrlen); + if (clientfd == -1) { + // FIXME error handling + con_error << "Network accept() error!" << std::endl; + perror("accept"); + return; + } else { + std::string host(inet_ntoa(client_addr.sin_addr)); + int port = client_addr.sin_port; + client_connect(clientfd, host, port); + } +} + +} diff --git a/src/net/tcpserver.h b/src/net/tcpserver.h new file mode 100644 index 0000000..dd87689 --- /dev/null +++ b/src/net/tcpserver.h @@ -0,0 +1,51 @@ +/* + net/tcpserver.h + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#ifndef __INCLUDED_NET_TCPSERVER_H__ +#define __INCLUDED_NET_TCPSERVER_H__ + +namespace net +{ + +#include + +/// A TCP server, listening on a port +class TCPServer +{ +public: + /// Create a new TCP server, listening on a port + TCPServer(std::string const host, unsigned int const port); + + /// Delete the TCP server. If the file descriptor is still open, it will be closed + virtual ~TCPServer(); + + /// Returns true if the TCP server has a valid file descriptor + bool valid() const; + + /// Returns true if the TCP server has an invalid file descriptor + bool invalid() const; + + /// Returns the error state of the TCP server + bool error() const; + +protected: + /// Accept incoming connections + void accept(); + /// Set the error state + void abort(); + /// Returns the file descriptor the TCP server is listening on + int fd() const; + /// Called by accept() whenever a new client connects + virtual void client_connect(int const clientfd, std::string const host, int const port) = 0; + +private: + bool tcpserver_error; + int tcpserver_fd; +}; + +} + +#endif // __INCLUDED_NET_TCPSERVER_H__ -- cgit v1.2.3