C 在所有接口上监听多播,响应与接收到的相同
C listen to multicast on all interfaces, respond on same as recieved
我正在尝试在系统中的所有接口上收听多播,但仅在我收到多播数据包的接口上响应。
我所做的是为每个接口创建一个套接字,问题就在这里开始了。
当我将接口绑定到 INADDR_ANY
时,它将接收所有接口的数据包并在默认接口上发送。如果我将端口绑定到特定接口,它将不会接收多播数据包(但它将能够在正确的接口上发送)。
我试过设置 IP_ADD_MEMBERSHIP
或 IP_MULTICAST_IF
等选项,但没有成功。
我认为其他选项是创建一个套接字以在所有接口的所有 ifs 和发送方套接字上接收,但在这种方法中我不知道进入哪个 ifs 数据包...
代码示例(经过简化,没有错误处理等):
正在创建套接字:
//here i am looping over all interfaces from getifaddrs
struct sockaddr_in *pAddr = (struct sockaddr_in *)tmp->ifa_addr;
sockets[i] = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
setsockopt(sockets[i], SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval);
mreq.imr_multiaddr.s_addr=inet_addr(MDNS_ADDRESS);
mreq.imr_interface.s_addr=pAddr->sin_addr.s_addr;
setsockopt(sockets[i], IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
setsockopt(sockets[i], IPPROTO_IP, IP_MULTICAST_IF, &pAddr, sizeof(struct in_addr));
memset(&my_addr, 0, sizeof(struct sockaddr_in));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = INADDR_ANY; //or pAddr->sin_addr.s_addr;
my_addr.sin_port = htons(port);
bind(sockets[i], (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1);
接收和发送:
recvfrom(sockfd, buf, MAXBUFLEN-1 , 0, (struct sockaddr *)&their_addr, &addr_len);
//do some magic and response (response should be a multicast too)
destination.sin_addr.s_addr = inet_addr(MULTICAST_ADDRESS);
destination.sin_family = AF_INET;
destination.sin_port = htons( port );
sendto(sockfd, buffer, len, 0, (struct sockaddr *)&destination, sizeof destination);
我想在工作中创建与 mDNS 类似的东西,因此当数据包进入特定接口程序时,应该使用有关此 if 的一些数据回答相同的 if。它不应该在其他 ifs 上发送它,因为它可能与它们无关,但它应该将它作为多播发送,这样同一网络中的任何其他主机都会收到响应。
你应该只需要一个插座。
首先绑定到 INADDR_ANY
和您选择的端口。然后在每个要接收多播的接口上用 IP_ADD_MEMBERSHIP
调用 setsockopt
。最后,在要发送多播的接口上用 IP_MULTICAST_IF
调用 setsockopt
。确保在每次调用时检查错误。
int socket s;
struct sockaddr_in sin;
struct ip_mreq mreq;
struct in_addr out_addr;
bzero(&sin,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr=htonl(INADDR_ANY);
sin.sin_port = htons(1044); // or whatever port you listen on
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("socket failed");
exit(1);
}
if (bind(s, (struct sockaddr *)&sin, sizeof(sin))==-1) {
perror("bind failed");
exit(1);
}
// Do this in a loop for each interface
mreq.imr_multiaddr = inet_addr("230.4.4.1"); // your multicast address
mreq.imr_interface = inet_addr("192.168.1.1"); // your incoming interface IP
if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) == -1) {
perror("Error joining multicast group");
exit(1);
}
out_addr.s_addr = inet_addr("192.168.1.1"); // your outgoing interface IP
if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&out_addr,sizeof(out_addr))== -1) {
perror("Error setting outgoing interface");
exit(1);
}
使用多播时,您应该始终绑定到 INADDR_ANY 地址。不这样做会中断 Linux 系统上的多播。
最初,我接受了@dbush 的回答,因为它让我走上了正轨。为了完整起见,我 post 更详细的回答,并且按照他的建议,我接受了我自己的回答。
一些代码是在这里找到的:Setting the source IP for a UDP socket
我能够使用单个套接字并设置 IP_PKTINFO 来完成所有这些操作。
代码示例(经过简化,没有错误处理等):
正在创建套接字:
if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) == -1) {
perror("socket");
exit(1);
}
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval) < 0) {
perror("setsockopt");
exit(1);
}
optval2 = 1;
if(setsockopt(sockfd, IPPROTO_IP, IP_PKTINFO, &optval2, sizeof(optval2)) < 0) {
perror("setsockopt");
exit(1);
}
memset(&my_addr, 0, sizeof(struct sockaddr_in));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = INADDR_ANY;
my_addr.sin_port = htons(5353);
if (bind(sockfd, (struct sockaddr *)&my_addr,
sizeof(struct sockaddr)) == -1) {
perror("bind");
exit(1);
}
接收和响应:
char buf[MAXBUFLEN];
char cmsgbuf[MAXBUFLEN];
struct iovec iov[1];
iov[0].iov_base=buf;
iov[0].iov_len=sizeof(buf);
struct cmsghdr *cmsg;
struct msghdr message;
message.msg_name=&their_addr;
message.msg_namelen=sizeof(their_addr);
message.msg_iov=iov;
message.msg_iovlen=1;
message.msg_control=cmsgbuf;
message.msg_controllen=MAXBUFLEN;
if ((numbytes = recvmsg(sockfd, &message, 0)) == -1) {
perror("recvfrom");
exit(1);
}
for (cmsg = CMSG_FIRSTHDR(&message); cmsg != NULL; cmsg = CMSG_NXTHDR(&message, cmsg)) {
// ignore the control headers that don't match what we want
if (cmsg->cmsg_level != IPPROTO_IP ||
cmsg->cmsg_type != IP_PKTINFO)
{
continue;
}
struct in_pktinfo *pi = CMSG_DATA(cmsg);
addr = pi->ipi_spec_dst.s_addr;
}
//DO SOME MAGIC
//HERE WE ARE SETTING ADDR - INTERFACE WITH THIS ADDR WILL SEND MULTICAST
sock_opt_addr.s_addr = addr;
setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF, &sock_opt_addr, sizeof(sock_opt_addr));
sendto(sockfd, buffer, len, 0, (struct sockaddr *)&destination, sizeof destination);
我正在尝试在系统中的所有接口上收听多播,但仅在我收到多播数据包的接口上响应。
我所做的是为每个接口创建一个套接字,问题就在这里开始了。
当我将接口绑定到 INADDR_ANY
时,它将接收所有接口的数据包并在默认接口上发送。如果我将端口绑定到特定接口,它将不会接收多播数据包(但它将能够在正确的接口上发送)。
我试过设置 IP_ADD_MEMBERSHIP
或 IP_MULTICAST_IF
等选项,但没有成功。
我认为其他选项是创建一个套接字以在所有接口的所有 ifs 和发送方套接字上接收,但在这种方法中我不知道进入哪个 ifs 数据包...
代码示例(经过简化,没有错误处理等):
正在创建套接字:
//here i am looping over all interfaces from getifaddrs
struct sockaddr_in *pAddr = (struct sockaddr_in *)tmp->ifa_addr;
sockets[i] = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
setsockopt(sockets[i], SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval);
mreq.imr_multiaddr.s_addr=inet_addr(MDNS_ADDRESS);
mreq.imr_interface.s_addr=pAddr->sin_addr.s_addr;
setsockopt(sockets[i], IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
setsockopt(sockets[i], IPPROTO_IP, IP_MULTICAST_IF, &pAddr, sizeof(struct in_addr));
memset(&my_addr, 0, sizeof(struct sockaddr_in));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = INADDR_ANY; //or pAddr->sin_addr.s_addr;
my_addr.sin_port = htons(port);
bind(sockets[i], (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1);
接收和发送:
recvfrom(sockfd, buf, MAXBUFLEN-1 , 0, (struct sockaddr *)&their_addr, &addr_len);
//do some magic and response (response should be a multicast too)
destination.sin_addr.s_addr = inet_addr(MULTICAST_ADDRESS);
destination.sin_family = AF_INET;
destination.sin_port = htons( port );
sendto(sockfd, buffer, len, 0, (struct sockaddr *)&destination, sizeof destination);
我想在工作中创建与 mDNS 类似的东西,因此当数据包进入特定接口程序时,应该使用有关此 if 的一些数据回答相同的 if。它不应该在其他 ifs 上发送它,因为它可能与它们无关,但它应该将它作为多播发送,这样同一网络中的任何其他主机都会收到响应。
你应该只需要一个插座。
首先绑定到 INADDR_ANY
和您选择的端口。然后在每个要接收多播的接口上用 IP_ADD_MEMBERSHIP
调用 setsockopt
。最后,在要发送多播的接口上用 IP_MULTICAST_IF
调用 setsockopt
。确保在每次调用时检查错误。
int socket s;
struct sockaddr_in sin;
struct ip_mreq mreq;
struct in_addr out_addr;
bzero(&sin,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr=htonl(INADDR_ANY);
sin.sin_port = htons(1044); // or whatever port you listen on
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("socket failed");
exit(1);
}
if (bind(s, (struct sockaddr *)&sin, sizeof(sin))==-1) {
perror("bind failed");
exit(1);
}
// Do this in a loop for each interface
mreq.imr_multiaddr = inet_addr("230.4.4.1"); // your multicast address
mreq.imr_interface = inet_addr("192.168.1.1"); // your incoming interface IP
if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) == -1) {
perror("Error joining multicast group");
exit(1);
}
out_addr.s_addr = inet_addr("192.168.1.1"); // your outgoing interface IP
if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&out_addr,sizeof(out_addr))== -1) {
perror("Error setting outgoing interface");
exit(1);
}
使用多播时,您应该始终绑定到 INADDR_ANY 地址。不这样做会中断 Linux 系统上的多播。
最初,我接受了@dbush 的回答,因为它让我走上了正轨。为了完整起见,我 post 更详细的回答,并且按照他的建议,我接受了我自己的回答。
一些代码是在这里找到的:Setting the source IP for a UDP socket
我能够使用单个套接字并设置 IP_PKTINFO 来完成所有这些操作。
代码示例(经过简化,没有错误处理等):
正在创建套接字:
if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) == -1) {
perror("socket");
exit(1);
}
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval) < 0) {
perror("setsockopt");
exit(1);
}
optval2 = 1;
if(setsockopt(sockfd, IPPROTO_IP, IP_PKTINFO, &optval2, sizeof(optval2)) < 0) {
perror("setsockopt");
exit(1);
}
memset(&my_addr, 0, sizeof(struct sockaddr_in));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = INADDR_ANY;
my_addr.sin_port = htons(5353);
if (bind(sockfd, (struct sockaddr *)&my_addr,
sizeof(struct sockaddr)) == -1) {
perror("bind");
exit(1);
}
接收和响应:
char buf[MAXBUFLEN];
char cmsgbuf[MAXBUFLEN];
struct iovec iov[1];
iov[0].iov_base=buf;
iov[0].iov_len=sizeof(buf);
struct cmsghdr *cmsg;
struct msghdr message;
message.msg_name=&their_addr;
message.msg_namelen=sizeof(their_addr);
message.msg_iov=iov;
message.msg_iovlen=1;
message.msg_control=cmsgbuf;
message.msg_controllen=MAXBUFLEN;
if ((numbytes = recvmsg(sockfd, &message, 0)) == -1) {
perror("recvfrom");
exit(1);
}
for (cmsg = CMSG_FIRSTHDR(&message); cmsg != NULL; cmsg = CMSG_NXTHDR(&message, cmsg)) {
// ignore the control headers that don't match what we want
if (cmsg->cmsg_level != IPPROTO_IP ||
cmsg->cmsg_type != IP_PKTINFO)
{
continue;
}
struct in_pktinfo *pi = CMSG_DATA(cmsg);
addr = pi->ipi_spec_dst.s_addr;
}
//DO SOME MAGIC
//HERE WE ARE SETTING ADDR - INTERFACE WITH THIS ADDR WILL SEND MULTICAST
sock_opt_addr.s_addr = addr;
setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF, &sock_opt_addr, sizeof(sock_opt_addr));
sendto(sockfd, buffer, len, 0, (struct sockaddr *)&destination, sizeof destination);