#include #include "GlobalConfigFile.h" #include "SimpleLogFile.h" #include "CommandDispatcher.h" #include "ReplyToClient.h" #include "MultiCast.h" // The code for the actual multicast reads and writes, in particular the // initial setup, was inspired by // https://gist.github.com/hostilefork/f7cae3dc33e7416f2dd25a402857b6c6 // TODO clean up a lot of sendToLogFile() statements. ///////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////// // std::string::starts_with() is added in c++20. static bool isPrefix(std::string const &prefix, std::string const &subject) { char const *p = prefix.c_str(); char const *s = subject.c_str(); while (true) { if (!*p) return true; if (*p != *s) return false; p++; s++; } } ///////////////////////////////////////////////////////////////////// // MultiCast ///////////////////////////////////////////////////////////////////// std::string MultiCast::getUserId(PropertyList const &message) { return getPropertyDefault(message, "user_id"); } void MultiCast::respond(PropertyList const &message) { const std::string originalUserId = getUserId(message); sendToLogFile(TclList()<listenForCommand("multi_cast_ping", this, mtMultiCastPing); } void MultiCast::handleRequestInThread(Request *original) { assert(original->callbackId == mtMultiCastPing); ExternalRequest *const request = dynamic_cast< ExternalRequest * >(original); const std::string userId = request->getProperty("user_id"); PropertyList toSend; if (userId.empty()) toSend[TYPE] = "PING"; else { toSend[TYPE] = PING_USER; toSend["user_id"] = userId; } sendRequest(toSend); sendToLogFile(TclList()<getSocketInfo(), TclList()<getResponseMessageId()); } void MultiCast::serialize(std::function< void() > toDo) { doWorkInThread(toDo); } static const int REQUEST_PORT = 1488; static const int RESPONSE_PORT = 1489; char const *const GROUP = "224.252.155.250"; MultiCast::MultiCast() : _sendHandle(-1), _receiveHandle(-1) { _callbacksForAll.emplace(); _callbacksByType.emplace(); _basicInfo.emplace(); // getConfigItem: disable_multicast=1 will disable any attempts to read // getConfigItem: or write to the multicast group. This is generally not a // getConfigItem: good idea. I added this just in case somehow this code // getConfigItem: causes problems and we need to disable it on the live // getConfigItem: servers quickly. if (getConfigItem("disable_multicast") == "1") return; addInfo("restart", ntoa(getMicroTime())); // getConfigItem: Search the config items for any keys that start with // getConfigItem: "multicast_status_". Copy any and all matching key value // getConfigItem: pairs to all multicast messages that we send. Remove // getConfigItem: the "multicast_status_" prefix from the key. const std::string PREFIX = "multicast_status_"; for (auto kvp : getConfigItems()) if (isPrefix(PREFIX, kvp.first)) addInfo(std::string(kvp.first, PREFIX.length()), kvp.second); addCallback([=](PropertyList const &) { PropertyList response = *getBasicInfo(); response[TYPE] = "PING_response"; sendResponse(response); }, "PING"); _sendHandle = socket(AF_INET, SOCK_DGRAM, 0); if (_sendHandle < 0) { sendToLogFile(TclList()<= 0) callbacks.waitForRead(_receiveHandle); } void MultiCast::awake(std::set< int > const &woken) { if ((_receiveHandle >= 0) && !woken.count(_receiveHandle)) return; int count = 0; while (true) { ssize_t nbytes = recv(_receiveHandle, NULL, 0, MSG_DONTWAIT | MSG_PEEK | MSG_TRUNC); if (nbytes < 0) { if (!((errno == EAGAIN ) || (errno == EWOULDBLOCK))) sendToLogFile(TclList()<size(); for (auto const &kvp : *callbacksByType) msg<