18 #ifndef GZ_TRANSPORT_DISCOVERY_HH_
19 #define GZ_TRANSPORT_DISCOVERY_HH_
33 #include <sys/types.h>
35 #include <sys/socket.h>
39 #include <arpa/inet.h>
43 #include <netinet/in.h>
49 #pragma warning(push, 0)
54 #pragma warning(disable: 4503)
56 #pragma warning(disable: 4996)
59 #include <gz/msgs/discovery.pb.h>
62 #include <condition_variable>
69 #include <type_traits>
73 #include <gz/msgs/Utility.hh>
75 #include "gz/transport/config.hh"
76 #include "gz/transport/Export.hh"
88 inline namespace GZ_TRANSPORT_VERSION_NAMESPACE {
118 template<
typename Pub>
130 const bool _verbose =
false)
131 : multicastGroup(_ip),
135 silenceInterval(kDefSilenceInterval),
136 activityInterval(kDefActivityInterval),
137 heartbeatInterval(kDefHeartbeatInterval),
138 connectionCb(nullptr),
139 disconnectionCb(nullptr),
142 numHeartbeatsUninitialized(0),
147 if (
env(
"GZ_IP", gzIp) && !gzIp.
empty())
149 this->hostInterfaces = {gzIp};
158 WORD wVersionRequested;
162 wVersionRequested = MAKEWORD(2, 2);
164 if (WSAStartup(wVersionRequested, &wsaData) != 0)
170 for (
const auto &netIface : this->hostInterfaces)
172 auto succeed = this->RegisterNetIface(netIface);
177 if (netIface == this->hostAddr && !succeed)
179 this->RegisterNetIface(
"127.0.0.1");
180 std::cerr <<
"Did you set the environment variable GZ_IP with a "
182 <<
" [" << netIface <<
"] seems an invalid local IP "
184 <<
" Using 127.0.0.1 as hostname." <<
std::endl;
185 this->hostAddr =
"127.0.0.1";
194 if (setsockopt(this->sockets.at(0), SOL_SOCKET, SO_REUSEADDR,
195 reinterpret_cast<const char *
>(&reuseAddr),
sizeof(reuseAddr)) != 0)
197 std::cerr <<
"Error setting socket option (SO_REUSEADDR)."
209 if (setsockopt(this->sockets.at(0), SOL_SOCKET, SO_REUSEPORT,
210 reinterpret_cast<const char *
>(&reusePort),
sizeof(reusePort)) != 0)
212 std::cerr <<
"Error setting socket option (SO_REUSEPORT)."
218 sockaddr_in localAddr;
219 memset(&localAddr, 0,
sizeof(localAddr));
220 localAddr.sin_family = AF_INET;
221 localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
222 localAddr.sin_port = htons(
static_cast<u_short
>(this->port));
224 if (bind(this->sockets.at(0),
225 reinterpret_cast<sockaddr *
>(&localAddr),
sizeof(sockaddr_in)) < 0)
232 memset(&this->mcastAddr, 0,
sizeof(this->mcastAddr));
233 this->mcastAddr.sin_family = AF_INET;
234 this->mcastAddr.sin_addr.s_addr =
235 inet_addr(this->multicastGroup.c_str());
236 this->mcastAddr.sin_port = htons(
static_cast<u_short
>(this->port));
240 if (
env(
"GZ_RELAY", gzRelay) && !gzRelay.
empty())
246 for (
auto const &relayAddr : relays)
247 this->AddRelayAddress(relayAddr);
250 this->PrintCurrentState();
257 this->exitMutex.lock();
259 this->exitMutex.unlock();
262 if (this->threadReception.joinable())
263 this->threadReception.join();
271 for (
const auto &sock : this->sockets)
294 this->enabled =
true;
298 this->timeNextHeartbeat = now;
299 this->timeNextActivity = now;
302 this->threadReception =
std::thread(&Discovery::RecvMessages,
this);
320 if (!this->info.AddPublisher(_publisher))
323 cb = this->connectionCb;
360 cb = this->connectionCb;
364 pub.SetTopic(_topic);
365 pub.SetPUuid(this->pUuid);
372 found = this->info.Publishers(_topic, addresses);
378 for (
const auto &proc : addresses)
380 for (
const auto &node : proc.second)
436 return this->info.Publishers(_topic, _publishers);
447 return this->remoteSubscribers.Publishers(_topic, _subscribers);
468 if (!this->info.Publisher(_topic, this->pUuid, _nUuid, inf))
472 this->info.DelPublisherByNode(_topic, this->pUuid, _nUuid);
480 msgs::Discovery::UNADVERTISE, inf);
491 return this->hostAddr;
501 return this->activityInterval;
512 return this->heartbeatInterval;
522 return this->silenceInterval;
531 this->activityInterval = _ms;
540 this->heartbeatInterval = _ms;
549 this->silenceInterval = _ms;
559 this->connectionCb = _cb;
569 this->disconnectionCb = _cb;
578 this->registrationCb = _cb;
587 this->unregistrationCb = _cb;
596 this->subscribersCb = _cb;
610 std::cout <<
"\tActivity: " << this->activityInterval
612 std::cout <<
"\tHeartbeat: " << this->heartbeatInterval
614 std::cout <<
"\tSilence: " << this->silenceInterval
623 if (this->activity.empty())
627 for (
auto &proc : this->activity)
649 if constexpr (std::is_same_v<Pub, MessagePublisher>)
658 this->info.TopicList(_topics);
661 this->remoteSubscribers.TopicList(remoteSubs);
664 for (
auto const &t : remoteSubs)
679 if (!this->initialized)
681 this->initializedCv.wait(lk, [
this]{
return this->initialized;});
688 private:
void UpdateActivity()
701 if (now < this->timeNextActivity)
704 disconnectCb = this->disconnectionCb;
706 for (
auto it = this->activity.cbegin(); it != this->activity.cend();)
709 auto elapsed = now - it->second;
712 if (std::chrono::duration_cast<std::chrono::milliseconds>
713 (elapsed).count() > this->silenceInterval)
716 this->info.DelPublishersByProc(it->first);
717 this->remoteSubscribers.DelPublishersByProc(it->first);
722 this->activity.
erase(it++);
738 for (
auto const &uuid : uuids)
741 publisher.SetPUuid(uuid);
742 disconnectCb(publisher);
752 for (
auto const &addr : this->relayAddrs)
754 if (addr.sin_addr.s_addr == inet_addr(_ip.
c_str()))
759 memset(&addr, 0,
sizeof(addr));
760 addr.sin_family = AF_INET;
761 addr.sin_addr.s_addr = inet_addr(_ip.
c_str());
762 addr.sin_port = htons(
static_cast<u_short
>(this->port));
764 this->relayAddrs.push_back(addr);
775 for (
auto const &addr : this->relayAddrs) {
776 result.
push_back(inet_ntoa(addr.sin_addr));
783 private:
void UpdateHeartbeat()
790 if (now < this->timeNextHeartbeat)
794 Publisher pub(
"",
"", this->pUuid,
"", AdvertiseOptions());
802 this->info.PublishersByProc(this->pUuid, nodes);
805 for (
const auto &topic : nodes)
807 for (
const auto &node : topic.second)
810 msgs::Discovery::ADVERTISE, node);
816 if (!this->initialized)
818 if (this->numHeartbeatsUninitialized == 2u)
821 this->initialized =
true;
824 this->initializedCv.notify_all();
826 ++this->numHeartbeatsUninitialized;
843 private:
int NextTimeout()
const
846 auto timeUntilNextHeartbeat = this->timeNextHeartbeat - now;
847 auto timeUntilNextActivity = this->timeNextActivity - now;
849 int t =
static_cast<int>(
850 std::chrono::duration_cast<std::chrono::milliseconds>
851 (
std::min(timeUntilNextHeartbeat, timeUntilNextActivity)).count());
852 int t2 =
std::min(t, this->kTimeout);
857 private:
void RecvMessages()
859 bool timeToExit =
false;
863 int timeout = this->NextTimeout();
867 this->RecvDiscoveryUpdate();
870 this->PrintCurrentState();
873 this->UpdateHeartbeat();
874 this->UpdateActivity();
886 private:
void RecvDiscoveryUpdate()
888 char rcvStr[Discovery::kMaxRcvStr];
889 sockaddr_in clntAddr;
890 socklen_t addrLen =
sizeof(clntAddr);
892 int64_t received = recvfrom(this->sockets.at(0),
893 reinterpret_cast<raw_type *
>(rcvStr),
895 reinterpret_cast<sockaddr *
>(&clntAddr),
896 reinterpret_cast<socklen_t *
>(&addrLen));
900 memcpy(&len, &rcvStr[0],
sizeof(len));
924 if (len +
sizeof(len) ==
static_cast<uint16_t
>(received))
926 std::string srcAddr = inet_ntoa(clntAddr.sin_addr);
927 uint16_t srcPort = ntohs(clntAddr.sin_port);
931 std::cout <<
"\nReceived discovery update from "
932 << srcAddr <<
": " << srcPort <<
std::endl;
935 this->DispatchDiscoveryMsg(srcAddr, rcvStr +
sizeof(len), len);
938 else if (received < 0)
940 std::cerr <<
"Discovery::RecvDiscoveryUpdate() recvfrom error"
949 private:
void DispatchDiscoveryMsg(
const std::string &_fromIp,
950 char *_msg, uint16_t _len)
952 gz::msgs::Discovery msg;
957 if (!msg.ParseFromArray(_msg, _len))
961 if (this->Version() != msg.version())
967 if (recvPUuid == this->pUuid)
979 if (msg.has_flags() && msg.flags().relay())
982 msg.mutable_flags()->set_relay(
false);
983 msg.mutable_flags()->set_no_relay(
true);
984 this->SendMulticast(msg);
988 this->AddRelayAddress(_fromIp);
996 else if (!msg.has_flags() || !msg.flags().no_relay())
998 msg.mutable_flags()->set_relay(
true);
999 this->SendUnicast(msg);
1002 bool isSenderLocal = (
std::find(this->hostInterfaces.begin(),
1003 this->hostInterfaces.end(), _fromIp) != this->hostInterfaces.end()) ||
1004 (_fromIp.
find(
"127.") == 0);
1007 DiscoveryCallback<Pub> connectCb;
1008 DiscoveryCallback<Pub> disconnectCb;
1009 DiscoveryCallback<Pub> registerCb;
1010 DiscoveryCallback<Pub> unregisterCb;
1015 connectCb = this->connectionCb;
1016 disconnectCb = this->disconnectionCb;
1017 registerCb = this->registrationCb;
1018 unregisterCb = this->unregistrationCb;
1019 subscribersReqCb = this->subscribersCb;
1024 case msgs::Discovery::ADVERTISE:
1028 publisher.SetFromDiscovery(msg);
1042 added = this->info.AddPublisher(publisher);
1045 if (added && connectCb)
1048 connectCb(publisher);
1053 case msgs::Discovery::SUBSCRIBE:
1059 recvTopic = msg.sub().topic();
1063 std::cerr <<
"Subscription discovery message is missing "
1064 <<
"Subscriber information.\n";
1069 Addresses_M<Pub> addresses;
1072 if (!this->info.HasAnyPublishers(recvTopic, this->pUuid))
1077 if (!this->info.Publishers(recvTopic, addresses))
1081 for (
const auto &nodeInfo : addresses[this->pUuid])
1093 msgs::Discovery::ADVERTISE, nodeInfo);
1098 case msgs::Discovery::SUBSCRIBERS_REQ:
1100 if (subscribersReqCb)
1105 case msgs::Discovery::SUBSCRIBERS_REP:
1109 publisher.SetFromDiscovery(msg);
1113 this->remoteSubscribers.AddPublisher(publisher);
1117 case msgs::Discovery::NEW_CONNECTION:
1121 publisher.SetFromDiscovery(msg);
1124 registerCb(publisher);
1128 case msgs::Discovery::END_CONNECTION:
1132 publisher.SetFromDiscovery(msg);
1136 this->remoteSubscribers.DelPublisherByNode(
1137 publisher.Topic(), publisher.PUuid(), publisher.NUuid());
1141 unregisterCb(publisher);
1145 case msgs::Discovery::HEARTBEAT:
1150 case msgs::Discovery::BYE:
1155 this->activity.erase(recvPUuid);
1161 pub.SetPUuid(recvPUuid);
1169 this->info.DelPublishersByProc(recvPUuid);
1170 this->remoteSubscribers.DelPublishersByProc(recvPUuid);
1175 case msgs::Discovery::UNADVERTISE:
1179 publisher.SetFromDiscovery(msg);
1192 disconnectCb(publisher);
1198 this->info.DelPublisherByNode(publisher.Topic(),
1199 publisher.PUuid(), publisher.NUuid());
1206 std::cerr <<
"Unknown message type [" << msg.type() <<
"].\n";
1218 private:
template<
typename T>
1220 const msgs::Discovery::Type _type,
1221 const T &_pub)
const
1223 gz::msgs::Discovery discoveryMsg;
1224 discoveryMsg.set_version(this->Version());
1225 discoveryMsg.set_type(_type);
1226 discoveryMsg.set_process_uuid(this->pUuid);
1227 _pub.FillDiscovery(discoveryMsg);
1231 case msgs::Discovery::ADVERTISE:
1232 case msgs::Discovery::UNADVERTISE:
1233 case msgs::Discovery::NEW_CONNECTION:
1234 case msgs::Discovery::END_CONNECTION:
1236 _pub.FillDiscovery(discoveryMsg);
1239 case msgs::Discovery::SUBSCRIBE:
1241 discoveryMsg.mutable_sub()->set_topic(_pub.Topic());
1244 case msgs::Discovery::HEARTBEAT:
1245 case msgs::Discovery::BYE:
1246 case msgs::Discovery::SUBSCRIBERS_REQ:
1247 case msgs::Discovery::SUBSCRIBERS_REP:
1250 std::cerr <<
"Discovery::SendMsg() error: Unrecognized message"
1251 <<
" type [" << _type <<
"]" <<
std::endl;
1258 this->SendMulticast(discoveryMsg);
1266 discoveryMsg.mutable_flags()->set_relay(
true);
1267 this->SendUnicast(discoveryMsg);
1272 std::cout <<
"\t* Sending " << msgs::ToString(_type)
1273 <<
" msg [" << _pub.Topic() <<
"]" <<
std::endl;
1279 private:
void SendUnicast(
const msgs::Discovery &_msg)
const
1283 #if GOOGLE_PROTOBUF_VERSION >= 3004000
1284 size_t msgSizeFull = _msg.ByteSizeLong();
1286 int msgSizeFull = _msg.ByteSize();
1288 if (msgSizeFull +
sizeof(msgSize) > this->kMaxRcvStr)
1290 std::cerr <<
"Discovery message too large to send. Discovery won't "
1291 <<
"work. This shouldn't happen.\n";
1294 msgSize = msgSizeFull;
1296 uint16_t totalSize =
sizeof(msgSize) + msgSize;
1297 char *buffer =
static_cast<char *
>(
new char[totalSize]);
1298 memcpy(&buffer[0], &msgSize,
sizeof(msgSize));
1300 if (_msg.SerializeToArray(buffer +
sizeof(msgSize), msgSize))
1305 for (
const auto &sockAddr : this->relayAddrs)
1308 auto sent = sendto(this->sockets.at(0),
1309 reinterpret_cast<const raw_type *
>(
1310 reinterpret_cast<const unsigned char*
>(buffer)),
1312 reinterpret_cast<const sockaddr *
>(&sockAddr),
1315 if (sent != totalSize)
1326 std::cerr <<
"Discovery::SendUnicast: Error serializing data."
1335 private:
void SendMulticast(
const msgs::Discovery &_msg)
const
1339 #if GOOGLE_PROTOBUF_VERSION >= 3004000
1340 size_t msgSizeFull = _msg.ByteSizeLong();
1342 int msgSizeFull = _msg.ByteSize();
1344 if (msgSizeFull +
sizeof(msgSize) > this->kMaxRcvStr)
1346 std::cerr <<
"Discovery message too large to send. Discovery won't "
1347 <<
"work. This shouldn't happen.\n";
1351 msgSize = msgSizeFull;
1352 uint16_t totalSize =
sizeof(msgSize) + msgSize;
1353 char *buffer =
static_cast<char *
>(
new char[totalSize]);
1354 memcpy(&buffer[0], &msgSize,
sizeof(msgSize));
1356 if (_msg.SerializeToArray(buffer +
sizeof(msgSize), msgSize))
1360 for (
const auto &sock : this->Sockets())
1363 if (sendto(sock,
reinterpret_cast<const raw_type *
>(
1364 reinterpret_cast<const unsigned char*
>(buffer)),
1366 reinterpret_cast<const sockaddr *
>(this->MulticastAddr()),
1367 sizeof(*(this->MulticastAddr()))) != totalSize)
1377 if (errno != EPERM && errno != ENOBUFS)
1379 std::cerr <<
"Exception sending a multicast message:"
1388 std::cerr <<
"Discovery::SendMulticast: Error serializing data."
1399 return this->sockets;
1404 private:
const sockaddr_in *MulticastAddr()
const
1406 return &this->mcastAddr;
1411 private: uint8_t Version()
const
1414 static int topicStats;
1416 if (
env(
"GZ_TRANSPORT_TOPIC_STATISTICS", gzStats) && !gzStats.
empty())
1418 topicStats = (gzStats ==
"1");
1421 return this->kWireVersion + (topicStats * 100);
1428 private:
bool RegisterNetIface(
const std::string &_ip)
1431 int sock =
static_cast<int>(socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP));
1441 struct in_addr ifAddr;
1442 ifAddr.s_addr = inet_addr(_ip.
c_str());
1443 if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
1444 reinterpret_cast<const char*
>(&ifAddr),
sizeof(ifAddr)) != 0)
1446 std::cerr <<
"Error setting socket option (IP_MULTICAST_IF)."
1451 this->sockets.push_back(sock);
1456 struct ip_mreq group;
1457 group.imr_multiaddr.s_addr =
1458 inet_addr(this->multicastGroup.c_str());
1459 group.imr_interface.s_addr = inet_addr(_ip.
c_str());
1460 if (setsockopt(this->sockets.at(0), IPPROTO_IP, IP_ADD_MEMBERSHIP,
1461 reinterpret_cast<const char*
>(&group),
sizeof(group)) != 0)
1463 std::cerr <<
"Error setting socket option (IP_ADD_MEMBERSHIP)."
1474 private:
static const unsigned int kDefActivityInterval = 100;
1479 private:
static const unsigned int kDefHeartbeatInterval = 1000;
1484 private:
static const unsigned int kDefSilenceInterval = 3000;
1490 private:
const int kTimeout = 250;
1493 private:
static const uint16_t kMaxRcvStr =
1498 private:
static const uint8_t kWireVersion = 10;
1515 private:
unsigned int silenceInterval;
1520 private:
unsigned int activityInterval;
1525 private:
unsigned int heartbeatInterval;
1528 private: DiscoveryCallback<Pub> connectionCb;
1531 private: DiscoveryCallback<Pub> disconnectionCb;
1534 private: DiscoveryCallback<Pub> registrationCb;
1537 private: DiscoveryCallback<Pub> unregistrationCb;
1543 private: TopicStorage<Pub> info;
1546 private: TopicStorage<Pub> remoteSubscribers;
1555 private:
bool verbose;
1561 private: sockaddr_in mcastAddr;
1585 private:
bool initialized;
1588 private:
unsigned int numHeartbeatsUninitialized;
1597 private:
bool enabled;