您的程序(可能)存在以下问题:
- 您应该使用bind()而不是connect(),
- 您缺少setsockopt(..., IP_ADD_MEMBERSHIP, ...)。
这里有一个接收多播消息的示例程序。它使用recvfrom()而不是recv(),但本质是相同的,只不过您还可以获取每个接收到的数据包的源地址。
要从多个多播组接收,请选择三种选项之一。
第一种选择:为每个多播组使用单独的套接字,并将每个套接字绑定到一个多播地址。这是最简单的选择。
第二种选择:为每个多播组使用单独的套接字,将每个套接字绑定到INADDR_ANY,并使用套接字过滤器来过滤所有除一个多播组之外的所有内容。
因为您已经绑定到了INADDR_ANY,所以仍然可能会收到其他多播组的数据包。但是,可以使用内核的套接字过滤器将它们过滤掉:
#include <stdint.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <linux/filter.h>
int add_ip_dst_filter (int fd, uint32_t dst_addr)
{
uint16_t hi = ntohl(dst_addr) >> 16;
uint16_t lo = ntohl(dst_addr) & 0xFFFF;
struct sock_filter filter[] = {
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, SKF_NET_OFF + 16),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, hi, 0, 3),
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, SKF_NET_OFF + 18),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, lo, 0, 1),
BPF_STMT(BPF_RET + BPF_K, 65535),
BPF_STMT(BPF_RET + BPF_K, 0)
};
struct sock_fprog fprog = {
.len = sizeof(filter) / sizeof(filter[0]),
.filter = filter
};
return setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
}
第三种选择:使用单个套接字接收所有多播组的多播。
在这种情况下,您应该为每个组执行IP_ADD_MEMBERSHIP。这样,您就可以在一个套接字上获取所有数据包。
但是,您需要额外的代码来确定接收到的数据包被寻址到哪个多播组。要做到这一点,您必须:
- 使用recvmsg()接收数据包并读取IP_PKTINFO或等效的辅助数据消息。但是,要使recvmsg()给您提供此消息,您首先必须
- 使用setsockopt()启用接收IP_PKTINFO辅助数据消息。
您需要执行的确切操作取决于IP协议版本和操作系统。以下是我执行此操作的方式(IPv6代码未经过测试):启用PKTINFO和读取选项。
这是一个简单的接收多播的程序,演示了第一种选择(绑定到多播地址)。
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define MAXBUFSIZE 65536
int main (int argc, char **argv)
{
if (argc != 4) {
printf("Usage: %s <group address> <port> <interface address>\n", argv[0]);
return 1;
}
int sock, status, socklen;
char buffer[MAXBUFSIZE+1];
struct sockaddr_in saddr;
struct ip_mreq imreq;
memset(&saddr, 0, sizeof(struct sockaddr_in));
memset(&imreq, 0, sizeof(struct ip_mreq));
sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
perror("socket failed!");
return 1;
}
imreq.imr_multiaddr.s_addr = inet_addr(argv[1]);
imreq.imr_interface.s_addr = inet_addr(argv[3]);
status = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(const void *)&imreq, sizeof(struct ip_mreq));
saddr.sin_family = PF_INET;
saddr.sin_port = htons(atoi(argv[2]));
saddr.sin_addr.s_addr = inet_addr(argv[1]);
status = bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in));
if (status < 0) {
perror("bind failed!");
return 1;
}
while (1) {
socklen = sizeof(saddr);
status = recvfrom(sock, buffer, MAXBUFSIZE, 0, (struct sockaddr *)&saddr, &socklen);
if (status < 0) {
printf("recvfrom failed!\n");
return 1;
}
buffer[status] = '\0';
printf("Received: '%s'\n", buffer);
}
}