Project::OSiRiON - Git repositories
Project::OSiRiON
News . About . Screenshots . Downloads . Forum . Wiki . Tracker . Git
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/net/Makefile.am6
-rw-r--r--src/net/net.h23
-rw-r--r--src/net/tcpclient.cc119
-rw-r--r--src/net/tcpclient.h59
-rw-r--r--src/net/tcpconnection.cc188
-rw-r--r--src/net/tcpconnection.h73
-rw-r--r--src/net/tcpserver.cc131
-rw-r--r--src/net/tcpserver.h51
8 files changed, 650 insertions, 0 deletions
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 <sys/types.h>
+#include <sys/socket.h>
+#include <assert.h>
+
+#include <iostream>
+
+#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 <string>
+
+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 <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <assert.h>
+
+#include <iostream>
+
+#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 <string>
+
+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 <unistd.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <iostream>
+
+#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 <string>
+
+/// 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__