#ifndef __ServerConnection64_h_ #define __ServerConnection64_h_ #include "TalkWithServer64.h" #include "ContainerThread.h" /* ServerConnection64 was heavily inspired by ServerConnection and * ForeverServerConnection. There are three major differences. * * 1) This code uses TalkWithServer64. This allows us to create and cancel * any number of streaming connections. This gets rid of the need for a * special manager class to share streaming connections. * 2) This code includes the equivalent of the C# code's SendManager. This * means that you can immediately resend a message as soon as you get the * onAbort() callback saying that the last attempt failed. There are * numerous problems with trying that in a ServerConnection or * ForeverServerConnection. It may be best to avoid the onAbort() callback * in those classes in general. * 3) It is not possible to delete a ServerConnection64. That feature of * ServerConnection was never used and caused a lot of issues. * * This is the ideal successor to ServerConnection. It gets rid of some issues * that made ServerConnection hard to use. Unfortunately that requires some * changes on the server side, so ServerConnection will not go away any time * in the foreseeable future. */ class ServerConnection64 : public ForeverThreadUser, protected TalkWithServer64::IMessageListener, NoCopy, NoAssign { public: typedef TalkWithServer64::MessageId MessageId; typedef TalkWithServer64::Message Message; typedef TalkWithServer64::IMessageListener IMessageListener; private: static const int CI_PING = -1; TalkWithServer64 *_connection; time_t _nextPingTime; time_t _connectionBlackout; bool _pingSent; void checkPing(); bool _pleaseSendSoon; struct MessageToSend { Message message; IMessageListener *listener; int64_t clientId; MessageId messageId; bool streaming; MessageToSend(Message const &message) : message(message), listener(NULL), streaming(false) { } MessageToSend() { } }; typedef std::map< MessageId, MessageToSend > MessagesToSend; MessagesToSend _messagesToSend; bool shouldDeferMessages() const; protected: const std::string _name; std::string _serverName; std::string _serverPort; void reset(); // This describes the talk with server object, and the corresponding TCP/IP // connection. Each time we reconnect we get a different value. Values will // never be reused. If there is no TalkWithServer, this will return 0. If // we have a TalkWithServer object, but it's already shutting down, we return // 0. 0 means we will have a new connection for any upcoming messages. // // How to use: // You need to ensure two messages, A and B, both go to the same session. // You don't want to send B over a new session until after we've sent A. // Send A. Record the current value of getConnectionId(). When you want to // send B, immediately before you send it, check the saved value of // getConnectionId(). If the saved value is 0 then we haven't sent A. // Otherwise check getConnectionId() again. If the new value is diffrent // then the saved value, that means that we sent A on a previous channel, // not this one. // // This was aimed at the old TopList code that we still use for history. // The plan is to make that code more like the new TopList streaming API. // In particular, in the newer APIs make every request is completely // seperate. In the older APIs we had some shared resources. Message A is // a single request that received all streaming results. Message B is a // request for a specific top list. // // In C# and Java/GWT we actually kept a pointer to the TalkWithServer // object, which we'd use for this same purpose. That's not a good idea in // C++. So we use a 64 bit integer and a one up counter instead. MessageId getConnectionId() const { return shouldDeferMessages()?0:_connection->getUniqueId(); } // From TalkWithServer64::IMessageListener virtual void onMessage(std::string bytes, int64_t clientId, MessageId messageId) override; virtual void onAbort(int64_t clientId, MessageId messageId) override; // From IContainerThreadUser virtual void beforeSleep(IBeforeSleepCallbacks &callbacks) override; virtual void awake(std::set< int > const &woken) override; // New virtual void onNewConnection(TalkWithServer64 *connection); virtual bool shouldTryToConnect(); void checkConnection(bool createIfRequired=true); ServerConnection64(std::string const &name, IContainerThread *thread = NULL); // Look for configName in the global config file. The value should look like // "www.trade-ideas.com:8844". You can specify a default value in case the // named key cannot be found in the config file. If you do not specify a // default value and we can't find a value in the config file, we will // print a reasonable error message in the log file. bool getAndParseAddress(std::string const &configName, std::string const &defaultValue = "NOT FOUND"); // These implement the equivalant of C#'s SendManager. It is safe to call // these from an abort callback. That will not cause the CPU to go to 100%. void sendMessage(Message const &message); MessageId sendMessage(Message const &message, IMessageListener *listener, int64_t clientId, bool streaming = false); void cancelMessage(MessageId messageId); ~ServerConnection64() { assert(false); } }; #endif