簡體   English   中英

需要適用於iOS 9的IPv6組播C代碼

[英]Need IPv6 Multicast C code that works on iOS 9

Apple現在要求iOS 9應用程序符合IPv6。 我們大部分都還可以,只是有一些代碼發送UDP廣播-現在在iOS 9中失敗。

我讀到的所有內容都告訴我,UDP多播是在IPv6中執行此操作的正確方法。 我找到了一些示例代碼,但是在我嘗試過的任何版本的iOS或Mac OS X上都無法使用。

該代碼是從程序內部的C / C ++庫中調用的,很難回調到Swift,Obj-C,Java等。此代碼將由我們應用的Mac OS X和Android版本共享。 有人會認為在任何POSIX環境中都可以在C中進行IPv6多播!

在下面的示例中,執行成功直到最后的sendto()調用,該調用實際上發送了UDP消息。 該sendto()失敗,失敗后將errno設置為EBROKENPIPE(22)。

我最好的猜測是我錯過了一些必需的setsockopt()調用,或者使用了錯誤的多播地址。 現在,我很困惑。

這是我正在進行的函數調用(在UDP端口4031上多播“有人在外面嗎?”):

char *msg = "Is anybody out there?";
err = multicast_udp_msg ( "FF01::1111", 4031, msg, strlen(msg) );

這是被調用的代碼:

// Multicasts a message on a specific UDP port.
// myhost - IPv6 address on which to multicast the message (i.e., ourself)
// port - UDP port on which to broadcast the mssage
// msg - message contents to broadcast
// msgsize - length of message in bytes
// Return value is zero if successful, or nonzero on error.

int multicast_udp_msg ( char *myhost, short port, char *msg, size_t msgsize )
{
    int        sockfd, n;
    char    service[16] = { 0 };
    int        err = 0;
    struct addrinfo hints = { 0 }, *res, *ressave;
    struct sockaddr_storage addr = { 0 };

    hints.ai_family = AF_INET6;
    hints.ai_socktype = SOCK_DGRAM;

    sprintf ( service, "%hd", port );
    n = getaddrinfo ( myhost, service, &hints, &res );
    if ( n < 0 )
    {
        fprintf(stderr, "getaddrinfo error:: [%s]\n", gai_strerror(n));
        return -1;
    }

    ressave = res;

    sockfd = socket ( res->ai_family, res->ai_socktype, res->ai_protocol );
    if ( sockfd >= 0 )
    {
        memcpy ( &addr, res->ai_addr, sizeof ( addr ) );
        if ( joinGroup ( sockfd, 0, 8, &addr ) == 0 )
            if ( bind ( sockfd, res->ai_addr, res->ai_addrlen ) == 0 )
                if ( sendto ( sockfd, msg, msgsize, 0, (struct sockaddr *) &addr, sizeof ( addr ) ) < 0 )
                    err = errno;

        close ( sockfd );

        res = res->ai_next;
    }

    freeaddrinfo ( ressave );
    return err;
}

int
joinGroup(int sockfd, int loopBack, int mcastTTL,
         struct sockaddr_storage *addr)
{
    int r1, r2, r3, retval;

    retval=-1;

    switch (addr->ss_family) {
        case AF_INET: {
            struct ip_mreq      mreq;

            mreq.imr_multiaddr.s_addr=
            ((struct sockaddr_in *)addr)->sin_addr.s_addr;
            mreq.imr_interface.s_addr= INADDR_ANY;

            r1= setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_LOOP,
                           &loopBack, sizeof(loopBack));
            if (r1<0)
                perror("joinGroup:: IP_MULTICAST_LOOP:: ");

            r2= setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL,
                           &mcastTTL, sizeof(mcastTTL));
            if (r2<0)
                perror("joinGroup:: IP_MULTICAST_TTL:: ");

            r3= setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
                           (const void *)&mreq, sizeof(mreq));
            if (r3<0)
                perror("joinGroup:: IP_ADD_MEMBERSHIP:: ");

        } break;

        case AF_INET6: {
            struct ipv6_mreq    mreq6;

            memcpy(&mreq6.ipv6mr_multiaddr,
                   &(((struct sockaddr_in6 *)addr)->sin6_addr),
                   sizeof(struct in6_addr));

            mreq6.ipv6mr_interface= 0; // cualquier interfaz

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

            r2= setsockopt(sockfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
                           &mcastTTL, sizeof(mcastTTL));
            if (r2<0)
                perror("joinGroup:: IPV6_MULTICAST_HOPS::  ");

            r3= setsockopt(sockfd, IPPROTO_IPV6,
                           IPV6_JOIN_GROUP, &mreq6, sizeof(mreq6));
            if (r3<0)
                perror("joinGroup:: IPV6_ADD_MEMBERSHIP:: ");

        } break;

        default:
            r1=r2=r3=-1;
    }

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

    return retval;
}

歡迎思想!

-蒂姆

在與Apple進行了反復交流和一些其他討論之后,我們有了一個答案。 但這不是我最初問題的答案。 首先,這是上下文的Apple線程:

https://forums.developer.apple.com/message/71107

事實證明,IPv6多播實際上並不是解決當前實際問題所需的,即在本地Wi-Fi網絡上找到舊式嵌入式設備。 我們確實必須使用IPv4 UDP廣播來做到這一點。 我們的嵌入式設備會忽略IPv6組播數據包,就像地球會忽略通過它的中微子一樣。

Apple給我們提供了一個setockopt()調用,該調用使IPv4 UDP廣播能夠在基礎結構Wi-Fi網絡上的iOS 9中工作。 這是此功能的預期用例。 當廣播無法在Ad Wic Wi-Fi網絡中正常工作時(這似乎是iOS 9的已知問題),Apple還為我們提供了可能的失敗原因。

因此,盡管此處未回答我的原始問題,但根本的問題已解決。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM