局域网设备自动发现常用方法

news2024/9/24 9:21:08

文章目录

  • 需求
  • 实现方法
    • ARP (Address Resolution Protocol)
      • Ping ip的流程
      • 抓包如下
      • 代码实现
    • mDNS
  • 对比测试
    • Avahi 介绍
    • Avahi 安装
    • Avahi 使用
    • 测试代码

需求

局域网设备自动发现是软件开发中的一个常见且重要的需求,它简化了设备间的协作机制,降低了软件各模块间进行复杂配置的需求。通过实现自动发现功能,不仅显著提升了用户的操作便捷性和满意度,还促进了网络资源的智能化分配与高效利用。为后续的通信和传输奠定了基础。

局域网设备自动发现通常具有以下几个核心功能:

  • 实时性:能够即时探测到局域网中新加入或离开的设备,确保网络环境的实时更新。
  • 广泛性:支持多种类型的设备发现,包括但不限于计算机、打印机、智能家电、网络摄像头等。
  • 易用性:用户无需手动配置或输入设备的IP地址等信息,即可轻松访问和使用这些设备。
  • 安全性:在自动发现设备的同时,也考虑到了网络安全问题,如防止未授权设备的接入等。

实现方法

ARP (Address Resolution Protocol)

ARP是一种协议,用于将 IP地址解析成 MAC 地址。当主机想要与同一局域网内的另一台主机通信时,它需要知道目标主机的 MAC 地址。ARP 就是用来完成这一任务的协议。ARP 请求是通过广播方式进行的,所有接收到 ARP 请求的设备都会检查是否请求的是自己的 IP 地址,如果是,则响应自己的 MAC 地址。

Ping ip的流程

当你使用 Ping 向某个 IP 地址发送数据包时,首先需要知道该 IP 地址对应的 MAC 地址。这就是 ARP 的作用

一台计算机 A 要 Ping 另一台计算机 B,过程如下:

  • A 查询自己的 ARP 缓存表,看是否有 B 的 IP 地址对应的 MAC 地址记录。
  • 如果找不到,则 A 发送一个 ARP 请求到局域网内所有设备(广播),询问谁拥有 B 的 IP 地址。
  • B 接收到 ARP 请求后,如果匹配自己的 IP 地址,就回复 ARP 响应给 A,告诉它自己的 MAC 地址。
  • A 收到 ARP 响应后,记录下 B 的 MAC 地址,并更新 ARP 缓存表。
  • A 然后构造一个 ICMP Echo Request 数据包,并使用 B 的 MAC 地址发送给 B。
  • B 收到 ICMP Echo Request 后,回应一个 ICMP Echo Reply 给 A。
  • A 收到回声应答,表示连通性测试成功。

抓包如下

在这里插入图片描述

代码实现

下面是简单c语言示例,
使用原始套接字发送 ARP 请求并接收 ARP 响应来获取局域网内所有在线设备的 IP 地址

安装 npm install libpcap-dev

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <netinet/arp.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <ifaddrs.h>

#define BROADCAST_MAC "\xff\xff\xff\xff\xff\xff"

void send_arp_request(int sockfd, struct ether_header *eh, struct arphdr *arp, char *mac, char *ip, char *target_ip) {
    memset(arp, 0, sizeof(struct arphdr));
    arp->ar_hrd = htons(ARPHRD_ETHER);    /* Ethernet */
    arp->ar_pro = htons(ETH_P_IP);       /* IP Protocol */
    arp->ar_op  = htons(AROP_REQUEST);   /* ARP Request */
    memcpy(arp->ar_saddr, mac, ETH_ALEN); /* Sender hardware address */
    memcpy(arp->ar_sha, mac, ETH_ALEN);  /* Sender hardware address */
    memcpy(arp->ar_tpa, inet_aton(target_ip), 4); /* Target protocol address */
    memcpy(arp->ar_tha, "\x00\x00\x00\x00\x00\x00", 6); /* Target hardware address (all zeros) */
    memcpy(arp->ar_spa, inet_aton(ip), 4); /* Sender protocol address */

    eh->ether_dhost[0] = BROADCAST_MAC[0]; /* Destination MAC address (Broadcast) */
    eh->ether_dhost[1] = BROADCAST_MAC[1];
    eh->ether_dhost[2] = BROADCAST_MAC[2];
    eh->ether_dhost[3] = BROADCAST_MAC[3];
    eh->ether_dhost[4] = BROADCAST_MAC[4];
    eh->ether_dhost[5] = BROADCAST_MAC[5];
    eh->ether_type = htons(ETHER_TYPE_ARP); /* ARP Packet */

    sendto(sockfd, eh, sizeof(struct ether_header) + sizeof(struct arphdr), 0, NULL, 0);
}

int main(void) {
    struct ifaddrs *ifAddrStruct = NULL;
    struct ifaddrs *tmpAddrPtr = NULL;
    int sockfd;
    char *target_ip = "192.168.1.0"; /* 目标 IP 地址段 */
    int i = 0;
    int len = 0;

    /* 获取本地接口信息 */
    if (getifaddrs(&ifAddrStruct) == -1) {
        perror("getifaddrs");
        return -1;
    }

    /* 打开原始套接字 */
    if ((sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1) {
        perror("socket");
        freeifaddrs(ifAddrStruct);
        return -1;
    }

    tmpAddrPtr = ifAddrStruct;
    while (tmpAddrPtr != NULL) {
        if (tmpAddrPtr->ifa_addr != NULL && strcmp(tmpAddrPtr->ifa_name, "lo") != 0) {
            char *ip = inet_ntoa(*(struct in_addr *)tmpAddrPtr->ifa_addr);
            struct ether_header eh;
            struct arphdr arp;

            send_arp_request(sockfd, &eh, &arp, (char *)tmpAddrPtr->ifa_ifu.ifu_data, ip, target_ip);

            /* 循环发送 ARP 请求 */
            for (i = 1; i <= 254; i++) {
                char ipaddr[INET_ADDRSTRLEN];
                sprintf(ipaddr, "%s.%d", target_ip, i);
                send_arp_request(sockfd, &eh, &arp, (char *)tmpAddrPtr->ifa_ifu.ifu_data, ip, ipaddr);
            }
        }
        tmpAddrPtr = tmpAddrPtr->ifa_next;
    }

    /* 清理 */
    freeifaddrs(ifAddrStruct);
    close(sockfd);

    return 0;
}

mDNS

  • mDNS(Multicast DNS)协议:使用5353端口,组播地址 224.0.0.251。

在一个没有常规DNS服务器的小型网络内,可以使用mDNS来实现类似DNS的编程接口、包格式和操作语义。MDNS协议的报文与DNS的报文结构相同,但有些字段对于MDNS来说有新的含义。

在局域网中,设备和设备之前相互通信需要知道对方的ip地址的,大多数情况,设备的ip不是静态ip地址,而是通过dhcp 协议动态分配的ip 地址,mDNS如何设备发现呢,

  • UPnP(Universal Plug and Play)技术:UPnP技术旨在让智能设备能够自动发现网络上的其他UPnP设备,并与之进行通信和协作。它支持设备之间的动态服务发现、自动配置和事件通知等功能。
  • LLMNR(Link-Local Multicast Name Resolution)协议:作为DNS的一种补充,LLMNR协议在局域网内通过多播方式解析设备的名称和地址。它特别适用于IPv6网络环境,并能够在DNS服务不可用的情况下提供快速的名称解析服务。
  • SNMP(Simple Network Management Protocol)协议:虽然SNMP主要用于网络管理而非设备发现,但它可以通过轮询网络中的设备来收集其状态信息,从而间接实现设备发现的目的。通过SNMP,管理员可以获取设备的型号、序列号、固件版本等详细信息。
  • 自定义协议

对比测试

Avahi 介绍

Avahi 是一个开源项目,提供了一套用于实现 mDNS (Multicast DNS) 和 SSDP (Simple Service
Discovery Protocol) 的工具和库。它主要用于局域网内的零配置网络服务发现。Avahi 支持多种操作系统,包括 Linux、BSD 变体以及其他类 Unix 系统。
Avahi 库的主要用途

  • 服务发现: 让设备在本地网络中自动发现彼此提供的服务。
  • 名称解析: 自动解析设备和服务的名称到 IP 地址,而无需手动配置。
  • 广告服务: 让设备能够发布自己提供的服务,以便其他设备可以发现。
  • 多播 DNS: 利用 mDNS 进行服务发现和名称解析。
  • 简单服务发现协议: 通过 SSDP 进行服务发现

Avahi 安装

yum install avahi libavahi-client-devel

在这里插入图片描述

Avahi 使用

常用API
初始化

avahi_init();

创建 Avahi 客户端:

AvahiClient *client;
client = avahi_client_new(avahi_poll_get(), AVAHI_CLIENT_FLAG_USE_MULTICAST, NULL, NULL);

回调监听状态变化

static void client_callback(AvahiClient *client, AvahiClientState state, void *userdata) {
    // 处理状态变化
}
avahi_client_set_callback(client, client_callback, NULL);

测试代码

#include <avahi-common/poll.h>
#include <avahi-core/core.h>
#include <avahi-client/client.h>
#include <stdio.h>
#include <stdlib.h>

static void service_resolved(AvahiServiceResolvedEvent *event, void *userdata) {
    printf("Found service '%s' at '%s', port %d.\n",
           event->name, event->address, event->port);
}

static void client_callback(AvahiClient *client, AvahiClientState state, void *userdata) {
    AvahiEntryGroup *group;
    AvahiEntryGroupState group_state;
    AvahiServiceResolver *res;

    if (state != AVAHI_CLIENT_S_RUNNING)
        return;

    /* Create a new entry group */
    if ((group = avahi_client_new_entry_group(client)) == NULL) {
        fprintf(stderr, "No memory, aborting.\n");
        return;
    }

    /* Setup callback for the entry group */
    avahi_entry_group_set_callback(group, entry_group_state_callback, userdata);

    /* Add a service browser to the entry group */
    if (avahi_entry_group_add_service_browser(group,
                                             AVAHI_IF_UNSPECIFIED,
                                             AVAHI_PROTO_INET,
                                             "_service._tcp", NULL, NULL) < 0) {
        fprintf(stderr, "Could not add service browser to entry group.\n");
        avahi_entry_group_free(group);
        return;
    }

    /* Commit the entry group */
    if (avahi_entry_group_commit(group) < 0) {
        fprintf(stderr, "Could not commit entry group.\n");
        avahi_entry_group_free(group);
        return;
    }

    /* Wait until the entry group is ready */
    do {
        avahi_entry_group_get_state(group, &group_state);
    } while (group_state != AVAHI_ENTRY_GROUP_COMMITTED);

    /* Now we can resolve services */
    if ((res = avahi_client_alloc_service_resolver(client)) == NULL) {
        fprintf(stderr, "No memory, aborting.\n");
        return;
    }

    avahi_service_resolver_set_callback(res, service_resolved, NULL);

    /* Resolve the first found service */
    avahi_service_resolver_resolve(res,
                                  AVAHI_IF_UNSPECIFIED,
                                  AVAHI_PROTO_INET,
                                  "my-service", "local", NULL);
}

int main(int argc, char *argv[]) {
    AvahiClient *client;
    AvahiClientState initial_state;

    /* Initialize Avahi library */
    avahi_init();

    /* Create a new client object */
    if ((client = avahi_client_new(avahi_poll_get(), AVAHI_CLIENT_FLAG_USE_MULTICAST, client_callback, NULL)) == NULL) {
        fprintf(stderr, "Failed to create client.\n");
        return 1;
    }

    /* Get the initial client state */
    avahi_client_get_state(client, &initial_state);

    /* Main loop */
    while (initial_state != AVAHI_CLIENT_S_RUNNING) {
        avahi_client_wait(client, 1000);
        avahi_client_get_state(client, &initial_state);
    }

    /* Clean up */
    avahi_client_free(client);

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2121889.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自动化测试之Selenium的使用

06、Selenium的使用 Selenium 是一个自动化测试工具&#xff0c;利用它可以驱动浏览器执行特定的动作&#xff0c;如点击、下拉等操作&#xff0c;同时还可以获取浏览器当前呈现的页面的源代码&#xff0c;做到可见即可爬。对于一些 JavaScript 动态渲染的页面来说&#xff0c…

CleanMyMac X2024破解版mac电脑清理工具

今天&#xff0c;我要跟大家分享一个让我彻底告别电脑卡顿的秘密武器——CleanMyMac X。这不仅仅是一款普通的清理工具&#xff0c;它是你电脑的私人健身教练&#xff0c;让电脑焕发青春活力&#xff01; CleanMyMac直装官方版下载地址&#xff1a; http://wm.makeding.com/i…

公共英语三级考试时间安排

公共英语三级考试时间安排

STM32学习笔记(二、初识stm32单片机)

一、stm32的含义是什么&#xff1f; 首先stm32是意法半导体公司&#xff08;ST&#xff09;使用ARM公司的Cortex-M为核心生产的32位的单片机。 其中&#xff0c;ST---意法半导体公司&#xff0c;即SOC厂商。 M---为Microelectronics的缩写&#xff0c;即微型处理器。 32---表示…

erp系统有哪些品牌?盘点2024年值得关注的十个爆款erp品牌!

本文将盘点主流的erp品牌&#xff0c;为企业选型提供参考&#xff01; ERP系统是企业数字化转型的基石&#xff0c;选择一款适合企业自身需求的ERP系统&#xff0c;不仅能够显著提升企业的运营效率&#xff0c;还能为企业的长期发展奠定坚实的基础。 随着市场竞争的日益激烈&a…

比较:#define,const,typedef

目录 1. #define 2. const 3. typedef #define 用于文本替换&#xff0c;没有类型检查&#xff0c;适用于定义简单的常量或宏。const 是类型安全的编译时常量&#xff0c;适合定义不可变的变量&#xff0c;有范围控制。typedef 用于定义类型的别名&#xff0c;简化代码复杂性…

ubuntu20.04 Qt6引用dcmtk库实现dicom文件读取和字符集转换

1 环境问题 安装完Qt6&#xff0c;新建Qt/QtQuick CMake工程编译出现如下错误: Found package configuration file: Qt6Config.cmake but it set Qt6 FOUND to FALSE so package "Qt6" is considered to be NOT FOUND. 原因&#xff1a; 这是因为系统中缺少OpenG…

IOS Siri和快捷指令打开app

使用场景 需要Siri打开应用或者在自定义快捷指令打开应用&#xff0c;并携带内容进入应用。 1.创建Intents文件 1.1 依次点开File->New->File 1.2 搜索intent关键字找到 SiriKit Intent Definition File文件 1.3 找到刚才创建的Intent文件&#xff0c;点击然后New Inte…

Vue2+JS项目升级为Vue3+TS之jquery的maphilight引入项目(附使用)

本人由于想提升自己的项目开发能力&#xff0c;所以将就项目的vue2JavaScriptwebpack的旧技术栈升级为vue3typescriptvite的技术栈&#xff0c;所以遇到很多坑&#xff0c;以下是maphilight的解决方法。 众所周知jquery是基于JavaScript进行开发&#xff0c;但是已有typescript…

钉钉虚拟位置打卡

我用蓝奏浏览器分享了[base_r_sign], 下载链接:https://wwp.lanzoup.com/i5NK526t7u9e 提取码 : 7wib, 你可以不限速下载哦\n\n通过百度网盘分享的文件&#xff1a;彤彤240724…\n链接:https://pan.baidu.com/s/1x_xhRQDopvQBAg-nWUNf4Q?pwd6666\n提取码:6666 下载好以后先配置…

超全!进销存系统排名前列的厂商有哪些?

本文将为大家盘点10款主流的进销存系统&#xff0c;为企业选型提供参考&#xff01; 进销存系统&#xff08;Inventory Management System&#xff09;&#xff0c;也称为物料管理系统或存货管理系统&#xff0c;是指企业为有效管理和控制进出货物的流动&#xff0c;准确记录库…

css——网格布局

名词解释 div{$}*9tab键&#xff0c;快捷生成 记首字母gtc 网格布局&#xff1a;display: grid; grid-template-columns: 100px 100px 100px; grid-template-rows: 100px 100px 100px; &#xff08;父元素&#xff09; <!DOCTYPE html> <html lang&q…

虚幻引擎Gameplay探索 Actor 之间的高效通信与交互技巧二

Actor通信介绍 在虚幻引擎中&#xff0c;Actor 是游戏世界中的基本构建块&#xff0c;类似于 Unity 中的 GameObject。Actor 通信是指不同 Actor 之间如何相互交互和传递信息&#xff0c;这在构建复杂的游戏逻辑时至关重要。以下是对 Actor 通信的详细介绍。 Actor通信方法表…

Redis复习笔记整理(没有人会有耐心看完包括我自己)

目录 1、Redis简介 1.1 补充数据类型Stream 1.2 Redis底层数据结构 1.3 Redis为什么快 1.4 持久化机制* 1.4.1 RDB持久化 bgsave执行流程 如何保证数据一致性 快照操作期间服务崩溃 RDB优缺点 1.4.2 AOF持久化 为什么采用写后日志 如何实现AOF 什么是AOF重写 AO…

Vue组件:动态组件、缓存组件、异步组件

1、动态组件 Vue.js 提供了对动态组件的支持。在使用动态组件时&#xff0c;多个组件使用同一挂载点&#xff0c;根据条件在不同组件之间进行动态切换。动态组件通过使用 Vue.js 中的 <component>元素&#xff0c;动态绑定到该元素的 is 属性&#xff0c;根据 is 属性的…

通过LDAP方式使用windows域认证

关于Windows的域认证, 网上大多都再介绍原理啥的, 但是对于从来没有做过.net的我来说, 和看天书一样. 我把我做的demo提供出来共大家参考. 需要参考的文章,参照如下 Windows下LDAP服务安装与使用_windows ldap-CSDN博客 OpenLDAP管理工具之LDAP Admin-腾讯云开发者社区-腾讯云…

计算机基础知识-3

机器周期的时钟周期&#xff0c;或者是cpu时钟周期&#xff0c;就是系统的主频&#xff0c;&#xff0c;根据主频的频率产生脉冲信号。一条指令的执行分为取指和执行&#xff0c;不同指令的取指和执行锁需要的时间也可能是不同的。 每个指令的机器周期可能不同&#xff0c;每个…

Centos镜像详细下载思路总结:包括阿里云镜像下载和官方地址下载--centos7和centos8 镜像下载

Centos镜像详细下载思路总结&#xff1a;包括阿里云镜像下载和官方地址下载。 系统镜像下载&#xff1a; 阿里云镜像&#xff1a; centos-vault安装包下载_开源镜像站-阿里云 官方网址&#xff1a; https://vault.centos.org/7.6.1810/isos/ 系统相关依赖包下载&#xff1a…

某里228滑块逆向分析

声明: 该文章为学习使用,严禁用于商业用途和非法用途,违者后果自负,由此产生的一切后果均与作者无关。 本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请联系作者立即删除! 前言 这次会简单的讲解…

【Web】XGCTF 西瓜杯 超详细题解

目录 CodeInject tpdoor easy_polluted Ezzz_php CodeInject eval里打代码注入 11);system("tac /0*");// tpdoor 可以传参isCache给../../config/route.php写入$config[request_cache_key] 打的是CheckRequestCache中间件解析的漏洞 think\middleware\Ch…