• 单网卡多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;
    }
  • 相关阅读:
    [Luogu P2563]质数和分解
    数据处理、降维--线性判别分析(linear discriminant analysis LDA)
    endnote导入参考文献及国标(Chinese standard)
    朴素贝叶斯分类器
    R语言--apply家族
    R语言--解决4*x+y之奇技淫巧、重复数字去重
    R语言中级--自定义方程
    R语言--一整套数据处理流程及简单的控制流
    R语言图形初阶
    R基础、进阶-矩阵数据框的操作
  • 原文地址:https://www.cnblogs.com/monotone/p/3988434.html
Copyright © 2020-2023  润新知