You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何利用Delphi通过MAC地址获取网络设备的IP地址?

用Delphi从MAC地址获取对应IP地址的实现方案

核心逻辑

通过MAC查IP,本质是发送标准ARP请求数据包(目标MAC为待查询设备的MAC,以广播形式发送),再监听网络中的ARP响应,从中提取对应IP。注意:ARP属于链路层协议,不能通过UDP/TCP套接字发送,必须用原始套接字直接构造链路层数据包。

Delphi实现步骤

1. 构造标准ARP请求结构

标准ARP请求的数据包分为以太网帧头和ARP数据段两部分,完全匹配路由器发送的合法ARP包格式:

  • 以太网帧头:
    • 目标MAC:广播地址 FF:FF:FF:FF:FF:FF
    • 源MAC:本地网卡的MAC地址
    • 类型:0x0806(标识ARP协议)
  • ARP数据段:
    • 硬件类型:0x0001(以太网)
    • 协议类型:0x0800(IPv4)
    • 硬件地址长度:6
    • 协议地址长度:4
    • 操作码:0x0001(ARP请求)
    • 发送端MAC:本地网卡MAC
    • 发送端IP:本地网卡IP
    • 目标端MAC:待查询的设备MAC
    • 目标端IP:0.0.0.0(反向查询时IP未知)

2. 用原始套接字发送请求并监听响应

调用Windows API创建原始套接字,设置权限后发送构造好的ARP包,再监听网络接口上的ARP响应,筛选出目标设备返回的数据包,提取对应IP。

核心代码示例

需引入WinSock2和Windows单元:

type
  TEthernetHeader = packed record
    DestMAC: array[0..5] of Byte;
    SrcMAC: array[0..5] of Byte;
    EtherType: Word;
  end;

  TARPHeader = packed record
    HardwareType: Word;
    ProtocolType: Word;
    HardwareAddrLen: Byte;
    ProtocolAddrLen: Byte;
    Operation: Word;
    SenderMAC: array[0..5] of Byte;
    SenderIP: array[0..3] of Byte;
    TargetMAC: array[0..5] of Byte;
    TargetIP: array[0..3] of Byte;
  end;

  TARPPacket = packed record
    Ethernet: TEthernetHeader;
    ARP: TARPHeader;
  end;

// 构造ARP请求数据包
function BuildARPRequest(const LocalMAC, TargetMAC: array of Byte; LocalIP: DWORD): TARPPacket;
begin
  // 填充以太网帧头
  FillChar(Ethernet.DestMAC, 6, $FF); // 广播MAC
  Move(LocalMAC[0], Ethernet.SrcMAC, 6);
  Ethernet.EtherType := htons($0806); // ARP协议标识

  // 填充ARP数据段
  ARP.HardwareType := htons($0001);
  ARP.ProtocolType := htons($0800);
  ARP.HardwareAddrLen := 6;
  ARP.ProtocolAddrLen := 4;
  ARP.Operation := htons($0001); // ARP请求操作码
  Move(LocalMAC[0], ARP.SenderMAC, 6);
  ARP.SenderIP[0] := (LocalIP shr 24) and $FF;
  ARP.SenderIP[1] := (LocalIP shr 16) and $FF;
  ARP.SenderIP[2] := (LocalIP shr 8) and $FF;
  ARP.SenderIP[3] := LocalIP and $FF;
  Move(TargetMAC[0], ARP.TargetMAC, 6);
  FillChar(ARP.TargetIP, 4, 0); // 目标IP未知,填充0
end;

// 发送ARP请求并获取目标IP
function GetIPFromMAC(const TargetMAC: array of Byte; const LocalIP: DWORD; LocalMAC: array of Byte): string;
var
  Sock: TSocket;
  WSAData: TWSAData;
  Addr: TSockAddr;
  Packet: TARPPacket;
  RecvBuf: array[0..1023] of Byte;
  RecvLen: Integer;
  ResponseARP: ^TARPHeader;
begin
  Result := '';
  if WSAStartup(MAKEWORD(2, 2), WSAData) <> 0 then Exit;
  try
    // 创建原始套接字
    Sock := WSASocket(AF_INET, SOCK_RAW, IPPROTO_RAW, nil, 0, WSA_FLAG_OVERLAPPED);
    if Sock = INVALID_SOCKET then Exit;
    try
      // 允许发送广播包
      var BroadcastOpt: BOOL := True;
      setsockopt(Sock, SOL_SOCKET, SO_BROADCAST, @BroadcastOpt, SizeOf(BroadcastOpt));
      // 禁用系统自动添加IP头
      var HDRIncl: BOOL := True;
      setsockopt(Sock, IPPROTO_IP, IP_HDRINCL, @HDRIncl, SizeOf(HDRIncl));

      // 构造ARP请求包
      Packet := BuildARPRequest(LocalMAC, TargetMAC, LocalIP);

      // 设置广播发送地址
      FillChar(Addr, SizeOf(Addr), 0);
      Addr.sin_family := AF_INET;
      Addr.sin_addr.S_addr := INADDR_BROADCAST;
      Addr.sin_port := 0;

      // 发送ARP请求
      sendto(Sock, @Packet, SizeOf(Packet), 0, @Addr, SizeOf(Addr));

      // 设置接收超时(5秒)
      var Timeout: Integer := 5000;
      setsockopt(Sock, SOL_SOCKET, SO_RCVTIMEO, @Timeout, SizeOf(Timeout));

      // 监听ARP响应
      while True do
      begin
        RecvLen := recvfrom(Sock, @RecvBuf, SizeOf(RecvBuf), 0, nil, nil);
        if RecvLen <= 0 then Break;

        // 判断是否为ARP数据包
        var EtherHeader: ^TEthernetHeader := @RecvBuf;
        if ntohs(EtherHeader.EtherType) = $0806 then
        begin
          ResponseARP := @RecvBuf[SizeOf(TEthernetHeader)];
          // 筛选目标设备的ARP响应
          if (ntohs(ResponseARP.Operation) = $0002) and // ARP响应操作码
             (CompareMem(@ResponseARP.SenderMAC, @TargetMAC, 6)) then
          begin
            // 格式化IP地址
            Result := Format('%d.%d.%d.%d', [
              ResponseARP.SenderIP[0],
              ResponseARP.SenderIP[1],
              ResponseARP.SenderIP[2],
              ResponseARP.SenderIP[3]
            ]);
            Break;
          end;
        end;
      end;
    finally
      closesocket(Sock);
    end;
  finally
    WSACleanup;
  end;
end;

关键注意事项

  • 程序需以管理员权限运行,原始套接字需要系统权限。
  • 要正确获取本地网卡的MAC和IP,可通过GetAdaptersInfo API实现。
  • 你之前的错误在于用UDP发送ARP包:UDP是传输层协议,ARP属于链路层,必须直接构造链路层数据包发送。

内容的提问来源于stack exchange,提问作者bluscape

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 06:17:37