From 83e8023c5e46635753a609329cf9805a3520001e Mon Sep 17 00:00:00 2001
From: Stijn Buys <ingar@osirion.org>
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

(limited to 'src')

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__
-- 
cgit v1.2.3