2011-02-10 6 views
3

Кажется, что ошибка в реализации MulticastReceiver.Дублирующие пакеты в многоадресном приемнике Socket

При создании двух экземпляров для < 224.0.25.46,13001> и < 224.0.25.172,13001>, я получаю каждый пакет дважды в каждом потоке. Любые указатели? Мое предположение - 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; 
} 

Просьба сообщить, пожалуйста, о любых улучшениях, которые могут быть сделаны.

ответ

1

Один из подходов, который вы можете предпринять, заключается в том, чтобы быть умным о том, как вы присоединяетесь к группам, вместо того чтобы создавать сокет, связывать (с REUSEADDR), а затем присоединяться к группе для каждой пары ip, port, создавать только один сокет и привязать к данному порту, а затем выпустить несколько IGMP-соединений в одном и том же сокете.

т. Е. В вашем случае создается только один сокет, вы привязываетесь один раз на порт, но вы объединяете несколько групп. Единственное различие заключается в том, что при выдаче запроса на чтение вы получаете пакет от одной или другой группы, и вам нужно иметь достаточно данных в пакете, чтобы вы могли различать.

1

Я предполагаю, что это связано с несколькими интерфейсами (вы присоединяетесь к группе на INADDR_ANY). Попробуйте указать точный интерфейс. Получите адрес интерфейса через ioctl(2) с SIOCGIFADDR. Проверьте, к каким группам вы присоединились в каком интерфейсе с netstat -ng.

1

Это функции маршрутизации Linux, для каждой сессии вам нужна уникальная группа портов/многоадресной рассылки, Linux будет перенаправлять на что угодно, пока порт соответствует, например, широковещательные пакеты. Windows неожиданно не имеет этого симптома, который, по-видимому, почему он медленнее.

Многие коммерческие пакеты промежуточного программного обеспечения обеспечивают соблюдение этого требования для совместимости, например, Rendezvous TIBCO не разрешает повторное использование одного и того же порта или группы.

+0

Пара <224.0.25.46,13001> и <224.0.25.172,13001> предназначена для подачи питания и B-потока одного и того же потока пакетов, и в идеале требуется обработать пакет из того потока, который он набирает первым , Но инициализация двух экземпляров MulticastReceiverSocket и вызов ReadN() фактически обеспечивает 4 идентичных пакета для каждого экземпляра. –

+0

С Linux 2.6.31 появилась новая функция, позволяющая отключить optioin IP_MULTICAST_ALL (см. Этот [ответ] (http: // stackoverflow.com/a/20919920/3102264)) – mpromonet

3

Заменить

mcast_Addr.sin_addr.s_addr = htonl(INADDR_ANY); 

с

mcast_Addr.sin_addr.s_addr = inet_addr (mc_addr_str); 

это помощь для меня (Linux), для каждого приложения я получить отдельный поток mcast из отдельной mcast группы по одному порту.

Также вы можете посмотреть источник проигрывателя VLC, он показывает много каналов mcast iptv из разных групп mcast на одном порту, но я не знаю, как он разделяет канал.

+1

@Stephan и Helius: это сработало для меня. Большое спасибо. –

1

Вы пробовали просто выключить петлю? Я считаю, что если у меня есть разумный TTL, не шлейфу не требуется, чтобы получить один, по крайней мере при использовании 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); 

Если я оставлю замыкания на себя включен - как это по умолчанию - я получаю двойной пакеты тоже.