appstore审核的那些事 --2. IPv6环境下的支持

本文介绍了一个iOS应用在App Store审核中遇到IPv6支持问题后的解决方案。通过将gethostbyname替换为getaddrinfo,并区分处理IPv4和IPv6,实现了网络连通性检测和服务器接口访问的IPv6兼容性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

AppStore审核的时候反馈:

We discovered one or more bugs in your app when reviewed on iPad and iPhone running iOS 10.2 on Wi-Fi connected to an IPv6 network.


因此我需要修改IPv6环境下的支持。使用了socket的时候仅支持了IPv4

1.在网络连通性检测的时候使用了socket

原来的表现:在IPv6环境下显示网络连接不通,包括直接IP访问(192.168.1.22)和域名访问(baidu.com

修改后结果:IP访问和域名访问都显示可以成功访问(都可以直接获取的IPv6格式的地址)

原理不再赘述,详见最下面的链接

主要修改的是:

1 gethostbyname —> getaddrinfo

2)根据getaddrinfoIPv4IPv6的区分

3)添加sockaddr_in6IPv6相关内容

修改后代码:

/**
 @brief     测试连接
 */
+ (void)testConnection:(NSString*)host port:(NSNumber*)port compelete:(nullable void(^)(BOOL isSuccess))compelete {
    if(!host){
        compelete(NO);
    }
    NSOperationQueue *operationQueue  = [[NSOperationQueue alloc]init];
    [operationQueue addOperationWithBlock:^{
        //    NSString * host =@"123.33.33.1";
        //    NSNumber * port = @1233;
        
        // 创建 socket
        int socketFileDescriptor = socket(AF_INET, SOCK_STREAM, 0);
        if (-1 == socketFileDescriptor) {
            NSLogD(@"创建失败");
            [[NSOperationQueue mainQueue]addOperationWithBlock:^{
                compelete(NO);
            }];
            return;
        }
        NSLogD(@"host   %@:%@",host,port);
        
        // 获取 IP 地址
        int error, sockfd;
        const char *cause = NULL;
        struct addrinfo hints, *res, *res0;
        memset(&hints, 0, sizeof(hints));
        hints.ai_family = PF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_flags = AI_DEFAULT;
        error = getaddrinfo([host UTF8String], "http", &hints, &res0);
        NSLogD(@"getaddrinfo    %d",error);
        
        if (0 != error) {
            close(socketFileDescriptor);
            NSLogD(@"%@",@"无法解析服务器的主机名");
            [[NSOperationQueue mainQueue]addOperationWithBlock:^{
                compelete(NO);
            }];
            return;
        }
        sockfd = -1;
        struct sockaddr_in *addr;
        struct sockaddr_in6 *addr6;
        char ipbuf[28];
        for (res = res0; res; res = res->ai_next) {
            sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
            if (sockfd < 0) {
                cause = "socket";
                continue;
            }
            if(AF_INET6==res->ai_family){
                addr6 = (struct sockaddr_in6 *)res->ai_addr;
                addr6->sin6_port = htons([port intValue]);  // 修改端口号
                printf("%s\n", inet_ntop(AF_INET6,  &addr6->sin6_addr, ipbuf, 28));
                // 连接 socket
                if (connect(sockfd, (struct sockaddr *) addr6, sizeof(*addr6)) < 0) {
                    cause = "connect v6";
                    close(sockfd);
                    sockfd = -1;
                    continue;
                }
            }else{
                addr = (struct sockaddr_in *)res->ai_addr;
                addr->sin_port = htons([port intValue]);
                printf("%s\n", inet_ntop(AF_INET,  &addr->sin_addr, ipbuf, 16));
                //            // 设置为非阻塞
                //            fcntl(sockfd,F_SETFL,fcntl(sockfd,F_GETFL,0)|O_NONBLOCK);
                // 连接 socket
                if (connect(sockfd, (struct sockaddr *) addr, sizeof(*addr)) < 0) {
                    //                if (connect(sockfd, res->ai_addr, res->ai_addrlen) < 0) {
                    cause = "connect";
                    close(sockfd);
                    sockfd = -1;
                    continue;
                }
            }
            break;  /* okay we got one */
        }
        if (sockfd < 0) {
            /*NOTREACHED*/
            NSLog(@"error cause    %s", cause);
            [[NSOperationQueue mainQueue]addOperationWithBlock:^{
                compelete(NO);
            }];
            return;
        }
        NSLogD(@"连接成功%@",host);
        close(sockfd);
        [[NSOperationQueue mainQueue]addOperationWithBlock:^{
            compelete(YES);
        }];
        
    }];
}

调用访问网络的方法:

[Utils testConnection:service port:@(port) compelete:^(BOOL isSuccess) {
    if(isSuccess){
        // success
    }else{
        // failure
    }
}];


2.在访问服务端接口的时候用了socket

与上述方法相同,只不过根据需求设置成阻塞/非阻塞的,看最后是否需要调用close(sockfd);关闭此连接


PS. 

1.IPv6环境的搭建

1)用wifihttp://www.jianshu.com/p/632d995749e1

2)用4Ghttp://www.cnblogs.com/SUPER-F/p/IPV6.html

2.官方指导和使用

https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/NetworkingOverview/UnderstandingandPreparingfortheIPv6Transition/UnderstandingandPreparingfortheIPv6Transition.html#//apple_ref/doc/uid/TP40010220-CH213-SW11

3.讲的比较详细的socket使用:https://github.com/WeMobileDev/article/blob/master/IPv6%20socket%E7%BC%96%E7%A8%8B.md#%E6%B7%B7%E5%90%88%E7%9A%84%E6%96%B9%E6%A1%88mac-osioslinuxandroid%E9%83%BD%E6%94%AF%E6%8C%81windowswp%E5%BE%85%E6%B5%8B%E8%AF%95


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值