問(wèn)題背景:
平臺(tái):Android
設(shè)備A:組播發(fā)送者,發(fā)送組播數(shù)據(jù)
設(shè)備B:組播接受者,接收組播數(shù)據(jù)
設(shè)備A與設(shè)備B通過(guò)有線(xiàn)連接,連接在設(shè)備B的eth0網(wǎng)卡上
問(wèn)題:
設(shè)備B在沒(méi)有通訊模塊網(wǎng)卡的情況下收得到設(shè)備A的組播數(shù)據(jù),一旦有了通訊模塊的撥號(hào)存在,則收不到A的組播數(shù)據(jù)了。但設(shè)備B上通過(guò)抓包,是能確認(rèn)有抓到組播數(shù)據(jù)進(jìn)來(lái),也就是說(shuō)eth0的網(wǎng)卡上,組播數(shù)據(jù)是進(jìn)來(lái)了
組播注冊(cè)偽代碼:
int listenSock = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == listenSock)
{
printf("Create socket failed! listenSock=%d\n", listenSock);
return -1;
}
xint32_t opt = 1;
setsockopt(listenSock, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, sizeof(opt));
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(static_cast<uint16_t>(端口));
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(listenSock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) == -1)
{
printf("Bind sock failed!!!\n");
close(listenSock);
return -1;
}
struct ip_mreq group;
group.imr_multiaddr.s_addr = inet_addr("組播ip");
group.imr_interface.s_addr = INADDR_ANY;
if (setsockopt(listenSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&group, sizeof(group)) < 0)
{
close(listenSock);
printf("Add membership fail! errno:%d\n", errno);
return -2;
}
struct ifreq nif = {0};
strcpy(nif.ifr_name, "eth0");
if (setsockopt(listenSock, SOL_SOCKET, SO_BINDTODEVICE, (char *)&nif, sizeof(nif)) < 0)
{
close(listenSock);
printf("Bind interface=eth0 fail!, errno:%d\n", errno);
return -1;
}
通過(guò)以上代碼,在存在通訊模塊的時(shí)候是收不到的。也懷疑過(guò)是否是因?yàn)楸惶砑恿薴ilter導(dǎo)致,后面也排除了,那么還有種可能,就是綁定網(wǎng)卡并沒(méi)有生效,我們雖然socket綁定到了eth0,但組播的加入也涉及網(wǎng)卡,我們這個(gè)方式因順序被加入到了其他網(wǎng)卡里,直接看修改,那么在添加組播組的時(shí)候也能綁定網(wǎng)卡,修改后的偽代碼如下:
int listenSock = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == listenSock)
{
printf("Create socket failed!!! listenSock=%d\n", m_listenSock);
return -1;
}
xint32_t opt = 1;
setsockopt(listenSock, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, sizeof(opt));
struct sockaddr_in localAddr;
memset(&localAddr, 0, sizeof(localAddr));
localAddr.sin_family = AF_INET;
inet_pton(AF_INET, "組播ip", &localAddr.sin_addr);
localAddr.sin_port = htons((uint16_t)端口);
localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
if (::bind(listenSock, reinterpret_cast<struct sockaddr*>(&localAddr), sizeof(localAddr)) == -1)
{
printf("Bind sock failed!!!\n");
close(listenSock);
return -1;
}
struct ifreq nif;
memset(&nif, 0, sizeof(ifreq));
strcpy(nif.ifr_name, "eth0");
if (::setsockopt(listenSock, SOL_SOCKET, SO_BINDTODEVICE, (char *)&nif, sizeof(ifreq)) < 0)
{
close(listenSock);
printf("Bind interface=eth0 fail!, errno:%d\n", errno);
return -1;
}
//關(guān)鍵修改在這
struct ip_mreqn group;
group.imr_multiaddr.s_addr = inet_addr("組播ip");
group.imr_address.s_addr = htonl(INADDR_ANY);
group.imr_ifindex = if_nametoindex("eth0");//在添加組的時(shí)候, 指定網(wǎng)卡
if (::setsockopt(listenSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&group, sizeof(group)) < 0)
{
close(listenSock);
printf("Add membership interface=eth0 fail!, errno:%d\n", errno);
return -2;
}
也看到了,結(jié)構(gòu)體用得也不一樣了,其實(shí) ip_mreq 與 ip_mreqn時(shí)差不多的含義
//來(lái)源:#include <in.h>
/* Internet address. */
struct in_addr {
__be32 s_addr;
};
/* Request struct for multicast socket ops */
struct ip_mreq {
struct in_addr imr_multiaddr; /* IP multicast address of group */ //組播組組地址
struct in_addr imr_interface; /* local IP address of interface */ // 組播組的的網(wǎng)卡ip, 對(duì)應(yīng)的網(wǎng)卡能夠接收對(duì)應(yīng)多播組的數(shù)據(jù)包
};
struct ip_mreqn {
struct in_addr imr_multiaddr; /* IP multicast address of group */
struct in_addr imr_address; /* local IP address of interface */ //組播組的的網(wǎng)卡ip, 對(duì)應(yīng)的網(wǎng)卡能夠接收對(duì)應(yīng)多播組的數(shù)據(jù)包
int imr_ifindex; /* Interface index */ //加入組播組的網(wǎng)卡index,優(yōu)先級(jí)高于以上地址
};
可以看到以下,ip_mreqn明確多了一個(gè)網(wǎng)卡指定。我在使用ip_mreq的時(shí)候也嘗試過(guò)將imr_interface指定為我eth0的ip來(lái)綁定,仍然無(wú)效。如果不是要收多個(gè)網(wǎng)卡的,可以使用以上方式修改,記錄借鑒