Commits

Christian Fischer  committed 0ab813c

resolving hostname for Socket-connections, connection callbacks, Engine::runOnMainThread

  • Participants
  • Parent commits 8e65d9c

Comments (0)

Files changed (9)

File src/base/wiesel/util/thread.h

 	{
 	friend void _thread_impl(Thread *thread);
 
-	protected:
+	public:
 		Thread();
 
 	public:

File src/core/wiesel/engine.cpp

 
 #include <wiesel/util/shared_object.h>
 #include <wiesel/util/log.h>
+#include <wiesel/util/thread.h>
 #include <wiesel/module.h>
 #include <wiesel/module_registry.h>
 
 
 
 Engine::Engine() {
+	mainthread			= new Thread();
+
 	exit_requested		= false;
 	application			= NULL;
 
 		now_t  = clock();
 		float dt = (float(now_t - last_t) / CLOCKS_PER_SEC);
 
+		// run all 'runOnMainThread' objects
+		if (run_once.empty() == false) {
+			mainthread->lock();
+
+			for(std::vector<IRunnable*>::iterator it=run_once.begin(); it!=run_once.end();) {
+				(*it)->run();
+				release(*it);
+
+				it = run_once.erase(it);
+			}
+
+			mainthread->unlock();
+		}
+
 		// run all updateable objects
 		for(int i=updateables.size(); --i>=0;) {
 			updateables.at(i)->update(dt);
 	return;
 }
 
+
+void Engine::runOnMainThread(IRunnable* runnable) {
+	mainthread->lock();
+
+	std::vector<IRunnable*>::iterator it = std::find(run_once.begin(), run_once.end(), runnable);
+	if (it == run_once.end()) {
+		run_once.push_back(keep(runnable));
+	}
+
+	mainthread->unlock();
+}
+

File src/core/wiesel/engine.h

 
 	class DataSource;
 	class FileSystem;
+	class IRunnable;
+	class Thread;
 	class TouchHandler;
 
 
 		 */
 		void unregisterUpdateable(IUpdateable *updateable);
 
+		/**
+		 * @brief Registers a task, which will be executed on the main thread.
+		 * The task will be exewcuted only once and released after that.
+		 */
+		void runOnMainThread(IRunnable *runnable);
+
 	// static members
 	private:
 		static Engine					instance;
 	
 	// instance members
 	private:
+		ref<Thread>						mainthread;
+
+		std::vector<IRunnable*>			run_once;
 		std::vector<IUpdateable*>		updateables;
 
 		bool							exit_requested;

File src/net/wiesel/io/net/connection.cpp

 using namespace wiesel;
 
 
+ConnectionListener::ConnectionListener() {
+	return;
+}
+
+ConnectionListener::~ConnectionListener() {
+	return;
+}
+
+void ConnectionListener::onConnected(const std::string& address, Connection* connection) {
+	return;
+}
+
+void ConnectionListener::onConnectionFailed(const std::string& address) {
+	return;
+}
+
+void ConnectionListener::onDisconnected(const std::string& address, Connection* connection) {
+	return;
+}
+
+
+
 Connection::Connection() {
 }
 
 
 	return NULL;
 }
+
+
+void Connection::setCurrentAddress(const std::string& address) {
+	this->address = address;
+}
+
+
+void Connection::fireOnConnected() {
+	for(Listeners::const_iterator it=listeners_begin(); it!=listeners_end(); it++) {
+		(*it)->onConnected(getAddress(), this);
+	}
+
+	return;
+}
+
+void Connection::fireOnConnectionFailed() {
+	for(Listeners::const_iterator it=listeners_begin(); it!=listeners_end(); it++) {
+		(*it)->onConnectionFailed(getAddress());
+	}
+
+	return;
+}
+
+void Connection::fireOnDisconnected() {
+	for(Listeners::const_iterator it=listeners_begin(); it!=listeners_end(); it++) {
+		(*it)->onDisconnected(getAddress(), this);
+	}
+
+	return;
+}

File src/net/wiesel/io/net/connection.h

 #define	__WIESEL_IO_NET_CONNECTION_H__
 
 #include <wiesel/util/shared_object.h>
+#include <wiesel/util/listener_support.h>
 #include <wiesel/wiesel-net.def>
 
 #include <string>
 
 namespace wiesel {
 
-	class WIESEL_NET_EXPORT Connection : public virtual SharedObject
+	class Connection;
+
+
+	/**
+	 * @brief A listener class receiving connection events.
+	 */
+	class WIESEL_NET_EXPORT ConnectionListener : public virtual SharedObject
+	{
+	public:
+		ConnectionListener();
+		virtual ~ConnectionListener();
+
+	public:
+		/**
+		 * @brief Notification when the connection was successfully created.
+		 * @param address		The address of the connection.
+		 * @param connection	The created connection.
+		 */
+		virtual void onConnected(const std::string& address, Connection* connection);
+
+		/**
+		 * @brief The connection couldn't be created successfully.
+		 * @param address		The address of the connection.
+		 */
+		virtual void onConnectionFailed(const std::string& address);
+
+		/**
+		 * @brief The connection was closed.
+		 * @param address		The address of the connection.
+		 * @param connection	The connection which was closed.
+		 */
+		virtual void onDisconnected(const std::string& address, Connection* connection);
+	};
+
+
+
+	/**
+	 * @brief Baseclass for all network connections.
+	 */
+	class WIESEL_NET_EXPORT Connection :
+			public virtual SharedObject,
+			public ListenerSupport<ConnectionListener>
 	{
 	protected:
 		Connection();
 		static Connection* createConnection(const std::string& address);
 
 	public:
+		/// alias type for data sent via network.
 		typedef uint8_t data_t;
 
 	public:
+		/**
+		 * @brief Checks whether this connection is connected or not.
+		 */
 		virtual bool isConnected() const = 0;
 
+		/**
+		 * @brief Disconnect this connection.
+		 */
 		virtual void disconnect() = 0;
 
+		/**
+		 * @brief Send the given data over network.
+		 * This function will block until all data was sent.
+		 * @param data	Pointer to the data to be sent.
+		 * @param size	The size of the data.
+		 * @return \c true when all data was sent successfully.
+		 */
 		virtual bool send(const data_t* data, size_t size) = 0;
 
+		/**
+		 * @brief Read data from the network connection.
+		 * @param ptr	Pointer to memory where to store the result.
+		 * @param size	Size of the data to be read.
+		 * @return \c The number of bytes which were read successfully.
+		 */
 		virtual int read(data_t *ptr, size_t size) = 0;
 
+	protected:
+		/**
+		 * @brief Set the current address of this connection.
+		 * This should be used by subclasses only, when trying to establish a connection.
+		 */
+		void setCurrentAddress(const std::string& address);
+
+	public:
+		/**
+		 * @brief Get the address of this connection.
+		 */
+		const std::string& getAddress() const {
+			return address;
+		}
+
+	protected:
+		void fireOnConnected();
+		void fireOnConnectionFailed();
+		void fireOnDisconnected();
+
 	private:
-
+		std::string address;
 	};
 
 }

File src/net/wiesel/io/net/message_dispatcher.cpp

  */
 #include "message_dispatcher.h"
 #include "wiesel/util/log.h"
+#include "wiesel/engine.h"
 
 
 using namespace wiesel;
 
 
 
+enum ConnectionEvent {
+	ConnectionEvent_Connected,
+	ConnectionEvent_ConnectionFailed,
+	ConnectionEvent_Disconnected,
+};
+
+
+class ConnectionEventDispatcher : public IRunnable
+{
+public:
+	ConnectionEventDispatcher(
+			MessageDispatcher::Listeners	listeners,
+			ConnectionEvent					event,
+			std::string						address,
+			Connection*						connection
+	) :
+		listeners(listeners),
+		event(event),
+		address(address),
+		connection(connection)
+	{
+		return;
+	}
+
+	virtual ~ConnectionEventDispatcher() {
+		return;
+	}
+
+public:
+	virtual void run() {
+		switch(event) {
+			case ConnectionEvent_Connected: {
+				for(MessageDispatcher::Listeners::iterator it=listeners.begin(); it!=listeners.end(); it++) {
+					(*it)->onConnected(address, connection);
+				}
+
+				break;
+			}
+
+			case ConnectionEvent_ConnectionFailed: {
+				for(MessageDispatcher::Listeners::iterator it=listeners.begin(); it!=listeners.end(); it++) {
+					(*it)->onConnectionFailed(address);
+				}
+
+				break;
+			}
+
+			case ConnectionEvent_Disconnected: {
+				for(MessageDispatcher::Listeners::iterator it=listeners.begin(); it!=listeners.end(); it++) {
+					(*it)->onDisconnected(address, connection);
+				}
+
+				break;
+			}
+		}
+
+		return;
+	}
+
+private:
+	MessageDispatcher::Listeners	listeners;
+	ConnectionEvent					event;
+	std::string						address;
+	ref<Connection>					connection;
+};
+
+
+
+
 MessageDispatcher::IMessage::IMessage() {
 	return;
 }
 	do {
 		// delete connection, when interrupted
 		if (connection && (connection->isConnected() == false)) {
+			Engine::getInstance()->runOnMainThread(new ConnectionEventDispatcher(
+					*(this->getListeners()),
+					ConnectionEvent_Disconnected,
+					this->address,
+					connection
+			));
+
+			// release the object (we're creating a new one)
 			safe_release(connection);
 
 			// stop here, when no reconnect-flag is set
 		// try to reconnect, if neccessary
 		if (connection == NULL) {
 			connection = keep(Connection::createConnection(this->address));
+
+			// notify, when the connection was successful
+			if (connection) {
+				Engine::getInstance()->runOnMainThread(new ConnectionEventDispatcher(
+						*(this->getListeners()),
+						ConnectionEvent_Connected,
+						this->address,
+						connection
+				));
+			}
+			else {
+				Engine::getInstance()->runOnMainThread(new ConnectionEventDispatcher(
+						*(this->getListeners()),
+						ConnectionEvent_ConnectionFailed,
+						this->address,
+						NULL
+				));
+			}
 		}
 
 		// when the connection is OK, try to fetch messages

File src/net/wiesel/io/net/message_dispatcher.h

 	 */
 	class WIESEL_NET_EXPORT MessageDispatcher :
 			public virtual SharedObject,
+			public ListenerSupport<ConnectionListener>,
 			protected IRunnable
 	{
 	public:

File src/net/wiesel/io/net/socket_connection.cpp

 #if WIESEL_PLATFORM_UNIX
 #include <sys/socket.h>
 #include <arpa/inet.h>
+#include <netdb.h>
 #endif
 
 #if WIESEL_PLATFORM_WINDOWS
 }
 
 bool SocketConnection::connect(const std::string& host, uint32_t port) {
-	int result;
+	bool connected = false;
+	
+	// check if there's already an existing connection
+	assert(isConnected() == false);
+	if (isConnected()) {
+		return false;
+	}
+
+	// set the current address (the address should be updated, even if no connection could be established).
+	{
+		std::stringstream ss;
+		ss << host;
+		ss << ':';
+		ss << port;
+
+		setCurrentAddress(ss.str());
+	}
 
 	socket_handle = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 	if (socket_handle == -1) {
 		return false;
 	}
 
-	sockaddr_in addr;
-	addr.sin_family			= AF_INET;
-	addr.sin_port			= htons(static_cast<uint16_t>(port));
-	addr.sin_addr.s_addr	= inet_addr(host.c_str());
-
-	if (addr.sin_addr.s_addr == INADDR_NONE) {
+	hostent* he = gethostbyname(host.c_str());
+	if (he == NULL) {
 		return false;
 	}
 
-	if (addr.sin_addr.s_addr == INADDR_ANY) {
-		return false;
+	sockaddr_in addr;
+	addr.sin_family		= he->h_addrtype;
+	addr.sin_port		= htons(static_cast<uint16_t>(port));
+
+	for(char **p=he->h_addr_list; *p; ++p) {
+		addr.sin_addr	= *reinterpret_cast<in_addr*>(*p);
+
+		if (addr.sin_addr.s_addr == INADDR_NONE) {
+			continue;
+		}
+
+		if (addr.sin_addr.s_addr == INADDR_ANY) {
+			continue;
+		}
+
+		int result = ::connect(socket_handle, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
+		if (result == -1) {
+			disconnect();
+			continue;
+		}
+
+		// connection successful
+		connected = true;
+
+		break;
 	}
 
-	result = ::connect(socket_handle, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
-	if (result == -1) {
-		disconnect();
-		return false;
+	// release the socket, when not connected
+	if (!connected) {
+		releaseSocket();
 	}
 
-	return true;
+	// fire notification
+	if (connected) {
+		fireOnConnected();
+	}
+	else {
+		fireOnConnectionFailed();
+	}
+
+	return connected;
 }
 
 
 
 void SocketConnection::disconnect() {
 	if (socket_handle != -1) {
-		#if WIESEL_PLATFORM_UNIX
-			close(socket_handle);
-		#endif
-
-		#if WIESEL_PLATFORM_WINDOWS
-			closesocket(socket_handle);
-		#endif
-
-		socket_handle = -1;
+		releaseSocket();
+		fireOnDisconnected();
 	}
 
 	return;
 
 
 int SocketConnection::read(data_t *ptr, size_t size) {
-	int result = ::recv(socket_handle, (char*)ptr, 1, 0);
+	int result = ::recv(socket_handle, (char*)ptr, size, 0);
+	if (result <= 0) {
+		disconnect();
+	}
+
 	return result;
 }
 
 
+void SocketConnection::releaseSocket() {
+	if (socket_handle != -1) {
+		#if WIESEL_PLATFORM_UNIX
+			close(socket_handle);
+		#endif
+
+		#if WIESEL_PLATFORM_WINDOWS
+			closesocket(socket_handle);
+		#endif
+
+		socket_handle = -1;
+	}
+
+	return;
+}
+

File src/net/wiesel/io/net/socket_connection.h

 
 
 
+	/**
+	 * @brief Connection class to connect a socket to a host and port.
+	 */
 	class WIESEL_NET_EXPORT SocketConnection : public Connection
 	{
 	public:
 		virtual ~SocketConnection();
 
 	public:
+		/**
+		 * @brief Try to connect to a remote host.
+		 * @param host	The remote host.
+		 * @param port	The port of the host to connect to.
+		 * @return \c true on success.
+		 */
 		virtual bool connect(const std::string& host, uint32_t port);
 
+	public:
 		virtual bool isConnected() const;
 
 		virtual void disconnect();
 		virtual int read(data_t *ptr, size_t size);
 
 	private:
+		void releaseSocket();
+
+	private:
 		int socket_handle;
 	};