【问题标题】:Multicasting on the loopback device环回设备上的组播
【发布时间】:2013-04-07 02:28:14
【问题描述】:

我希望将 UDP 多播数据包发送到环回地址并在其他应用程序中接收相同的数据。所有测试均在 fedora core 17 Linux 上完成。

这个想法是通过 RTSP/HTTP 或任何其他网络协议接收视频流,并在环回地址上多播它,以便我可以使用 VLC 使用多播地址播放流。撇开其他比特率和受控多播问题不谈,我尝试在环回设备上读取一个视频文件和多播。但是当试图在 vlc 上玩同样的游戏时,它并没有奏效。我可以看到在wireshark中传输的数据包,但是src ip取自我的默认网络接口(即我的默认网关接口)

我已经尝试过以下命令

sudo ifconfig lo multicast
sudo ip route add 239.252.10.10 dev lo

这方面的任何建议都会非常有帮助。

下面粘贴的测试程序代码

    #include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>


    #define MULTICAST_ADDRESS "239.252.10.10"
    #define UDP_PORT 1234
    #define INTERFACE_IP    "127.0.0.1"
    #define MTU 1474
    #define DATA_BUFFER_SIZE  (1024*1024)

    static int  socket_init(char *intf_ip) {
    int sd;
    struct in_addr localInterface;

      sd = socket (AF_INET, SOCK_DGRAM, 0);
      if (sd < 0) {
          perror ("Opening datagram socket error");
          return -1;
      }
      else
        printf ("Opening the datagram socket...OK.\n");

      localInterface.s_addr = inet_addr (intf_ip);

      if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, (char *) &localInterface,sizeof (localInterface)) < 0){
          perror ("Setting local interface error");
          close(sd);
          return -1;
      }
      else
        printf ("Setting the local interface...OK\n");
    #if 1
        char loopch = 1;

        if(setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopch, sizeof(loopch)) < 0){
        perror("Setting IP_MULTICAST_LOOP error");
        close(sd);
        return -1;
        }
        else
        printf("Enabling the loopback...OK.\n");
    #endif
      return sd;

    }


    static int transmit_packet(int sd, char *databuf, int size,char *ip, unsigned short port){

    struct sockaddr_in groupSock;
    int len,datalen,rc;

      memset ((char *) &groupSock, 0, sizeof (groupSock));

      groupSock.sin_family = AF_INET;

      groupSock.sin_addr.s_addr = inet_addr (ip);

      groupSock.sin_port = htons (port);

      len=0;
      datalen = MTU;
      if(size < MTU)
        datalen = size;

      while(len < size){
        rc = sendto(sd, databuf, datalen, 0, (struct sockaddr *) &groupSock,sizeof (groupSock));
        if(rc <0){
          perror ("Sending datagram message error");
          return -1;
        }
        usleep(10000);
        len += rc;
      }
      return len;
    }

    static int transmit_file(char *filepath, char *dstip, char *srcip,unsigned short port) {
    FILE *fp;
    int sd,rc;
    char *databuf;


        fp = fopen(filepath, "r");
        if(!fp) {
        printf("transmit_file : no such file or directory %s \n",filepath);
        return -1;
        }
        sd = socket_init(srcip);
        if(sd < 0) {
        printf("Socket initialization failed \n");
        fclose(fp);
        return -1;
        }
        databuf = (char*) malloc(sizeof(char)*DATA_BUFFER_SIZE);
        if(!databuf) {
        printf("Unable to allocate databuf\n");
        close(sd);fclose(fp);
        return -1;
        }
        while(!feof(fp)){
        rc = fread(databuf,1,DATA_BUFFER_SIZE,fp);
        if(rc<= 0) {
            printf("read failed or EOF reached\n");
            break;
        }           
        if(transmit_packet(sd,databuf,rc,dstip,port) <0)
            printf("Transmit failed\n");    
        }
        close(sd);fclose(fp);
        free(databuf);
        return 0;
    }

    int main(int argc, char *argv[]){

       if(argc != 3){
        printf("%s <filename> <ip>\n",argv[0]);
        return -1;
       }
       transmit_file(argv[1],argv[2],INTERFACE_IP,UDP_PORT);
       return 0;
    }

【问题讨论】:

    标签: c linux networking multicast


    【解决方案1】:

    您可以在环回上使用多播,但您必须添加新路由,因为您的操作系统默认使用默认外部接口进行多播。默认情况下,也可以在环回时禁用多播。在 linux 上,您可以使用以下命令更改它:

    route add -net 224.0.0.0 netmask 240.0.0.0 dev lo
    ifconfig lo multicast
    

    【讨论】:

    • 它也适用于 ROS2 用户。设置上述命令后尝试以下命令。 1号航站楼:ros2 run demo_nodes_cpp listener,2号航站楼:ros2 run demo_nodes_cpp talker
    【解决方案2】:

    如果您不希望通过网络发送 IP 多播流量(例如 IGMP 消息),则必须绑定或路由到环回设备。但是,这通常仅在网络上存在可能通过使用相同多播组进行干扰的其他计算机时才需要。

    真正的问题是让同一主机上的程序接收彼此发送的多播数据(或者,等效地,使单个程序中的套接字接收彼此发送的多播数据),当它们都配置为使用相同的多播组。

    这是一个很常见的问题,上面有很多 StackOverflow 问题,但它们经常被误解或措辞不当。很难专门针对操作系统行为或标准化来搜索此问题。

    在硬件级别,多播流量被视为广播流量,因为它不会路由回发送它的物理端口,以防止链路级循环。这意味着操作系统负责将流量转发到加入多播组的同一主机上的其他程序或套接字,因为它不会从接口读取。

    这是由标准的IP_MULTICAST_LOOP选项配置的,最好的总结是IP Multicast MSDN article(archived)

    目前,大多数 IP 多播实现都使用 Steve Deering 向 Internet 工程任务组 (IETF) 提出的一组套接字选项。因此提供了五种操作:

    [...]

    • IP_MULTICAST_LOOP—控制多播流量的环回。

    [...]

    IP_MULTICAST_LOOP 选项的 Winsock 版本在语义上与IP_MULTICAST_LOOP 选项的 UNIX 版本不同:

    • 在 Winsock 中,IP_MULTICAST_LOOP 选项仅适用于接收路径。
    • 在 UNIX 版本中,IP_MULTICAST_LOOP 选项适用于发送路径。

    例如,应用程序 ON 和 OFF(比 X 和 Y 更容易[跟踪])在同一界面上加入同一组; application ON 将IP_MULTICAST_LOOP 选项设置为on,application OFF 将IP_MULTICAST_LOOP 选项设置为off。如果 ON 和 OFF 是 Winsock 应用程序,OFF 可以发送到 ON,但 ON 不能发送到 OFF。相反,如果 ON 和 OFF 是 UNIX 应用程序,ON 可以发送到 OFF,但 OFF 不能发送到 ON。

    根据我的阅读,此设置可能在 Windows 上默认禁用,在 Linux 上默认启用,但我自己没有测试过。

    作为重要的附注,IP_MULTICAST_LOOP 选项与IPV6_MULTICAST_LOOP 选项完全不同,请参阅 Linux 的 ip(7)ipv6(7) 手册页:

    IP_MULTICAST_LOOP (Linux 1.2 起) 设置或读取一个布尔整数参数,该参数确定发送的多播数据包是否应循环回本地套接字。

    IPV6_MULTICAST_LOOP 控制套接字是否看到它自己[发送]的多播数据包。参数是一个指向布尔值的指针。

    IP_MULTICAST_LOOP 允许在发送它的同一主机上的不同套接字上接收 IP 多播流量。 IPV6_MULTICAST_LOOP 允许在发送它的同一个套接字上接收 IPv6 多播流量——这在 IPv4 中通常是不可能的。

    如果有人参考了有关实现预期行为的官方标准(RFC、IEEE POSIX 标准等),请将它们发布在 cmets 中或编辑此答案。

    【讨论】:

      【解决方案3】:

      我希望将 UDP 多播数据包发送到环回地址

      停在那里。你不能那样做。不可能。您只能将多播发送到多播地址。您的代码不进行任何多播,只是发送到 127.0.0.1。

      如果你只是发送到本地主机,你为什么要使用多播呢?你有多个监听进程吗?

      src ip 取自我的默认网络接口(即我的默认网关接口)

      很可能,因为您没有绑定您的套接字。你期待什么?

      【讨论】:

      • 多播地址可以从命令行给出,该地址作为目标IP。基本上我想以多播方式将我收到的任何内容传输到环回接口,以便一个应用程序可以接收它以记录在文件中,而另一个应用程序则播放它等等。这就是为 udp 数据包的目标 ip 设置多播目标的原因。
      • 对于接口 src ip,即使我将套接字绑定到 127.0.0.1,它仍然不会在传出数据包中使用它。
      • @EJP 如果我想以 sub/pub 的方式从一个流中读取多个进程怎么办?例如直播视频。
      • 这个答案对于 OP 已经解释的内容没有任何帮助,而且实际上是错误的。
      猜你喜欢
      • 1970-01-01
      • 2011-01-01
      • 2013-01-16
      • 2012-06-28
      • 1970-01-01
      • 2015-01-03
      • 2017-10-24
      • 2014-03-21
      • 1970-01-01
      相关资源
      最近更新 更多