Duplicate packets in Multicast Receiver Socket [du

2020-07-18 23:50发布

问题:

There seems to be a bug in the following MulticastReceiver implementation.

On creating two instances for <224.0.25.46,13001> and <224.0.25.172,13001>, I get each packet twice in each stream. Any pointers ? My guess is REUSEADDR ?

class MulticastReceiverSocket {
  protected:
    const std::string listen_ip_;
    const int listen_port_;
    int socket_file_descriptor_;
  public:

  MulticastReceiverSocket ( const std::string & listen_ip, 
                            const int listen_port )
    : listen_ip_ ( listen_ip ), listen_port_ ( listen_port ), 
      socket_file_descriptor_ ( -1 )
  { 

  /* create socket to join multicast group on */
  socket_file_descriptor_ = socket ( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
  if ( socket_file_descriptor_ < 0 ) 
    { exit(1); }

  /* set reuse port to on to allow multiple binds per host */
  {
    int flag_on = 1;
    if ( ( setsockopt ( socket_file_descriptor_, SOL_SOCKET, 
                        SO_REUSEADDR, &flag_on,
                        sizeof(flag_on) ) ) < 0 ) 
      { exit(1); }
  }

  McastJoin ( );

  {
    /* construct a multicast address structure */
    struct sockaddr_in mcast_Addr;
    bzero ( &mcast_Addr, sizeof(mcast_Addr) );
    mcast_Addr.sin_family = AF_INET;
    mcast_Addr.sin_addr.s_addr = htonl(INADDR_ANY);
    mcast_Addr.sin_port = htons ( listen_port_ );

    /* bind to specified port onany interface */
    if ( bind ( socket_file_descriptor_, (struct sockaddr *) &mcast_Addr, sizeof ( struct sockaddr_in ) ) < 0 ) 
       { exit(1); } 
  }
}

void McastJoin ( )
{ 
    /* construct an IGMP join request structure */

    struct ip_mreq mc_req;
    inet_pton ( AF_INET, listen_ip_.c_str(), &(mc_req.imr_multiaddr.s_addr) ); 
    mc_req.imr_interface.s_addr = htonl(INADDR_ANY);

    /* send an ADD MEMBERSHIP message via setsockopt */
    if ( ( setsockopt ( socket_file_descriptor_, IPPROTO_IP, IP_ADD_MEMBERSHIP, 
                (void*) &mc_req, sizeof(mc_req))) < 0) 
    {
      printf ("setsockopt() failed in IP_ADD_MEMBERSHIP %s\n", listen_ip_.c_str() );
      exit(1);
    } 

}

inline int ReadN ( const unsigned int _len_, void * _dest_ ) 
{
  if ( socket_file_descriptor_ != -1 )
    {
      return recvfrom ( socket_file_descriptor_, _dest_, _len_, 0, NULL, NULL );
    }
  return -1;
}

Please advise, and of course, please point out any improvements, optimizations that can be made.

回答1:

One approach you can take is to be smart about how you join the groups, so rather than create a socket, bind (with REUSEADDR) and then join group for each pair of ip, port, only construct a single socket and bind to a given port, and then issue multiple IGMP joins on the same socket.

i.e. in your case, only one socket is created, you bind once per port, but you join multiple groups. The only difference is that when you issue the read call, you will get a packet from one or the other group and you need to have enough data in the packet to allow your to distinguish.



回答2:

Replace

mcast_Addr.sin_addr.s_addr = htonl(INADDR_ANY);

with

mcast_Addr.sin_addr.s_addr = inet_addr (mc_addr_str);

it's help for me (linux), for each application i receive separate mcast stream from separate mcast group on one port.

Also you can look into VLC player source, it show many mcast iptv channel from different mcast group on one port, but i dont know, how it separetes channel.



回答3:

I'm guessing this is because of multiple interfaces (you join the group on INADDR_ANY). Try specifying exact interface. Get the interface address via ioctl(2) with SIOCGIFADDR. Check what groups you joined on what interface with netstat -ng.



回答4:

It's a feature of Linux routing, you need a unique port/multicast group for each session, Linux will forward on anything as long as the port matches, for example broadcast packets too. Windows surprisingly doesn't have this symptom, which is presumably why it is slower.

Many commercial middleware packages enforce this requirement for compatibility, for example TIBCO's Rendezvous will not permit reusing of the same port or group.



回答5:

Have you tried just turning loopback off? I find that if I have a reasonable TTL, no loopback is required to get a single, at least when using SO_REUSEPORT:

int sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
if( sock < 0 )
    exit( -11 );

int on = true;
if( setsockopt ( sock, SOL_SOCKET, SO_REUSEPORT, & on, sizeof( on ) ) < 0 ) 
    exit( -12 );

int off = 0;
if ( setsockopt ( sock, IPPROTO_IP, IP_MULTICAST_LOOP, & off, sizeof( off ) ) < 0 )
    exit( -13 );

int ttl = 3;
if ( setsockopt ( sock, IPPROTO_IP, IP_MULTICAST_TTL, & ttl, sizeof( ttl ) ) < 0 )
    exit( -14 );

If I leave loopback turned on--as it is by default--I get double packets too.