rosaria/Legacy/Aria/ArNetworking/include/ArClientBase.h

425 lines
16 KiB
C
Raw Permalink Normal View History

2021-12-16 15:07:59 +01:00
#ifndef NLCLIENTBASE_H
#define NLCLIENTBASE_H
#include "Aria.h"
#include "ArClientCommands.h"
#include "ArServerCommands.h"
#include "ArNetPacket.h"
#include "ArNetPacketSenderTcp.h"
#include "ArNetPacketReceiverTcp.h"
#include "ArNetPacketReceiverUdp.h"
#include "ArClientData.h"
/**
@brief The base client class
You need to connect a client to a server using blockingConnect() with
the address for the server. Then you should add handlers to
the client to receive information from the server (with addHandler(),
request(), and requestOnce()). Then call run() or runAsync()
(or loopOnce() if you really know what you're doing).
You can also add a callback that will get called every cycle of the
client.
User and password information may be required by the server.
For details on that see ArServerBase. The most important thing is
the modification to blockingConnect(), it can now take a user and
password argument and upon failure you'll want to check wasRejected()
to determine whether the client couldn't find the server or if the user and
password were rejected. Another relevant function is setServerKey()
to set the key (string of text) we need to connect to the server.
This class should be thread safe... The only thing you cannot do is
call 'addHandler' or 'remHandler' from within a handler for that
specific piece of data.
**/
class ArClientBase : public ArASyncTask
{
public:
/// The state of the connection
enum ClientState {
STATE_NO_CONNECTION, ///< The client has not connected
STATE_FAILED_CONNECTION, ///< The client tried to connect and failed
STATE_OPENED_SOCKET, ///< Client opened socket, waiting for intro from srvr
STATE_EXCHANGED_INTROS, ///< Client and server have exchanged introductions
STATE_REJECTED, ///< Client was rejected by server
STATE_WAITING_LIST, ///< Client was connected to server, waiting for list
STATE_CONNECTED, ///< Client is connected to server
STATE_LOST_CONNECTION ///< Client lost connection to server
};
enum {
CLIENT_KEY_LENGTH = 16
};
enum NonBlockingConnectReturn
{
NON_BLOCKING_CONTINUE, ///< Client didn't connect or fail to connect yet
NON_BLOCKING_CONNECTED, ///< Client connected
NON_BLOCKING_FAILED ///< Client couldn't connect
};
/// Constructor
AREXPORT ArClientBase();
/// Destructor
AREXPORT virtual ~ArClientBase();
/// Sets the optional name of the connected robot for debugging purposes.
AREXPORT virtual void setRobotName(const char *name,
bool debugLogging = false,
int robotId = 0);
/// Returns the optional name of the connected robot for debugging purposes.
AREXPORT virtual const char *getRobotName() const;
/// Returns the entire string used to prefix log entries for this client base.
AREXPORT virtual const char *getLogPrefix() const;
/// Returns if we're doing debug logging or not
AREXPORT bool getDebugLogging(void);
/// Connect to a server
AREXPORT bool blockingConnect(const char *host, int port, bool log = true,
const char *user = NULL,
const char *password = NULL,
const char *openOnIP = NULL);
/// Disconnect from a server
/**
* This method blocks while the connection is being shutdown.
**/
AREXPORT bool disconnect(void);
/// Disconnect from a server next time through the loop
/**
* This method does not block while the connection is being
* shutdown, and the disconnect only happens the next time the
* client base is in its loop
**/
AREXPORT void disconnectSoon(void);
/** @return true if we're connected to a server */
AREXPORT bool isConnected(void) { return myState == STATE_CONNECTED;}
/** @return true if a server connection attempt failed because the server rejected the username or password, false if the connection failed for another reason, or the username/password were accepted. */
AREXPORT bool wasRejected(void) { return myState == STATE_REJECTED; }
/// Gets the state of the client
AREXPORT ClientState getState(void) { return myState; }
/// Adds a functor for some particular data
AREXPORT bool addHandler(const char *name,
ArFunctor1 <ArNetPacket *> *functor);
/// Removes a functor for some particular data by name
AREXPORT bool remHandler(const char *name, ArFunctor1<ArNetPacket *> *functor);
/// Request some data every @a mSec milliseconds
AREXPORT bool request(const char *name, long mSec,
ArNetPacket *packet = NULL);
/// Don't want this data anymore
AREXPORT bool requestStop(const char *name);
/// Request some data (or send a command) just once
AREXPORT bool requestOnce(const char *name,
ArNetPacket *packet = NULL,
bool quiet = false);
/// Request some data (or send a command) just once by UDP
AREXPORT bool requestOnceUdp(const char *name,
ArNetPacket *packet = NULL,
bool quiet = false);
/// Request some data (or send a command) just once with a string as argument
AREXPORT bool requestOnceWithString(const char *name, const char *str);
/// Sees if this data exists
AREXPORT bool dataExists(const char *name);
/// Gets the name of the host we tried to connect to
AREXPORT const char *getHost(void);
/// Sets the 'key' needed to connect to the server
AREXPORT void setServerKey(const char *serverKey, bool log = true);
/// Enforces the that the server is using this protocol version
AREXPORT void enforceProtocolVersion(const char *protocolVersion,
bool log = true);
/// Enforces that the robots that connect are this type
AREXPORT void enforceType(ArServerCommands::Type type, bool log = true);
/// Gets the last time a packet was received
AREXPORT ArTime getLastPacketReceived(void);
/// Sets the backup timeout
AREXPORT void setBackupTimeout(double timeoutInMins);
/// Runs the client in this thread
AREXPORT virtual void run(void);
/// Runs the client in its own thread
AREXPORT virtual void runAsync(void);
/// Stops the thread and sets the stopped flag
AREXPORT virtual void stopRunning(void);
/// Returns whether the client has been stopped (regardless of whether its in its own thread)
AREXPORT virtual bool isStopped();
/// Print out or data with descriptions
AREXPORT void logDataList(void);
/// Adds a functor to call every cycle
AREXPORT void addCycleCallback(ArFunctor *functor);
/// Removes a functor called every cycle
AREXPORT void remCycleCallback(ArFunctor *functor);
/// Send a packet over TCP
AREXPORT bool sendPacketTcp(ArNetPacket *packet);
/// Send a packet over UDP (unless client only wants tcp then sends over tcp)
AREXPORT bool sendPacketUdp(ArNetPacket *packet);
/// Sets the time to allow for connection (default 3)
AREXPORT void setConnectTimeoutTime(int sec);
/// Gets the time allowed for connection
AREXPORT int getConnectTimeoutTime(void);
/// Logs the tracking information (packet and byte counts)
AREXPORT void logTracking(bool terse);
/// Clears the tracking information (resets counters)
AREXPORT void resetTracking(void);
/// Adds a call for when the server shuts down
AREXPORT void addServerShutdownCB(ArFunctor *functor,
ArListPos::Pos position = ArListPos::LAST);
/// Removes a call for when the server shuts down
AREXPORT void remServerShutdownCB(ArFunctor *functor);
/// Adds a call for when a disconnection has occured because of error
AREXPORT void addDisconnectOnErrorCB(ArFunctor *functor,
ArListPos::Pos position = ArListPos::LAST);
/// Removes a call for when a disconnection has occured because of error
AREXPORT void remDisconnectOnErrorCB(ArFunctor *functor);
/// Run the loop once
AREXPORT void loopOnce(void);
/// Process the packet wherever it came from
AREXPORT void processPacket(ArNetPacket *packet, bool tcp);
/// Process a packet from udp (just hands off to processPacket)
AREXPORT void processPacketUdp(ArNetPacket *packet,
struct sockaddr_in *sin);
/// Sets it so we only get TCP data from the server not UDP
AREXPORT void setTcpOnlyFromServer(void);
/// Sets it so we only send TCP data to the server
AREXPORT void setTcpOnlyToServer(void);
/// Returns whether we only get TCP data from the server not UDP
AREXPORT bool isTcpOnlyFromServer(void);
/// Returns whether we only send TCP data to the server
AREXPORT bool isTcpOnlyToServer(void);
/// Gets a (probably) unique key that can be used to identify the client (after connection)
AREXPORT void getClientKey(unsigned char key[CLIENT_KEY_LENGTH]);
/// Starts the process of disconnecting from the server.
/**
* This method should be called in threads that should not block
* (such as the main GUI thread). The call should be followed by
* a delay of one second, followed by a call to
* finishNonBlockingDisconnect().
**/
AREXPORT bool startNonBlockingDisconnect();
/// Finishes the process of disconnecting from the server.
/**
* This method should be used in conjunction with
* startNonBlockingDisconnect in threads that should not block
* (such as the main GUI thread).
**/
AREXPORT void finishNonBlockingDisconnect();
/// Gets the name of the data a packet is for
AREXPORT const char *getName(ArNetPacket *packet, bool internalCall = false);
/// Gets the name of the data a command is
AREXPORT const char *getName(unsigned int command, bool internalCall = false);
// the function for the thread
AREXPORT virtual void * runThread(void *arg);
/// Internal function to get the socket (no one should need this)
AREXPORT struct in_addr *getTcpAddr(void) { return myTcpSocket.inAddr(); }
/// Internal function that'll do the connection (mainly for switch connections)
AREXPORT bool internalBlockingConnect(
const char *host, int port, bool log, const char *user,
const char *password, ArSocket *tcpSocket,
const char *openOnIP = NULL);
/// Internal function that'll start the non blocking connection (mainly for switch connections)
AREXPORT NonBlockingConnectReturn internalNonBlockingConnectStart(
const char *host, int port, bool log, const char *user,
const char *password, ArSocket *tcpSocket,
const char *openOnIP = NULL);
/// Internal function that'll start the non blocking connection (mainly for switch connections)
AREXPORT NonBlockingConnectReturn internalNonBlockingConnectContinue(void);
/// Internal function to get the tcp socket
AREXPORT ArSocket *getTcpSocket(void) { return &myTcpSocket; }
/// Internal function to get the udp socket
AREXPORT ArSocket *getUdpSocket(void) { return &myUdpSocket; }
/// Internal function get get the data map
AREXPORT const std::map<unsigned int, ArClientData *> *getDataMap(void)
{ return &myIntDataMap; }
/// Returns the command number from the name
AREXPORT unsigned int findCommandFromName(const char *name);
/// Request some data every mSec milliseconds by command not name
AREXPORT bool requestByCommand(unsigned int command, long mSec,
ArNetPacket *packet = NULL);
/// Don't want this data anymore, by command not name
AREXPORT bool requestStopByCommand(unsigned int command);
/// Request some data (or send a command) just once by command not name
AREXPORT bool requestOnceByCommand(unsigned int command,
ArNetPacket *packet = NULL);
/// Request some data (or send a command) just once by command not name
AREXPORT bool requestOnceByCommandUdp(unsigned int command,
ArNetPacket *packet = NULL);
/// Gets if we received the list of commands
bool getReceivedDataList(void) { return myReceivedDataList; }
/// Gets if we received the list of arguments and
bool getReceivedArgRetList(void) { return myReceivedArgRetList; }
/// Gets if we received the list of commands
bool getReceivedGroupAndFlagsList(void) { return myReceivedGroupAndFlagsList; }
/// Tells us the reason the connection was rejected (see ArServerCommands for details)
int getRejected(void) { return myRejected; }
/// Tells us the reason the connection was rejected (see ArServerCommands for details)
const char *getRejectedString(void) { return myRejectedString; }
protected:
/// Optional robot name for logging
std::string myRobotName;
/// Optional prefix to be inserted at the start of log messages
std::string myLogPrefix;
/// If we're enforcing the version of the protocol or not
std::string myEnforceProtocolVersion;
/// if we're a particular type
ArServerCommands::Type myEnforceType;
AREXPORT bool setupPacket(ArNetPacket *packet);
ArTime myLastPacketReceived;
std::list<ArFunctor *> myServerShutdownCBList;
std::list<ArFunctor *> myDisconnectOnErrorCBList;
std::list<ArFunctor *> myCycleCallbacks;
void clear(void);
// does the first part of connection
bool internalConnect(const char *host, int port, bool obsolete,
const char *openOnIP);
void internalStartUdp(void);
void buildList(ArNetPacket *packet);
void internalSwitchState(ClientState state);
bool myReceivedDataList;
bool myReceivedArgRetList;
bool myReceivedGroupAndFlagsList;
ClientState myState;
ArTime myStateStarted;
bool myUdpConfirmedFrom;
bool myUdpConfirmedTo;
// if we only send tcp
bool myTcpOnlyTo;
// if we only receive tcp from the server
bool myTcpOnlyFrom;
bool myIsRunningAsync;
bool myDisconnectSoon;
bool myIsStartedDisconnect;
bool myIsStopped;
bool myQuiet;
bool myDebugLogging;
ArLog::LogLevel myVerboseLogLevel;
std::string myHost;
std::string myUser;
std::string myPassword;
// the time we allow for connections
int myTimeoutTime;
// the time we started our connection
ArTime myStartedConnection;
ArTime myStartedUdpConnection;
ArTime myStartedShutdown;
enum NonBlockingConnectState
{
NON_BLOCKING_STATE_NONE,
NON_BLOCKING_STATE_TCP,
NON_BLOCKING_STATE_UDP
};
NonBlockingConnectState myNonBlockingConnectState;
ArMutex myDataMutex;
ArMutex myClientMutex;
ArMutex myMapsMutex;
ArMutex myStateMutex;
ArMutex myCallbackMutex;
ArMutex myCycleCallbackMutex;
ArMutex myPacketTrackingMutex;
// our map of names to ints
std::map<std::string, unsigned int> myNameIntMap;
// our map of ints to functors
std::map<unsigned int, ArClientData *> myIntDataMap;
struct sockaddr_in myUdpSin;
bool myUdpSinValid;
// the port the server said it was using
unsigned int myServerReportedUdpPort;
// the port the server actually is using
unsigned int myServerSentUdpPort;
unsigned int myUdpPort;
long myAuthKey;
long myIntroKey;
std::string myServerKey;
double myBackupTimeout;
// this is a key we have for identifying ourselves moderately uniquely
unsigned char myClientKey[16];
ArNetPacketSenderTcp myTcpSender;
ArNetPacketReceiverTcp myTcpReceiver;
ArNetPacketReceiverUdp myUdpReceiver;
ArSocket myTcpSocket;
ArSocket myUdpSocket;
ArFunctor2C<ArClientBase, ArNetPacket *, bool> myProcessPacketCB;
ArFunctor2C<ArClientBase, ArNetPacket *, struct sockaddr_in *> myProcessPacketUdpCB;
int myRejected;
char myRejectedString[32000];
ArTime myTrackingStarted;
class Tracker
{
public:
Tracker() { reset(); }
virtual ~Tracker() {}
void reset(void)
{ myPacketsTcp = 0; myBytesTcp = 0; myPacketsUdp = 0; myBytesUdp = 0; }
long myPacketsTcp;
long myBytesTcp;
long myPacketsUdp;
long myBytesUdp;
};
AREXPORT void trackPacketSent(ArNetPacket *packet, bool tcp);
AREXPORT void trackPacketReceived(ArNetPacket *packet, bool tcp);
std::map<unsigned int, Tracker *> myTrackingSentMap;
std::map<unsigned int, Tracker *> myTrackingReceivedMap;
};
#endif // NLCLIENTBASE_H