#ifndef TCPMANAGER_H #define TCPMANAGER_H #include "TcpHandlers.h" #include "TcpBlockAllocator.h" #include "IPAddress.h" #include "Clock.h" #if defined(WIN32) #include typedef int socklen_t; #else // for non-windows platforms (linux) #include #include #include #include #include #include #include const int INVALID_SOCKET = 0xFFFFFFFF; const int SOCKET_ERROR = 0xFFFFFFFF; typedef int SOCKET; #endif #ifdef EXTERNAL_DISTRO namespace NAMESPACE { #endif class TcpConnection; struct ConnectionList { ConnectionList(TcpConnection *con, int id) : m_beginList(con), m_listID(id) {} TcpConnection *m_beginList; int m_listID; }; /** * @brief The purpose of the TcpManager is to manage a set of connections that are coming in on a particular port. * */ class TcpManager { public: /** @brief Parameters for the TcpManager. */ struct TcpParams { /** @brief Simple constructor sets default values for members. */ TcpParams(); /** @brief Simple copy constructor. */ TcpParams(const TcpParams &cpy); /** * @brief Connection port number. * * this is the port number that this manager will use for all incoming and outgoing data. On the client side * this is typically set to 0, which causes the manager object to randomly pick an available port. On the server * side, this port should be set to a specific value as it will represent the port number that clients will use * to connect to the server (ie. the listening port). It's generally a good idea to give the user on the client * side the option of fixing this port number at a specific value as well as it is often necessary for them to * do so in order to navigate company firewalls which may have specific port numbers open to them for this purpose. * default = 0 */ unsigned short port; /** * @ brief Server bind ip. * */ char bindAddress[64]; /** * @brief Maximum number of connections that can be established by this manager. * * this is the maximum number of connections that can be established by this manager, any incoming/outgoing connections * over this limit will be refused. On the client side, this typically only needs to be set to 1, though there * is little harm in setting this number larger. * default = 10 */ unsigned maxConnections; /** * @brief The size of the incoming socket buffer. * * The client will want to set this fairly small (32k or so), but the server * will want to set this fairly large (512k) * default = 64k */ unsigned incomingBufferSize; /** * @brief The size of the outgoing socket buffer. * * The client will want to set this fairly small (32k or so), but the server * will want to set this fairly large (512k) * default = 64k */ unsigned outgoingBufferSize; /** * @brief The block size of a single outgoing buffer memory allocator block. * * This param should allways be set at least as high as the maximum message size you * expect to send (performance will suffer otherwise). * default = 8K */ unsigned allocatorBlockSize; /** * @brief The number of block memory allocator 'blocks' created at a time. * * This is the number of blocks created for the buffer allocator for each * TcpConnection opened by this manager. Since the block size should be * the max size of an outgoing message, the recommended setting is: greater * than the number of concurrent connections you expect to normally have open. * default = 1024 */ unsigned allocatorBlockCount; /** * @brief The maximum size that a recvd message is allowed to be. * * Really only here for protection, not required. If you set this, you can safeguard * your client/server from receiving stray oversized messages. If a message on the socket * specifies it's length at larger than this value, then the message is not read, and the connection * is terminated. If the value is set to 0, then there is no max message size checking * on incoming messages (this will also cary a performance hit, since every new message * recieved will have to have a new buffer created if you don't specify a value here). Be careful * not to set this too small, if you have messages that could exceed the value you set here * they will be discarded, and the connection will be terminated without warning. * default = 0 */ unsigned maxRecvMessageSize; unsigned keepAliveDelay; unsigned noDataTimeout; }; /** * @brief */ TcpManager(const TcpParams ¶ms); /** * @brief Use to specify a handler object to receive callbacks. * * To have the TcpManager call your object directly when connection requests come in, you * simply need to derive your class (multiply if necessary) from TcpManagerHandler, then you can use * this method to set the object the TcpManager will call as appropriate. The TcpConnection object * also has a handler mechanism that replaces the other callback functions below, see TcpConnection::SetHandler * default = NULL (no callbacks made) * * @param handler The object which will be called for manager related notifications. */ void SetHandler(TcpManagerHandler *handler); /** * @brief This function MUST be called on a regular basis in order to give the manager object time to service the socket and give time to various connection objects that may need processing time, etc. * * @param maxTimeAcceptingConnections The max amount of time in milliseconds to spend accepting new client connections. * This parameter is only used if this manager has been bound as a server (bindAsServer). * If you set this param to 0, it will not attempt to accept any new connections. * * @param giveConnectionsTime * True if every connection opened on this manager is given time in this call, false if * no connections are given time. * * @param maxSendTimePerConnection Max amount of time in milliseconds to spend on each client processing outgoing messages. * A max of the specified amount of time will be spent on each and every individual connection. If you set * this parametrer to 0, it will not process any outgoing messages on any clients. Note also that when attempting * to establish new connections (via the EstablishConnection method), this parameter must be > 0 in order to * complete the connection process for any connections that were still negotiating. * * @param maxRecvTimePerConnection Max amount of time in milliseconds to spend on each client processing incoming messages. * A max of the specified amount of time will be spent on each and every individual connection. If you set * this param to 0, it will not process any incoming messages on any clients. * This is a good way to give the manager processing time for outgoing packets in situations * where the application does not want to have to worry about processing incoming packets. * * @return true if any incoming packets were processed during this time slice, otherwise returns false */ bool GiveTime(unsigned maxTimeAcceptingConnections = 5, unsigned maxSendTimePerConnection = 5, unsigned maxRecvTimePerConnection = 5); /** * @brief Used to establish a connection to a server that is listening at the specified address and port. * * The serverAddress will do a DNS lookup as appropriate. This call will block long enough to resolve * the DNS lookup, but then will return a TcpConnection object that will be in a StatusNegotiating * state until the connection is actually established. The application must give the manager * object time after calling EstablishConnection or else the negotiation process to establish the * connection will never have time to actually occur. Typically the client establishing the connection * will call EstablishConnection, then sit in a loop calling TcpManager::GiveTime and checking to see * if the status of the returned TcpConnection object is changed from StatusNegotiating. This allows * the application to look for the ESC key or timeout an attempted connection. * * @param serverAddress The address of the server to open a connection to. * * @param serverPort The port of the server to open a connection to. * * @param timeout How long to attempt connecting to the server (in milliseconds). * Setting the timeout value to something greater than 0 will cause the TcpConnection object to change * from a StatusNegotiating state to a StatusDisconnected state after the timeout has expired. It will also cause * the connect-complete callback to be called if the connection is succesfull. * * @return A pointer to a TcpConnection object. * NULL if the manager object has exceeded its maximum number of connections * or if the serverAddress cannot be resolved to an IP address. */ TcpConnection *EstablishConnection(const char *serverAddress, unsigned short serverPort, unsigned timeout = 0); /** * @brief Binds this manager as a server which will listen for and accept incoming connections. * * @return 'true' if manager is able to bind succesfully, false otherwise. */ bool BindAsServer(); /** * @brief Standard AddRef/Release scheme */ void AddRef(); /** * @brief Standard AddRef/Release scheme */ void Release(); /** * @brief Returns the ip address of this machine. If the machine is multi-homed, this value may be blank. */ IPAddress GetLocalIp() const; /** * @brief Returns the port the manager is actually using. This value will be the same as is specified in * Params::port (or if Params::port was set to 0, this will be the dynamically assigned port number) */ unsigned int GetLocalPort() const; protected: friend class TcpConnection; void removeConnection(TcpConnection *con); TcpManagerHandler *m_handler; ConnectionList m_keepAliveList; ConnectionList m_aliveList; ConnectionList m_noDataList; ConnectionList m_dataList; private: ~TcpManager(); TcpParams m_params; int m_refCount; TcpConnection *m_connectionList; unsigned m_connectionListCount; #ifdef WIN32 fd_set m_permfds; /**< Used for select on WIN32 if we are in server mode. Keeps track of all clients connected to us. */ #endif //WIN32 SOCKET m_socket; bool m_boundAsServer; TcpBlockAllocator m_allocator; Clock m_keepAliveTimer; Clock m_noDataTimer; void addNewConnection(TcpConnection *con); SOCKET getMaxFD(); TcpConnection *getConnection(SOCKET fd); TcpConnection *acceptClient(); }; #ifdef EXTERNAL_DISTRO }; #endif #endif //TCPMANAGER_H