2012-04-01 10 views
0

Ich baue ein Overlay-Netzwerk-System, um ein Protokoll zu testen (Uni Research). Ich muss Multicast-Gruppen verbinden und verlassen, um Pakete von verschiedenen Quellen zu empfangen.Multicast-Gruppen Linux - Verbinden und Verlassen

Ich bin nicht sicher über die korrekte Einrichtung des Sockets, so dass ich den Socket schließen und die Multicast-Gruppe verlassen und dann wieder die gleiche Multicast-Gruppe später verbinden kann. Wenn ich versuche, der gleichen Multicast-Gruppe beizutreten, bekomme ich "bind error: Address already in use".

//for setting up individual groups 
int setUpForGroup(struct locgro_node* node, const char* port) 
{ 
    char mcastaddr[INET6_ADDRSTRLEN]; 

    struct in6_addr* full_addr_gro = &(node->group); 
    if(NULL == inet_ntop(AF_INET6, full_addr_gro, mcastaddr, INET6_ADDRSTRLEN)) 
    { 
     printf("error inet_pton, retval: \n"); 
     return -1; 
    } 

    if (buildAdd(mcastaddr, port, AF_INET6, SOCK_DGRAM, &(node->addr_st)) <0) 
    { 
     fprintf(stderr, "get_addr error:: could not find multicast, address=[%s] port=[%s]\n", mcastaddr, port); 
     return -1; 
    } 

    node->sockfd = socket(AF_INET6, SOCK_DGRAM, 0); 

    if (bind(node->sockfd, (struct sockaddr *)&(node->addr_st), sizeof(node->addr_st)) < 0) { 
     perror("bind error:: "); 
     close(node->sockfd); 
     return -1; 
    } 

    if (joinGroup(node->sockfd, 0 , 8, &(node->addr_st)) <0) { 
     close(node->sockfd); 
     return -1; 
    } 

    return 0; 
} 
//internal function 
int joinGroup(int sockfd, int loopBack, int mcastTTL, struct sockaddr_in6 *addr_st) 
{ 
    int r1, r2, r3, retval; 
    retval=-1; 
    struct ipv6_mreq mreq6; 

    memcpy(&mreq6.ipv6mr_multiaddr, &((addr_st)->sin6_addr), sizeof(struct in6_addr)); 

    mreq6.ipv6mr_interface= 0; // allow any interface 

    //set the loopback case 
    r1 = setsockopt(sockfd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loopBack, sizeof(loopBack)); 
    if (r1<0) perror("joinGroup:: IPV6_MULTICAST_LOOP:: "); 

/* 
    setsockopt(sock_fd, IPPROTO_IPV6, SO_REUSEADDR, &mreq6, sizeof(mreq6)); 

    setsockopt(sock_fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &multicast_req, sizeof(multicast_req)); 
*/ 
    //set the time to live for the packets (hops) 
    r2 = setsockopt(sockfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &mcastTTL, sizeof(mcastTTL)); 
    if (r2<0) perror("joinGroup:: IPV6_MULTICAST_HOPS:: "); 

    //add this address to the group 
    r3 = setsockopt(sockfd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6)); 
    if (r3<0) perror("joinGroup:: IPV6_ADD_MEMBERSHIP:: "); 

    if ((r1>=0) && (r2>=0) && (r3>=0)) retval=0; 

    return retval; 
} 


//internal function 
int buildAdd(const char *hostname, const char *service, int family, int socktype, struct sockaddr_in6 *addr_st) 
{ 
    struct addrinfo hints, *res, *ressave; 
    int n, sockfd, retval; 

    retval = -1; 

    memset(&hints, 0, sizeof(struct addrinfo)); 
    hints.ai_family = family; 
    hints.ai_socktype = socktype; 

    n = getaddrinfo(hostname, service, &hints, &res); 

    if (n <0) 
    { 
     fprintf(stderr, "getaddrinfo error: [%s]\n", gai_strerror(n)); 
     return retval; 
    } 

    ressave = res; 

    sockfd=-1; 
    while(res) 
    { 
     sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 

     if (!(sockfd < 0)) 
     { 
      int opval = 1; 
      setsockopt(sockfd, IPPROTO_IPV6, SO_REUSEADDR, &opval, res->ai_addrlen); 

      if (bind(sockfd, res->ai_addr, res->ai_addrlen) == 0) 
      { 
      //to test that the address really is correct 
       close(sockfd); 
       memcpy(addr_st, res->ai_addr, sizeof(*addr_st)); 
       retval=0; 
       break; 
      } 
      perror("build addr : bind error"); 
      close(sockfd); 
      sockfd=-1; 
     } 
     res=res->ai_next; 
    } 

    freeaddrinfo(ressave); //free the struct 

    return retval; 
} 

Dies sind die Funktionen, die ich für den Beitritt zu einer Gruppe verwendet habe. Wenn ich einfach verlassen tun: (die in einer anderen Funktion geschieht)

struct ipv6_mreq mreq6; 
    memcpy(&mreq6.ipv6mr_multiaddr, &(temp_lnode->group), sizeof(struct in6_addr)); 
    mreq6.ipv6mr_interface= 0; // allow any interface 
    setsockopt(temp_lnode->sockfd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)); 

    close(temp_lnode->sockfd); //close socket 

Ich bin eine „Adresse bereits verwendet“ Fehler bekommen, wenn ich versuche, die Adresse in der Build-Adresse Funktion zu binden. Ich habe versucht, das zu lösen, indem ich SO_REUSEADDR gesetzt habe, aber es hat nicht geholfen, auch die DROP_MEMBERSHIP hinzugefügt, aber die Bindung schlägt immer noch fehl.

Muss ich binden, damit es funktioniert? Was soll ich anrufen oder tun, um Gruppen ohne diese Probleme beitreten und verlassen zu können? Ich würde dies in Abständen von 30s tun müssen.

Vielen Dank M

+0

Problem gelöst. Verwenden Sie SO_REUSEADDR beim ersten Erstellen der Gruppen und verwenden Sie DROP_MEMBERSHIP den Trick. Ich bin mir nicht sicher, welches die eigentliche Reparatur war, da ich erkannte, dass ich DROP_MEMBERSHIP in einem Fall nicht sah, wie auch immer ich gelesen habe, dass es ratsam ist, SO_REUSEADDR immer einzustellen, wenn Multicast behandelt wird. Prost, M – unixsnob

+0

@ unixsnob: gut, dass Sie es selbst gelöst haben. Sie können das wahrscheinlich als Ihre Antwort verwenden. Offtopic: Bitte machen Sie Kommentare im Code ungültig, die wirklich trivial sind: zB // free the struct hat keinen Wert. – Jayan

Antwort

1

roblem gelöst. Verwenden Sie SO_REUSEADDR, wenn Sie die Gruppen zum ersten Mal erstellen und DROP_MEMBERSHIP verwenden. Ich bin mir nicht sicher, welches die eigentliche Reparatur war, da ich erkannte, dass ich DROP_MEMBERSHIP in einem Fall nicht sah, wie auch immer ich gelesen habe, dass es ratsam ist, SO_REUSEADDR immer einzustellen, wenn Multicast behandelt wird. Prost, M