[Отредактировано, чтобы уточнить, что bind()
может фактически включать адрес многоадресной рассылки.]
Итак, приложение присоединяется к нескольким группам многоадресной рассылки и получает сообщения, отправленные на любую из них, на один и тот же порт. SO_REUSEPORT
позволяет привязать несколько сокетов к одному порту. Помимо порта, bind()
нужен IP-адрес. INADDR_ANY
- это универсальный адрес, но также можно использовать IP-адрес, включая многоадресный. В этом случае только пакеты, отправленные на этот IP, будут доставлены в сокет. То есть Вы можете создать несколько сокетов, по одному для каждой многоадресной группы. bind()
каждый сокет с (group_addr, порт) И присоединение к group_addr. Тогда данные, адресованные разным группам, будут отображаться в разных сокетах, и вы сможете различить их таким образом.
Я проверял, что на FreeBSD работает следующее:
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/param.h>
#include <unistd.h>
#include <errno.h>
int main(int argc, const char *argv[])
{
const char *group = argv[1];
int s = socket(AF_INET, SOCK_DGRAM, 0);
int reuse = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse)) == -1) {
fprintf(stderr, "setsockopt: %d\n", errno);
return 1;
}
/* construct a multicast address structure */
struct sockaddr_in mc_addr;
memset(&mc_addr, 0, sizeof(mc_addr));
mc_addr.sin_family = AF_INET;
mc_addr.sin_addr.s_addr = inet_addr(group);
mc_addr.sin_port = htons(19283);
if (bind(s, (struct sockaddr*) &mc_addr, sizeof(mc_addr)) == -1) {
fprintf(stderr, "bind: %d\n", errno);
return 1;
}
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr(group);
mreq.imr_interface.s_addr = INADDR_ANY;
setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
char buf[1024];
int n = 0;
while ((n = read(s, buf, 1024)) > 0) {
printf("group %s fd %d len %d: %.*s\n", group, s, n, n, buf);
}
}
Если вы запустите несколько таких процессов для разных многоадресных адресов и отправите сообщение на один из адресов, только соответствующий процесс получит его. Конечно, в вашем случае вы, вероятно, захотите, чтобы все сокеты были в одном процессе, и вам придется использовать select
или poll
или эквивалентный, чтобы прочитать их все.