禁用UDP多播的自我接收

时间:2017-08-17 00:26:31

标签: c sockets network-programming udp multicast

我正在努力了解IP多播。特别是,我想禁止将数据包循环回多播组。

我只是将这些代码放在一起,以示例我所看到的。

/*

example.c

 */

#include <sys/types.h>

#include <net/if.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#include <pthread.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>

#define EXAMPLE_PORT 6000
#define EXAMPLE_GROUP "224.0.0.1"

const long MYSENDER = 0; // send thread ID
const long MYRECVR = 1; // recv thread ID

int status;
int sock;

void *sender(void *threadid);
void *receiver(void *threadid);

main(int argc) {
    pthread_t threads[2];
    struct sockaddr_in addr;

    /* Set up socket */
    sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (sock == -1) {
        perror("socket");
        exit(1);
    }

    status = pthread_create(&threads[MYRECVR], NULL, receiver, (void *) MYRECVR); // Start the server thread and check for error
    if (status) {
        fprintf(stderr, "Error: pthread_create(receiver) returned %d\n", status);
        exit(-1);
    }

    status = pthread_create(&threads[MYSENDER], NULL, sender, (void *) MYSENDER); // Start the client thread and check for error
    if (status) {
        fprintf(stderr, "Error: pthread_create(sender) returned %d\n", status);
        exit(-1);
    }

    pthread_join(threads[MYRECVR], NULL); // main() will wait for the server thread to complete
    pthread_join(threads[MYSENDER], NULL); // main() will wait for the client thread to complete
}

void *sender(void *threadid) {
    int c;
    char message[50];
    struct sockaddr_in addr;
    int addrlen, cnt;

    memset((char *) &addr, 0, sizeof (addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_addr.s_addr = inet_addr(EXAMPLE_GROUP);
    addr.sin_port = htons(EXAMPLE_PORT);
    addrlen = sizeof (addr);

    fprintf(stderr, "Starting sender thread!\n");

    /* Send */
    while (1) {
        time_t t = time(0);
        sprintf(message, "time is %-24.24s", ctime(&t));
        printf("sending: %s\n", message);
        cnt = sendto(sock, message, sizeof (message), 0,
                (struct sockaddr *) &addr, addrlen);
        if (cnt < 0) {
            perror("sendto");
            exit(1);
        }
        sleep(5);
    }
    return 0;
}

void *receiver(void *threadid) {
    int opt = 1;
    char message[50];
    struct sockaddr_in addr;
    int addrlen, cnt;
    struct ip_mreq mreq;

    fprintf(stderr, "Starting receiver thread!\n");

    memset((char *) &addr, 0, sizeof (addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(EXAMPLE_PORT);
    addrlen = sizeof (addr);


    /* Receive */

    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt));
    if (bind(sock, (struct sockaddr *) &addr, sizeof (addr)) < 0) {
        perror("bind");
        exit(1);
    }

    mreq.imr_multiaddr.s_addr = inet_addr(EXAMPLE_GROUP);
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
    if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
            &mreq, sizeof (mreq)) < 0) {
        perror("setsockopt mreq");
        exit(1);
    }

    while (1) {
        cnt = recvfrom(sock, message, sizeof (message), 0,
                (struct sockaddr *) &addr, &addrlen);
        if (cnt < 0) {
            perror("recvfrom");
            exit(1);
        } else if (cnt == 0) {
            break;
        }
        printf("%s: message = \"%s\"\n", inet_ntoa(addr.sin_addr), message);
    }
    return 0;
}

运行正常并产生以下输出......

picozed@ubuntu:/tmp$ ./test
Starting receiver thread!
Starting sender thread!
sending: time is Wed Aug 16 19:23:29 2017
10.249.27.51: message = "time is Wed Aug 16 19:23:29 2017"
sending: time is Wed Aug 16 19:23:34 2017
10.249.27.51: message = "time is Wed Aug 16 19:23:34 2017"
^C
picozed@ubuntu:/tmp$

但是,我想阻止这些消息被重新载入......

1 个答案:

答案 0 :(得分:1)

默认情况下,大多数操作系统会将传出的多播流量循环回本地计算机。如果要禁用此行为,则需要将IP_MULTICAST_LOOP套接字选项设置为0。

int opt=0;
if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, &opt, sizeof (opt)) < 0) {
    perror("setsockopt");
    exit(1);
}