linux下获取局域网内可用IP的C语言实现

前言

由于项目需要,了解到这部分内容,查了很多资料,没看到合适的。最后参考网上资料再加上自己的尝试,终于解决问题。
需求:NVR 在搜索IPC时,判断我们的IPC是否和NVR在同一网段,如果是检查是否有IP 冲突,如果有IP冲突,重新分配一个可用的IP。如果不在同一网段,重新分配一个在同一网段的可用IP。(且有多个 IPC 同时存在的情况)
解决方案:首先通过 onvif 协议获取 NVR IP 地址。假设 NVR 是C类网络,并且 为192.168.x.x 格式。且子网掩码为255.255.255.0 。得到 NVR IP 以后,判断是否符合假设如果不是不进行进一步处理。然后拿本 IPC 的 IP 和NVR 的 IP 分别和子网掩码做按位与操作,然后比较二者的值。如果在同一网段,检查是否有IP冲突,有冲突的话获取本地可用 IP 并分配;然后再检查,如此循环最多五次。如果不在同一网段获取本地可用IP并分配,检查是否有 IP 冲突,如果有冲突,再分配再检查,循环最多五次。

具体函数实现
1、判断本机是否和局域网内其它设备有 IP 冲突

/*description check if there is ip conflict
 *return 1 ip conflict, 0 not
 */
int check_if_ip_conflict()
{
    char arrcCmd[60] = {0};
    char *pIP = NULL;
    int iConflictFlag = 0;      

    struct in_addr stIPLocal = g_pIPCamSystemInfo->lan_config.net.ip;	// this var is an environmnet which holds the IP address of current device.
    pIP = inet_ntoa(stIPLocal);
    printf("current local ip is %s\n", pIP);
    
    sprintf(arrcCmd, "arping -D -c 1 %s > /mnt/mtd/etc/null", pIP);

    int ret = system(arrcCmd);
    if(-1 != ret && 1 == WEXITSTATUS(ret)) 
    {
        iConflictFlag = 1;
    }

    return iConflictFlag;

}

注:实现主要利用了 arping 命令。 -D 检测是否有 IP 冲突。原理是 arping 向局域网内发送广播时,自己不会回复。如果 arping 了自己的 IP 收到回复,说明有其它设备使用了相同的IP。-c 指定发送请求包的次数,指定为最小值 1 减少arping 命令的阻塞时间。这些参数的意思都可在 linux shell 中端输入 arping 获得。> 意思是把命令的结果重定向到文件 /mnt/mtd/etc/null 中去,且覆盖之前的信息。
arping -D 收到回复时返回 1 。这个可以在 shell 终端测试,shell 命令的返回值,可以在执行完后,在终端输入 $? 获得。
WEXITSTATUS() 宏 通过 system 的返回值,获取shell 命令的返回值。

2、获取一个随机数作为主机号

static int get_random_host_num()
{
    struct drand48_data randbuf;
    struct timeval  tv;
	gettimeofday(&tv, NULL );
	srand48_r(tv.tv_usec, &randbuf);
	double x;
    drand48_r(&randbuf, &x);
    int iHostNum = (int)(x*1000000)%254 + 1;    // range [1,254]
    printf("radom host num is %d\n",iHostNum);
    if(iHostNum < 2)
    {
        iHostNum = 2;   // ignore host num 1;
    }
	return iHostNum;
    
}

注:由于有多个 IPC 同时在查找可用 IP,为避免每个 IPC 查找到的 IP 互相重复,使用随机作为主机号以减少重复的概率。获取随机数也可以使用 srand() 和 rand() 这种简单的实现,drand48_r() 随机性更好一些。

3、查找局域网内的可用 IP

/*description find an available ip 
 *@iHostNum random number in [2,254] used as host number
 *@ucIPPart3 the 3rd part of a numbers-and-dots notation ip address.
 *@pstNet a struct to store ip and gateway
 *return 1 available ip has been found, 0 not.
 */

typedef struct 
{
	struct in_addr ip;
	struct in_addr gateway;
} IP_GATE_LAN;

int find_available_ip(int iHostNum, const unsigned char ucIPPart3, IP_GATE_LAN *pstNet)
{
    char arrcCmd[60] = {0};
    char arrcDottedIP[16] = {0};
    int iFindFlag = 0;

    // traverse ip addresses
    for(int i = iHostNum; i < 255; i++)
    {           
        memset(arrcCmd, 0, sizeof(arrcCmd));
        sprintf(arrcDottedIP, "192.168.%d.%d", ucIPPart3, i);
        sprintf(arrcCmd, "arping -c 1 %s > /mnt/mtd/etc/null", arrcDottedIP);
        int ret = system(arrcCmd);
        
        if(-1 != ret && 1 == WEXITSTATUS(ret))    // here dest ip is not alive. ping returns 0 if dest host is alive, 1 if not.
        {            
            iFindFlag = 1;
            printf("the first aviliable IP is %s\n", arrcDottedIP);           
            break;
        }
        
    }

    if(1 == iFindFlag)
    {
        char arrcGateway[16] = {0};
        sprintf(arrcGateway,"192.168.%d.1", ucIPPart3);
        pstNet->ip.s_addr = inet_addr(arrcDottedIP);
        pstNet->gateway.s_addr = inet_addr(arrcGateway);                
    }
    
    return iFindFlag;
    
}

注:这里的关键还是对 arping 命令的使用,开始想使用 ping 命令后来发现 ping 命令无法跨网段通信。网上看到有人说 arping 命令,测试可以。
arping x.x.x.x 如果主机 是 alive ,只需要 1 到 2 毫秒, not alive , -c 1,应该在 1 秒以内,这是我能控制的最短时间了。-w 参数可以指定超时时间,但最小值只能是 1 秒。arping 已经可以 send 两次了。总体上效率还是比较高的。

4、 设置 IP 和 gateway

int set_ip_and_gateway(const IP_GATE_LAN *pstNet);

注: 这里主要调用了公司的函数实现,就不列出来了。

5、循环检测 IP 冲突 和 查找并设置 IP

/*decription check if the IP of ipc is in same net segment with that of nvr, 
 *if not find an available ip addr and set.
 *@cuiIPNVR ip of nvr from onvif
 */
int keep_ip_with_nvr_net(const unsigned int cuiIPNVR)
{    
    
    unsigned int uiNetMask = inet_addr("255.255.255.0");
    unsigned int uiIPLocal = g_pIPCamSystemInfo->lan_config.net.ip.s_addr;
    IP_GATE_LAN stNet = {0};
    int iRet = 0; //return value;
    
    const unsigned char *pcucIP = (const unsigned char *)&cuiIPNVR;
    printf("nvr ip is %d.%d.%d.%d\n", *pcucIP, *(pcucIP + 1), *(pcucIP + 2), *(pcucIP + 3));
    unsigned char ucIPPart3 = *(pcucIP + 2);
    int iHostNum = 2;
    int i = 0;
    
    if((uiNetMask & uiIPLocal) == (uiNetMask & cuiIPNVR))
    {
        printf("local ip is in the same net segment with nvr ip\n");
        // check if ip conflict        
        while(check_if_ip_conflict())
        {
            i++;
            printf("while has cycled %d times \n", i);
            
            iHostNum = get_random_host_num();
            iRet = find_available_ip(iHostNum, ucIPPart3, &stNet);
            if(1 == iRet)
            {
                set_ip_and_gateway(&stNet);

            }
            else
            {
                iRet = find_available_ip(2, ucIPPart3, &stNet);
                if(1 == iRet)
                {
                    set_ip_and_gateway(&stNet);
                }
                else
                {
                    printf("There is no available IP left in LAN\n");
                    return 1;

                }
                
            }
            if(i >= 5)
            {
                printf("ip conflict can not handle automaticaly,please handle manualy\n");
                return -1;
            }
           

        }
        
        return 0;
        
    }

    else    
    {   
        do
        {
            i++;
            printf("do while has cycled %d times \n", i);
            
            iHostNum = get_random_host_num();
            iRet = find_available_ip(iHostNum, ucIPPart3, &stNet);
            if(1 == iRet)
            {
                set_ip_and_gateway(&stNet);

            }
            else
            {
                iRet = find_available_ip(2, ucIPPart3, &stNet);
                if(1 == iRet)
                {
                    set_ip_and_gateway(&stNet);
                }
                else
                {
                    printf("There is no available IP left in LAN\n");
                    return 1;

                }
                
            }
            if(i >= 5)
            {
                printf("ip conflict can not handle automaticaly,please handle manualy\n");
                return -1;
            } 
        }
        while(check_if_ip_conflict());
        
        return 0;
        
    }

    return 0;
   
}
/*added by joden 20200514  end*/

总结
此博客的实现也是本人摸着石头过河一点点摸索出来的,由于本人对网络编程了解甚少,可能有的代码看起来比较蠢。不过不管怎样我最终实现了客户的需求,写出了让自己满意的代码。在此记下,以便日后回顾,也希望给有这方面需求的新人一点帮助,希望别人不要像我那样,很久都找不到可以参考的资料。
Joden 20200517

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值