У меня есть приложение, которое получает данные из нескольких источников многоадресной передачи на том же порте. Я могу получить данные. Однако я пытаюсь объяснить статистику каждой группы (т.е. полученный msgs, полученные байты), и все данные становятся перепутанными. Кто-либо знает как к решенному эта проблема? Если я пытаюсь посмотреть на адрес отправителя, это не групповой адрес, а скорее IP передающей машины.
Я использую следующие опции сокета:
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr("224.1.2.3");
mreq.imr_interface.s_addr = INADDR_ANY;
setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
и также:
setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse));
Адрес многоадресной рассылки будет адресом получателя, а не адресом отправителя в пакете. Посмотрите IP-адрес получателя.
[Отредактировано, чтобы уточнить, что bind ()
на самом деле может включать адрес многоадресной рассылки.]
Таким образом, приложение присоединяется к нескольким группам многоадресной рассылки и получает сообщения, отправленные любому из них, на тот же порт. SO_REUSEPORT
позволяет привязать несколько сокетов к одному порту. Помимо порта, bind ()
нужен IP-адрес. INADDR_ANY
- это адрес для приема всей почты домена, но также может использоваться IP-адрес, включая многоадресный. В этом случае в сокет будут доставлены только пакеты, отправленные на этот IP-адрес. Т.е. вы можете создать несколько сокетов, по одному для каждой многоадресной группы. bind ()
каждый сокет к (group_addr, port) И присоединяется к 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
или эквивалент, чтобы прочитать их все.
IIRC recvfrom() дает вам разные адреса/порты чтения для каждого отправителя.
Вы также можете поместить заголовок в каждый пакет, идентифицирующий отправителя источника.
Используйте setsockopt()
и IP_PKTINFO
или IP_RECVDSTADDR
в зависимости от вашей платформы, предполагая IPv4. Это в сочетании с recvmsg()
или WSARecvMsg()
позволит вам найти исходный и адрес назначения каждого пакета.
Unix/Linux, обратите внимание, FreeBSD использует IP_RECVDSTADDR
, в то время как оба поддерживают IP6_PKTINFO
для IPv6.
Windows, также имеет IP_ORIGINAL_ARRIVAL_IF