单网卡多IP导致的socket connect 10060超时错误

问题:

接管别人代码时遗留的一个bug,在win7下,给一个网卡设置多个ip时,发现无法连接上服务器了。XP下就不会,这多个ip为192.168.1.127,172.1.1.13,10.0.0.1。

复现过程:

复现的过程中我的服务器ip为192.168.1.128,子网掩码255.255.255.0,默认网关为192.168.1.128(服务器不需要联网,所以就这么设置了)。

客户端按照bug说明设置了对应的3个IP。

发现无法复现,然后和测试沟通要求重现,发现他那边设置这样的IP是会出现的。因此比较了下他的服务器IP和我的服务器IP的区别,竟然是默认网关的问题,设置成192.168.1.1就能复现了。(后来发现设置成其他的也可以)。

解决过程:

首先恶补了一下IP、子网掩码、网关的知识。(都还给老师了)(socket技术也不是很熟悉)

分析的话还是从客户端IP开始,首先3个IP中肯定要存在的是192.168.1.127,因为只有这个IP和服务器同网段,其他两个都是不同网段的,并且刚好占据了A、B、C三类IP。于是试着去掉172网段的IP,发现也会复现。

OK,那基本确定就是因为存在一个不同网段的IP导致连接不上服务器了,于是去看源代码,跟踪发现是在调用getaddrinfo时,传入本主机名称,返回的IP列表中存在两个IP,但是代码里面默认就只使用第一个,这win7下第一个刚好是10.0.0.1,当然就无法和192.168.1.128的服务器通信了。测试了下XP下刚好第一个IP是和服务器同网段的,所以就能通信。(不过这里有个疑问就是服务器默认网关设置成192.168.1.128时仍然能通信,这是为什么?)

因为后面调用connect的地方和getaddrinfo的地方不在同一个层级,而且都已经经过了封装,想要将返回的IP列表全部返回到connect的地方会牵涉到比较大的改动,我比较担心引发新的BUG。所以决定在返回Ip给connect使用之前就过滤出和服务器IP在同一网段的IP。

这里是将服务器IP传进去作为一个参照对象。将返回的IP列表中的IP全部转换成unsigned long类型,然后和服务器IP做差,差值最小的就是最接近服务器IP的(认为是和服务器IP同网段的)。要注意的是,inet_addr函数返回的unsigned long是将高段的值放置在末尾的,所以这里调用了ntohl函数将unsigned long逆序一遍。

代码片段如下:

if ( SOCKET_ERROR != getaddrinfo(pstrHost, pstrService, &aiHints, &aiList) && ( aiList != 0 ))

    {

        // 优先匹配在同一网段的IP地址来使用

        ADDRINFO *aiUse = aiList;

        if(NULL != aiUse->ai_next && NULL != pszRemote && AF_INET == nFamily)    // 只支持ip4的

        {

            char szAddress[MAX_PATH] = { 0 };

            ADDRINFO* aiCrt = aiUse;

            unsigned long lulMinimumDiff = 0xffffffff;

            unsigned long llRemote = ntohl(inet_addr(pszRemote));

            unsigned long llCrt = 0;

            unsigned long llDiff = 0;

            while(NULL != aiCrt)

            {

                // 转换成数值,差值最小的就是ip最接近的

                const void* addr = &((const sockaddr_in*)aiCrt->ai_addr)->sin_addr;

                if (inet_ntop(nFamily, addr, szAddress, MAX_PATH) != NULL)

                {

                    llCrt = ntohl(inet_addr(szAddress));

                    if(llCrt > llRemote)

                        llDiff = llCrt - llRemote;

                    else 

                        llDiff = llRemote - llCrt;



                    if(llDiff < lulMinimumDiff)

                    {

                        lulMinimumDiff = llDiff;

                        aiUse = aiCrt;

                    }

                }



                aiCrt = aiCrt->ai_next;

            }

        }

        ADDRINFO ai = { 0 };

        ai.ai_addr = sockAddr;

        memcpy(ai.ai_addr, aiUse->ai_addr, aiUse->ai_addrlen);

        freeaddrinfo( aiList );

        return true;
}

你可能感兴趣的:(connect)