Ваша программа (вероятно) имеет следующие проблемы:
- вы должны использовать bind () вместо connect (), а
- вам не хватает setsockopt (..., IP_ADD_MEMBERSHIP, ...).
Вот пример программы, которая получает многоадресную рассылку.Он использует recvfrom (), а не recv (), но он такой же, за исключением того, что вы также получаете адрес источника для каждого полученного пакета.
Для получения из нескольких групп многоадресной рассылки у вас есть три опции.
Первый параметр : использовать отдельный сокет для каждой группы многоадресной рассылки и связывать () каждый сокет с адресом многоадресной рассылки .Это самый простой вариант.
Второй параметр : использовать отдельный сокет для каждой группы многоадресной рассылки, связывать () каждый сокет INADDR_ANY и использовать фильтр сокетов для фильтрации всех, кроме одной многоадресной рассылки.group.
Поскольку вы связались с INADDR_ANY, вы все равно можете получать пакеты для других групп многоадресной рассылки.Их можно отфильтровать, используя фильтры сокетов ядра:
#include <stdint.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <linux/filter.h>
/**
* Adds a Linux socket filter to a socket so that only IP
* packets with the given destination IP address will pass.
* dst_addr is in network byte order.
*/
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), // A <- IP dst high
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, hi, 0, 3), // if A != hi, goto ignore
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, SKF_NET_OFF + 18), // A <- IP dst low
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, lo, 0, 1), // if A != lo, goto ignore
BPF_STMT(BPF_RET + BPF_K, 65535), // accept
BPF_STMT(BPF_RET + BPF_K, 0) // ignore
};
struct sock_fprog fprog = {
.len = sizeof(filter) / sizeof(filter[0]),
.filter = filter
};
return setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
}
Третий вариант : использовать один сокет для получения многоадресных рассылок для всех групп многоадресной рассылки.
InВ этом случае вы должны сделать IP_ADD_MEMBERSHIP для каждой из групп.Таким образом вы получаете все пакеты в одном сокете.
Однако вам необходим дополнительный код, чтобы определить, к какой группе многоадресной рассылки был адресован полученный пакет.Для этого вам необходимо:
- получить пакеты с помощью recvmsg () и прочитать IP_PKTINFO или эквивалентное сообщение вспомогательных данных.Тем не менее, чтобы recvmsg () выдал вам это сообщение, сначала вы должны
- включить прием сообщений вспомогательных данных IP_PKTINFO с помощью setsockopt ().
Именно то, что вам нужно сделатьзависит от версии протокола 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;
// set content of struct saddr and imreq to zero
memset(&saddr, 0, sizeof(struct sockaddr_in));
memset(&imreq, 0, sizeof(struct ip_mreq));
// open a UDP socket
sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
perror("socket failed!");
return 1;
}
// join group
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;
}
// receive packets from socket
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);
}
}