diff options
Diffstat (limited to 'src/net')
| -rw-r--r-- | src/net/Makefile.am | 6 | ||||
| -rw-r--r-- | src/net/net.h | 23 | ||||
| -rw-r--r-- | src/net/tcpclient.cc | 119 | ||||
| -rw-r--r-- | src/net/tcpclient.h | 59 | ||||
| -rw-r--r-- | src/net/tcpconnection.cc | 188 | ||||
| -rw-r--r-- | src/net/tcpconnection.h | 73 | ||||
| -rw-r--r-- | src/net/tcpserver.cc | 131 | ||||
| -rw-r--r-- | src/net/tcpserver.h | 51 | 
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__  | 
