Skip to content

Commit 73081eb

Browse files
committed
Address issue #10 for multicast with multi-homed systems
1 parent b6c94e2 commit 73081eb

File tree

4 files changed

+54
-10
lines changed

4 files changed

+54
-10
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ The UdpSocket class has the following interface for managing the UDP socket:
5252
UdpSocket(CallbackImpl &callback, SocketOpt *options = nullptr);
5353

5454
// Start a multicast socket
55-
SocketRet startMcast(const char *mcastAddr, uint16_t port);
55+
SocketRet startMcast(const char *mcastAddr, uint16_t port, const char *localIpAddr = nullptr);
5656

5757
// Start a unicast client/server socket
5858
SocketRet startUnicast(const char *remoteAddr, uint16_t localPort, uint16_t port)

examples/mcastApp.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
class McastApp {
1010
public:
1111
// UDP Multicast
12-
McastApp(const char *multicastAddr, uint16_t port);
12+
McastApp(const char *multicastAddr, uint16_t port, const char *localIface);
1313

1414
virtual ~McastApp() = default;
1515

@@ -21,8 +21,8 @@ class McastApp {
2121
sockets::UdpSocket<McastApp> m_mcast;
2222
};
2323

24-
McastApp::McastApp(const char *multicastAddr, uint16_t port) : m_mcast(*this) {
25-
sockets::SocketRet ret = m_mcast.startMcast(multicastAddr, port);
24+
McastApp::McastApp(const char *multicastAddr, uint16_t port, const char *localIface) : m_mcast(*this) {
25+
sockets::SocketRet ret = m_mcast.startMcast(multicastAddr, port, localIface);
2626
if (ret.m_success) {
2727
std::cout << "Connected to mcast group " << multicastAddr << ":" << port << "\n";
2828
} else {
@@ -45,28 +45,32 @@ void McastApp::onReceiveData(const char *data, size_t size) {
4545
}
4646

4747
void usage() {
48-
std::cout << "McastApp -m <mcastAddr> -p <port>\n";
48+
std::cout << "McastApp -m <mcastAddr> -p <port> -[-l <localAddr>]\n";
4949
}
5050

5151
int main(int argc, char **argv) {
5252
int arg = 0;
5353
const char *addr = nullptr;
54+
const char *local = nullptr;
5455
uint16_t port = 0;
55-
while ((arg = getopt(argc, argv, "m:p:?")) != EOF) { // NOLINT
56+
while ((arg = getopt(argc, argv, "m:p:l:?")) != EOF) { // NOLINT
5657
switch (arg) {
5758
case 'm':
5859
addr = optarg;
5960
break;
6061
case 'p':
6162
port = static_cast<uint16_t>(std::stoul(optarg));
6263
break;
64+
case 'l':
65+
local = optarg;
66+
break;
6367
case '?':
6468
usage();
6569
exit(1); // NOLINT
6670
}
6771
}
6872

69-
auto *app = new McastApp(addr, port);
73+
auto *app = new McastApp(addr, port, local);
7074

7175
while (true) {
7276
std::string data;

include/sockets-cpp/UdpSocket.h

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ class UdpSocket {
6868
* @param port - port number to listen/connect to
6969
* @return SocketRet - indication that multicast setup was successful
7070
*/
71-
SocketRet startMcast(const char *mcastAddr, uint16_t port) {
71+
SocketRet startMcast(const char *mcastAddr, uint16_t port, const char *localIpAddr = nullptr) {
7272
SocketRet ret;
7373

7474
int result = m_socketCore.Initialize();
@@ -164,7 +164,11 @@ class UdpSocket {
164164
//
165165
struct ip_mreq mreq { };
166166
inet_pton(AF_INET,mcastAddr,&mreq.imr_multiaddr);
167-
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
167+
if (localIpAddr) {
168+
inet_pton(AF_INET, localIpAddr, &mreq.imr_interface.s_addr);
169+
} else {
170+
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
171+
}
168172
if (m_socketCore.SetSockOpt(m_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, reinterpret_cast<char *>(&mreq), sizeof(mreq)) < 0) {
169173
ret.m_success = false;
170174
#if defined(FMT_SUPPORT)
@@ -177,6 +181,25 @@ class UdpSocket {
177181
return ret;
178182
}
179183

184+
struct in_addr addr;
185+
if (localIpAddr) {
186+
inet_pton(AF_INET, localIpAddr, &addr.s_addr);
187+
} else {
188+
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
189+
}
190+
if (m_socketCore.SetSockOpt(m_fd, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr)) < 0)
191+
{
192+
ret.m_success = false;
193+
#if defined(FMT_SUPPORT)
194+
ret.m_msg = fmt::format("Error: setsockopt(IP_MULTICAST_IF) failed: errno {}", errno);
195+
#else
196+
std::array<char,MSG_SIZE> msg;
197+
(void)snprintf(msg.data(),msg.size(),"Error: setsockopt(IP_MULTICAST_IF) failed: %d", errno);
198+
ret.m_msg = msg.data();
199+
#endif
200+
return ret;
201+
}
202+
180203
m_thread = std::thread(&UdpSocket::ReceiveTask, this);
181204
ret.m_success = true;
182205
return ret;

test/test_UdpSocket.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ TEST(UdpSocket,mcast_start_stop)
196196
MockSocketCore &core = app.m_socket.getCore();
197197

198198
EXPECT_CALL(core, Socket(_,_,_)).WillOnce(Return(4));
199-
EXPECT_CALL(core, SetSockOpt(_,_,_,_,_)).WillOnce(Return(0)).WillOnce(Return(0)).WillOnce(Return(0)).WillOnce(Return(0));
199+
EXPECT_CALL(core, SetSockOpt(_,_,_,_,_)).WillOnce(Return(0)).WillOnce(Return(0)).WillOnce(Return(0)).WillOnce(Return(0)).WillOnce(Return(0));
200200
EXPECT_CALL(core,Bind(_,_,_)).WillOnce(Return(0));
201201
EXPECT_CALL(core, Close(_)).WillOnce(Return(0));
202202
EXPECT_CALL(core, Select(_,_,_,_,_)).WillRepeatedly(Return(0));
@@ -207,6 +207,23 @@ TEST(UdpSocket,mcast_start_stop)
207207
app.m_socket.finish();
208208
}
209209

210+
TEST(UdpSocket,mcast_start_stop_local)
211+
{
212+
UdpTestApp app;
213+
MockSocketCore &core = app.m_socket.getCore();
214+
215+
EXPECT_CALL(core, Socket(_,_,_)).WillOnce(Return(4));
216+
EXPECT_CALL(core, SetSockOpt(_,_,_,_,_)).WillOnce(Return(0)).WillOnce(Return(0)).WillOnce(Return(0)).WillOnce(Return(0)).WillOnce(Return(0));
217+
EXPECT_CALL(core,Bind(_,_,_)).WillOnce(Return(0));
218+
EXPECT_CALL(core, Close(_)).WillOnce(Return(0));
219+
EXPECT_CALL(core, Select(_,_,_,_,_)).WillRepeatedly(Return(0));
220+
auto ret = app.m_socket.startMcast("224.0.0.1",5000,"127.0.0.1");
221+
EXPECT_EQ(true,ret.m_success);
222+
223+
std::this_thread::sleep_for(std::chrono::seconds(1));
224+
app.m_socket.finish();
225+
}
226+
210227
TEST(UdpSocket,finish_close_failure)
211228
{
212229
UdpTestApp app;

0 commit comments

Comments
 (0)