Cocos2d-x开发系列 苹果IPv6 only问题处理

2016年6月1日,苹果出台新政策,要求所有提交Apple Store渠道的包必须要支持IPv6。这昂无疑让很多人心头一凉。
果然在这几天很多公司的产品都被打回来了。我们公司也不例外,提交了几个包,只有一款侥幸逃过一劫,其它全部被打回。
碰到这种问题,第一时间就去调查了下,首先肯定是去构建一个ipv6网络测试。面临着一大堆的问题,这个是客户端还是服务端的问题?如果是服务端的问题,怎么处理,服务端需要更改什么内容?如果是客户端的问题又怎么办呢?……
慌归慌,事情还是要去处理,首先去百度科普下苹果的这条新规的规则,然后再去熟悉下什么是ipv6(尴尬脸~)。这个就不赘述了,自行度娘。

怎么搭建ipv6的网络?
这个估计网上搜索一大堆,简单描述下。苹果机器是可以分享一个ipv6网络热点的。首先找一台苹果机器,连上网线(必须是以太网),然后再设置界面按option键,单机分享,就可以在互联网共享看到,创建NAT64网络的checkbox,我们勾选上,然后分享热点就可以了。一顿测试,发现很多游戏不能正常玩下去。

服务端需要修改吗?
服务端是否要修改,这个问题估计是大家比较关注的问题,因为如果服务端需要修改,那就麻烦了(可能其实并不麻烦~ 欠揍脸)。正好公司有oc写的客户端,拿了一个包在ipv6环境下测试,是正常的(因为实用了CFNETWork,苹果提供的兼容ipv6的网络库)。所以看得出来,服务端基本不需要修改。

需要在域名绑定的时候修改一个ipv6地址吗?
一开始我也纠结了下这个问题,是不是需要去绑定一个ipv6的地址。后来用oc写的客户端可以连接正常,说明基本没问题,不需要特别操作。那么基本得出结论,只要在客户端修改即可。

怎么改?
cocos2d-x 第一时间就更新了自身的curl和web socket。 http://forum.cocos.com/t/ipv6-only/36895

 根据苹果官网提出的修改策略,我们的http、socket都需要支持。所以我们可以更新cocos团队提供的patch。简单解决http的问题。
 现在最麻烦的事去修改socket,因为我们使用了BSDSocket的封装。网上并不难查到相关的资料,虽然基本都是如出一辙。

修改Socket
IPv4是“.”来分割的,但是IPv6是“:”来分割的。客户端如果是用苹果家族的语言开发的,我们基本不需要修改,或者是很少量的修改,因为他们本身提供的库就只支持。但是Cocos2d-x开发,我们一般会自行封装自己的Socket。一般来说是BSDSocket的使用。这时,我们就需要去做如下工作了。

一、将项目中的ip地址全部改成域名,因为IPv4的地址跟IPv6的地址不一样。另外解析域名的时候不再使用 gethostbyname函数,而是使用 getaddrinfo函数。添加一个网络判断的函数。

 //判断是否IPv6网络
staticbool isIPV6Net(conststd::string domainStr = "www.baidu.com")
{
    bool isIPV6Net = false;

    struct addrinfo *result = nullptr,*curr;

    struct sockaddr_in6 dest;
    bzero(&dest, sizeof(dest));

    dest.sin6_family  = AF_INET6;

    int ret = getaddrinfo(domainStr.c_str(),nullptr,nullptr,&result);
    if (ret == 0)
    {
        for (curr = result; curr != nullptr; curr = curr->ai_next)
        {
            switch (curr->ai_family)
            {
                case AF_INET6:
                {
                    isIPV6Net = true;
                    break;
                }
                case AF_INET:

                    break;

                default:
                    break;
            }
        }
    }

    freeaddrinfo(result);

    return isIPV6Net;
}

二、添加域名解析函数

static std::string domainToIP(const char* pDomain)
{
    if (isIPV6Net())
    {
        struct addrinfo hint;
        memset(&hint, 0x0, sizeof(hint));
        hint.ai_family = AF_INET6;
        hint.ai_flags = AI_V4MAPPED;

        addrinfo* answer = nullptr;
        getaddrinfo(pDomain, nullptr, &hint, &answer);

        if (answer != nullptr)
        {
            char hostname[1025] = "";

            getnameinfo(answer->ai_addr,answer->ai_addrlen,hostname,1025,nullptr,0,0);

            char ipv6[128] = "";
            memcpy(ipv6,hostname,128);

            CCLOG("domainToIP addrStr:%s", ipv6);

            return ipv6;
        }

        freeaddrinfo(answer);
    }
    else
    {
        struct hostent* h = gethostbyname(pDomain);
        if( h != NULL )
        {
            unsigned char* p = (unsigned char *)(h->h_addr_list)[0];
            if( p != NULL )
            {
                char ip[16] = {0};
                sprintf(ip, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]);
                return ip;
            }
        }
    }
    return "";
}

三、修改Socket初始化函数

m_uSocket = socket((isIPV6Net()?AF_INET6:AF_INET), SOCK_STREAM, IPPROTO_TCP);

四、Socket连接函数
Connect函数的第二个参数的获取

struct sockaddr* WWInetAddress::getSockaddr() const
{
    return m_isNetWorkIpv6 ? (struct sockaddr*)&addr_v6 : (struct sockaddr*)&addr_v4;
}

第三个参数获取

int WWInetAddress::getLength()
{
    return m_isNetWorkIpv6 ? sizeof(sockaddr_in6) : sizeof(sockaddr_in);
}

连接Socket

int nRet = ::connect(m_uSocket, m_oInetAddress.getSockaddr(), m_oInetAddress.getLength());

五、其它需要注意事项
Socket发送接口及接收数据接口都不需要去处理,另外需要考虑的是Socket的重连,及网络切换的问题。不过这些都是流程代码,只要稍加注意即可。

你可能感兴趣的:(Cocos2d-x)