Skip to content

Commit

Permalink
Use persistent connection to server
Browse files Browse the repository at this point in the history
To speed up file operations when a remote server is in use, the
connection to that server is now held open and reused, rather than a
new connection being created for every file operation.
  • Loading branch information
Colin Ward authored and hitman-codehq committed Jul 5, 2024
1 parent 61a884c commit 5c0d288
Show file tree
Hide file tree
Showing 14 changed files with 319 additions and 102 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ if(${Qt6_FOUND} EQUAL 1)

set(SourceFiles ${SourceFiles} "StdApplication.cpp")
set(SourceFiles ${SourceFiles} "RemoteDir.cpp")
set(SourceFiles ${SourceFiles} "RemoteFactory.cpp")
set(SourceFiles ${SourceFiles} "RemoteFile.cpp")
set(SourceFiles ${SourceFiles} "RemoteFileUtils.cpp")
set(SourceFiles ${SourceFiles} "StdCharConverter.cpp")
Expand Down
15 changes: 7 additions & 8 deletions RemoteDir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,15 @@ int RRemoteDir::open(const char *a_pattern)
{
(void) a_pattern;

RSocket socket;
int retVal = socket.open(m_remoteFactory->getServer().c_str(), m_remoteFactory->getPort());
int retVal = KErrNone;

if (retVal == KErrNone)
if (m_socket->isOpen())
{
CDir *handler = nullptr;

try
{
socket.write(g_signature, SIGNATURE_SIZE);

handler = new CDir(&socket, a_pattern);
handler = new CDir(m_socket, a_pattern);
handler->sendRequest();

if (handler->getResponse()->m_result == KErrNone)
Expand Down Expand Up @@ -110,11 +107,13 @@ int RRemoteDir::open(const char *a_pattern)
}

delete handler;
socket.close();
}
else
{
Utils::info("RRemoteDir::open() => Cannot connect to %s (%d)", m_remoteFactory->getServer().c_str(), retVal);
Utils::info("RRemoteDir::open() => Connection to server \"%s:%d\" is not open", m_remoteFactory->getServer().c_str(),
m_remoteFactory->getPort());

retVal = KErrNotOpen;
}

return retVal;
Expand Down
9 changes: 7 additions & 2 deletions RemoteDir.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,21 @@ class RRemoteFactory;
class RRemoteDir : public RDirObject
{
RRemoteFactory *m_remoteFactory; /**< Pointer to the remote factory that owns this instance */
RSocket *m_socket; /**< Socket for communicating with remote RADRunner */

public:

RRemoteDir() : m_remoteFactory(nullptr) { }
RRemoteDir() : m_remoteFactory(nullptr), m_socket(nullptr) { }

int open(const char *a_pattern);

int read(TEntryArray *&a_entries, enum TDirSortOrder a_sortOrder = EDirSortNone);

void setFactory(RRemoteFactory *a_remoteFactory) { m_remoteFactory = a_remoteFactory; }
void setFactory(RRemoteFactory *a_remoteFactory, RSocket *a_socket)
{
m_remoteFactory = a_remoteFactory;
m_socket = a_socket;
}
};

#endif /* ! REMOTEDIR_H */
210 changes: 210 additions & 0 deletions RemoteFactory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@

#include "StdFuncs.h"
#include "RemoteFactory.h"

#if defined(__unix__) || defined(__amigaos__)

#include <fcntl.h>
#include <sys/socket.h>

#else /* ! defined(__unix__) || defined(__amigaos__) */

#include <ws2tcpip.h>

#endif /* ! defined(__unix__) || defined(__amigaos__) */

/**
* Gets an object for directory manipulation.
* Returns an instance of a class derived from RDirObject which can be used to manipulate directories, either
* locally or remotely, depending on how the factory was configured.
*
* @date Saturday 25-May-2024 8:27 am, Code HQ Tokyo Tsukuda
* @return A reference to an RDirObject derived object
*/

RDirObject &RRemoteFactory::getDirObject()
{
if (isRemote())
{
checkConnection();
m_remoteDir.setFactory(this, &m_socket);
return m_remoteDir;
}
else
{
return m_dir;
}
}

/**
* Gets an object for file manipulation.
* Returns an instance of a class derived from RFileObject which can be used to manipulate files, either
* locally or remotely, depending on how the factory was configured.
*
* @date Saturday 25-May-2024 8:27 am, Code HQ Tokyo Tsukuda
* @return A reference to an RFileObject derived object
*/

RFileObject &RRemoteFactory::getFileObject()
{
if (isRemote())
{
checkConnection();
m_remoteFile.setFactory(this, &m_socket);
return m_remoteFile;
}
else
{
return m_file;
}
}

/**
* Gets an object for file system manipulation.
* Returns an instance of a class derived from RFileUtilsObject which can be used to manipulate file system
* objects, either locally or remotely, depending on how the factory was configured.
*
* @date Saturday 25-May-2024 8:27 am, Code HQ Tokyo Tsukuda
* @return A reference to an RFileUtilsObject derived object
*/

RFileUtilsObject &RRemoteFactory::getFileUtilsObject()
{
if (isRemote())
{
checkConnection();
m_remoteFileUtils.setFactory(this, &m_socket);
return m_remoteFileUtils;
}
else
{
return m_fileUtils;
}
}

/**
* Open a connection to a remote RADRunner instance.
* This function can be called any time a connection to RADRunner needs to be made. It can be called either
* by client code at startup, or The Framework will do it automatically if a file request is made and it is
* detected that the connection has been lost.
*
* @pre Server and port must have already been set
*
* @date Saturday 11-May-2024 9:14 am, Enoshima holiday apartment
* @return KErrNone if successful, otherwise one of the errors returned by RSocket::open()
*/

int RRemoteFactory::openRemote()
{
int retVal = m_socket.open(getServer().c_str(), getPort());

if (retVal == KErrNone)
{
try
{
m_socket.write(g_signature, SIGNATURE_SIZE);
}
catch (RSocket::Error &a_exception)
{
Utils::info("RRemoteFactory::openRemote() => Unable to perform I/O on socket (Error = %d)", a_exception.m_result);
}
}

return retVal;
}

/**
* Close the remote factory.
* Closing the remote factory will also close any connected sockets.
*
* @date Saturday 11-May-2024 9:16 am, Enoshima holiday apartment
*/

void RRemoteFactory::close()
{
m_socket.close();
}

/**
* Check connection and reopen if necessary.
* This method is a part of the self-healing functionality of The Framework's socket system. It can be called at
* any time, whether the socket has been connected previously or not. If the socket is closed, it will be opened.
* If the socket is open, it will be checked to see if it is still connected. If it is not, it will be reconnected.
*
* @pre Server and port must have already been set
*
* @date Tuesday 14-May-2024 6:02 am, Code HQ Tokyo Tsukuda
* @return KErrNone if successful, otherwise one of the errors returned by RRemoteFactory::openRemote()
*/

// TODO: CAW - Move this stuff into RSocket?
int RRemoteFactory::checkConnection()
{
int retVal = KErrNone;

/* If the socket is open, it could be that the connection has been lost, so we'll check that by setting */
/* the socket to non-blocking and trying to read a byte. If it fails, it means the connection is closed. */
/* This trick depends on the remote server not sending data without being requested to */
if (m_socket.isOpen())
{
int result = 0;

/* Error handling is difficult here, as it is unlikely to fail unless an invalid socket is used, and if */
/* it does fail, there is not much we can do as this is supposed to be a transparent self-healing routine. */
/* So we'll check for errors and handle but not report them, and if the final call to set the socket back */
/* to blocking mode fails, all hell will break loose, but so be it */

#ifdef WIN32

u_long blocking = 1;

if (ioctlsocket(m_socket.m_socket, FIONBIO, &blocking) == 0)
{
char buffer[1];
result = recv(m_socket.m_socket, buffer, 1, 0);

blocking = 0;

if (ioctlsocket(m_socket.m_socket, FIONBIO, &blocking) != 0)
{
result = -1;
}
}

#else /* ! WIN32 */

int flags = fcntl(m_socket.m_socket, F_GETFL, 0);

if (flags != -1)
{
if (fcntl(m_socket.m_socket, F_SETFL, flags | O_NONBLOCK) != -1)
{
char buffer[1];
result = recv(m_socket.m_socket, buffer, 1, 0);

if (result != -1)
{
if (fcntl(m_socket.m_socket, F_SETFL, flags) == -1)
{
result = -1;
}
}
}
}

#endif /* ! WIN32 */

if (result == 0)
{
close();

retVal = openRemote();
}
}
else
{
retVal = openRemote();
}

return retVal;
}
50 changes: 14 additions & 36 deletions RemoteFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "RemoteDir.h"
#include "RemoteFile.h"
#include "RemoteFileUtils.h"
#include "Yggdrasil/Commands.h"

/**
* A class for management of file system related objects.
Expand All @@ -26,51 +27,25 @@ class RRemoteFactory
RRemoteFile m_remoteFile; /**< Class for remote file access */
RFileUtils m_fileUtils; /**< Class for local file system manipulation */
RRemoteFileUtils m_remoteFileUtils; /**< Class for remote file system manipulation */
RSocket m_socket; /**< Socket for communicating with remote RADRunner */
std::string m_serverName; /**< The host name of the instance of RADRunner to use */
unsigned short m_serverPort; /**< The port on which RADRunner is listening */

public:

RRemoteFactory() : m_useLocal(false) { }

RDirObject &getDirObject()
{
if (isRemote())
{
m_remoteDir.setFactory(this);
return m_remoteDir;
}
else
{
return m_dir;
}
}
int openRemote();

RFileObject &getFileObject()
{
if (isRemote())
{
m_remoteFile.setFactory(this);
return m_remoteFile;
}
else
{
return m_file;
}
}
void close();

RFileUtilsObject &getFileUtilsObject()
{
if (isRemote())
{
m_remoteFileUtils.setFactory(this);
return m_remoteFileUtils;
}
else
{
return m_fileUtils;
}
}
int checkConnection();

RDirObject &getDirObject();

RFileObject &getFileObject();

RFileUtilsObject &getFileUtilsObject();

bool isRemote()
{
Expand All @@ -87,6 +62,9 @@ class RRemoteFactory
m_serverPort = a_port;
}

/**< Set this to true when the RRemoteFactory instance is configured to be used for remote
* access, but there is a need to temporarily access the local file system
*/
void useLocal(bool a_useLocal)
{
m_useLocal = a_useLocal;
Expand Down
Loading

0 comments on commit 5c0d288

Please sign in to comment.