簡體   English   中英

C在所有接口上偵聽多播,響應與收到的一樣

[英]C listen to multicast on all interfaces, respond on same as recieved

我試圖在系統中的所有接口上偵聽多播,但只響應我收到組播數據包的情況。

我所做的是為每個接口創建一個套接字,這里問題就出現了。

當我將接口綁定到INADDR_ANY ,它將接收所有接口的數據包並發送默認接口。 如果我將端口綁定到特定接口,它將不會接收多播數據包(但它將能夠在正確的接口上發送)。

我嘗試過設置IP_ADD_MEMBERSHIPIP_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。 它不應該在其他ifs上發送,因為它可能與它們無關,但它應該將其作為多播發送,因此同一網絡中的任何其他主機都將收到響應。

你應該只需要一個插座。

首先綁定到INADDR_ANY和您選擇的端口。 然后在要接收多播的每個接口上使用IP_ADD_MEMBERSHIP調用setsockopt 最后,在要發送多播的接口上調用setsockoptIP_MULTICAST_IF 確保檢查每次通話時的錯誤。

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的回答,因為它允許我走上正軌。 為了完整起見,我發布了更詳細的答案,正如他所建議我接受了我自己的答案。

這里找到了一些代碼: 設置UDP套接字的源IP

我能夠使用單個套接字並設置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);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM