请教高手:怎样用Delphi根据已知IP地址查询局域网中对应网卡的MAC地址

解决方案 »

  1.   

    (即在局域网内,知道IP查MAC地址,知道MAC地址查IP)
    你可以使用arp程序(WINDOWS自带)
    eg: arp -a 192.168.0.1如果使用程序查的话,你需要去实现一个ARP、RARP协议的软件
    如果只是查看一下对应关系,可以登陆到以太网交换机上查看IP和MAC之间的对应
    在Windows9x中,可按如下思路进行:
    1、建立局域网内IP地址--MAC地址对照表:
      调用icmp.dll中的IcmpSendEcho函数对一个地址发一个ping操作。
      成功后马上调用Iphlpapi.dll中的GetIpNetTable函数来取所有的ARP表,从中找出
    所关心的MAC地址。
      保存IP地址和MAC地址。
      依此类推,取得局域网内所有IP地址和MAC地址的对照表。
      (可取范围为IPADDR and SUBMASK 子网范围内的对照表)
    2、做一个查询界面。Windows2000中,应该可以更简单一些。因为它支持更多的函数如SendARP。
    IP to MAC 较容易:
    方法1. DOS命令: nbtstat -a IP
    方法2. DOS命令: ping IP 然后 arp -a
    方法3. 仿照nbtstat,向对方的137端口发数据包,包的内容如下(C代码)
    BYTE b[50]={0x0,0x00,0x0,0x10,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,
                 0x20,0x43,0x4b,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
                 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
                 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
                 0x41,0x41,0x41,0x0,0x0,0x21,0x0,0x1};
    (这是发nbtstat命令后用IPMan截获的),对方会向你返回一个数据包,
    其中有对方的工作组,主机名,用户名,MAC地址。当然,返回包的格式要
    自己分析,也可以查RFC文档。你自己的端口可以随便取一个值。
    方法4.在win9x下可用IPMan直接发ARP。
    方法5.在win2000下可用SendArp()直接发ARP。
    ----------------------------------------------------------------
    MAC to IP似乎较困难。
    Win9x下IPMan有此项功能,但我试了一下似乎不行。
    来自:zw84611, 时间:2001-10-7 19:37:00, ID:659172
    发出去了,是用VC写的。因为我不喜欢Delphi5 的udp控件。其实用delphi也一样。很
    简单的,就是向对方137端口发内容为
    BYTE b[50]={0x0,0x00,0x0,0x10,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,
                 0x20,0x43,0x4b,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
                 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
                 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
                 0x41,0x41,0x41,0x0,0x0,0x21,0x0,0x1};
    的UDP包(Delphi中用$代替0x),对方就会给你应答,你接收就是了。Delphi源码如下,其中UDP用Socket API实现。
    -------------------------------------------------------unit udp;interfaceuses
      Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, winsock,
      StdCtrls;const
      WM_SOCK = WM_USER + 1;     //自定义windows消息
      UDPPORT = 6767;            //设定UDP端口号
      NBTPORT = 137;type
      Tfrmmain = class(TForm)
        Button1: TButton;
        ListBox1: TListBox;
        Edit1: TEdit;
        procedure FormCreate(Sender: TObject);
        procedure FormClose(Sender: TObject; var Action: TCloseAction);
        procedure Button1Click(Sender: TObject);
      private
        { Private declarations }
        s: TSocket;
        addr: TSockAddr;
        FSockAddrIn : TSockAddrIn; //利用消息实时获知UDP消息
        procedure ReadData(var Message: TMessage); message WM_SOCK;
      public
        { Public declarations }
        procedure SendData(b:array of byte);
      end;var
      frmmain: Tfrmmain;implementation{$R *.DFM}procedure Tfrmmain.FormCreate(Sender: TObject);
    var
       TempWSAData: TWSAData;
       //optval: integer;
    begin
         // 初始化SOCKET
         if WSAStartup($101, TempWSAData)=1 then
            showmessage('StartUp Error!');     s := Socket(AF_INET, SOCK_DGRAM, 0);
         if (s = INVALID_SOCKET) then   //Socket创建失败
         begin
              showmessage(inttostr(WSAGetLastError())+'  Socket创建失败');
              CloseSocket(s);
         end;
         //本机SockAddr绑定
         addr.sin_family := AF_INET;
         addr.sin_addr.S_addr := INADDR_ANY;
         addr.sin_port := htons(UDPPORT);
         if Bind(s, addr, sizeof(addr)) <> 0  then
           begin
             showmessage('bind fail');
           end;
         WSAAsyncSelect(s, frmmain.Handle , WM_SOCK, FD_READ);
         //对方SockAddrIn设定
         FSockAddrIn.SIn_Family := AF_INET;
         FSockAddrIn.SIn_Port := htons(NBTPORT);
    end;procedure Tfrmmain.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
        CloseSocket(s);
    end;procedure GetInfo(buf: Array of byte);
    {这个过程是根据返回报文的内容凑出来的,偶尔会出错。如果能根据
    报文格式的定义来做(可查阅相关RFC文档,可惜我没时间,以后做吧),就更好了。
    如果你作出来了,不妨寄给我一个哟。e-mail:[email protected]}
    var bAdd,bMac:boolean;
        str,strHost,strHex,strMac:string;
        tem,i,j:integer;
    begin  bAdd:=true;
      strHost:='';
      strHex:='';
      tem:=0;
      for i:=57 to 500 do //57-72
      begin
        if(Buf[i]=$cc)then break;
        if(Buf[i]=$20)then bAdd:=false;
        if(bAdd)then
        begin
          str:=char(Buf[i]);
          strHost:=strHost+str;      str:=Format('%x.',[Buf[i]]);
          strHex:=strHex+str;
        end;    inc(tem);
        if(tem mod 18=0)then
        begin
          bAdd:=true;
          Trim(strHex);
          bMac:=true;
          for j:=1 to length(strHex) do if strHex[j]>'0' then bMac:=false;
          //showmessage(strHex);
          if(bMac)then
          with frmmain.ListBox1 do
          begin
            Items.Delete(items.count-1);
            Items.add('mac address:');
            Delete(strMac,17,length(strMac)-17);
            Items.Add(strMac);
            break;
          end;
          frmmain.ListBox1.items.Add(strHost);
          strMac:=strHex;
          strHost:='';
          strHex:='';
        end;
      end;end;procedure Tfrmmain.ReadData(var Message: TMessage);
    var
       buffer: Array [1..500] of byte;
       len{,i}: integer;
       flen: integer;
       Event: word;
       value: string;begin
         value:='';
         Event := WSAGetSelectEvent(Message.LParam);
         if Event = FD_READ then
         begin
              len := recvfrom(s, buffer, sizeof(buffer), 0, FSockAddrIn, flen);
              {for i:=1 to len do value:=value+format('%x',[buffer[i]]);
              ListBox1.items.add(value);
              value:='';
              for i:=1 to len do if char(buffer[i])<>#0 then value:=value+char(buffer[i]);
              ListBox1.items.add(value);}
              if len<> 0 then GetInfo(buffer);
         end;
    end;procedure Tfrmmain.SendData(b:array of byte);
    var
       len: integer;
    begin     FSockAddrIn.SIn_Addr.S_addr := inet_addr(pchar(edit1.text));
         len := sendto(s, b[0],50, 0, FSockAddrIn, sizeof(FSockAddrIn));
         //if (WSAGetLastError() <> WSAEWOULDBLOCK) and (WSAGetLastError() <> 0) then showmessage(inttostr(WSAGetLastError()));
         if len = SOCKET_ERROR then
            showmessage('send fail');
         if len <> 50 then
            showmessage('Not Send all');
    end;procedure Tfrmmain.Button1Click(Sender: TObject);
    const NbtstatPacket:array[0..49]of byte
          =($0,$0,$0,$10,$0,$1,
          $0,$0,$0,$0,$0,$0,$20,$43,$4b,
          $41,$41,$41,$41,$41,$41,$41,$41,
          $41,$41,$41,$41,$41,$41,$41,$41,
          $41,$41,$41,$41,$41,$41,$41,$41,
          $41,$41,$41,$41,$41,$41,$0,$0,$21,$0,$1);
    begin  senddata(NbtstatPacket);
      
    end;end.
      

  2.   

    在iphlpapi.dll里面有一个函数:GetAdaptersInfo() 
    好像是干这个用的。说明如下: 
    GetAdaptersInfo 
    The GetAdaptersInfo function retrieves adapter information for the local computer. DWORD GetAdaptersInfo( 
      PIP_ADAPTER_INFO pAdapterInfo,    // buffer to receive data 
      PULONG pOutBufLen                 // size of data returned 
    ); 
    Parameters 
    pAdapterInfo  
    [out] Pointer to a buffer that, , receives a linked list of IP_ADAPTER_INFO structures.  
    pOutBufLen  
    [in] Pointer to a ULONG variable that specifies the size of the buffer pointed to by the pAdapterInfo parameter. If this size is insufficient to hold the adapter information, GetAdaptersInfo fills in this variable with the required size, and returns an error code of ERROR_BUFFER_OVERFLOW.  
    Return Values 
    If the function succeeds, the return value is ERROR_SUCCESS. If the function fails, the return value is one of the following error codes. Value Meaning  
    ERROR_BUFFER_OVERFLOW The buffer size indicated by the pOutBufLen parameter is too small to hold the adapter information. The pOutBufLen parameter points to the required size.  
    ERROR_INVALID_PARAMETER The pOutBufLen parameter is NULL, or the calling process does not have read/write access to the memory pointed to by pOutBufLen, or the calling process does not have write access to the memory pointed to by the pAdapterInfo parameter.  
    ERROR_NO_DATA No adapter information exists for the local computer.  
    ERROR_NOT_SUPPORTED GetAdaptersInfo is not supported by the operating system running on the local computer.  
    Other If the function fails, use FormatMessage to obtain the message string for the returned error.  
    Requirements  
      Windows NT/2000: Requires Windows 2000. 
      Windows 95/98: Requires Windows 98. 
      Header: Declared in Iphlpapi.h.//没有 
      Library: Use Iphlpapi.lib.//没有 IP_ADAPTER_INFO 
    The IP_ADAPTER_INFO structure contains information about a particular network adapter on the local computer. typedef struct _IP_ADAPTER_INFO { 
      struct _IP_ADAPTER_INFO* Next; 
      DWORD ComboIndex; 
      char AdapterName[MAX_ADAPTER_NAME_LENGTH + 4]; 
      char Description[MAX_ADAPTER_DESCRIPTION_LENGTH + 4]; 
      UINT AddressLength; 
      BYTE Address[MAX_ADAPTER_ADDRESS_LENGTH]; 
      DWORD Index; 
      UINT Type; 
      UINT DhcpEnabled; 
      PIP_ADDR_STRING CurrentIpAddress; 
      IP_ADDR_STRING IpAddressList; 
      IP_ADDR_STRING GatewayList; 
      IP_ADDR_STRING DhcpServer; 
      BOOL HaveWins; 
      IP_ADDR_STRING PrimaryWinsServer; 
      IP_ADDR_STRING SecondaryWinsServer; 
      time_t LeaseObtained; 
      time_t LeaseExpires;  
    } IP_ADAPTER_INFO, *PIP_ADAPTER_INFO; 
    Members 
    Next  
    Pointer to the next adapter in the linked list of adapters.  
    ComboIndex  
    This member is unused.  
    AdapterName[MAX_ADAPTER_NAME_LENGTH + 4]  
    Specifies the name of the adapter.  
    Description[MAX_ADAPTER_DESCRIPTION_LENGTH + 4]  
    Specifies a description for the adapter.  
    AddressLength  
    Specifies the length of the hardware address for the adapter.  
    Address[MAX_ADAPTER_ADDRESS_LENGTH]  
    Specifies the hardware address for the adapter. //这个是不是你想要的? 
    Index  
    Specifies the adapter index.  
    Type  
    Specifies the adapter type.  
    DhcpEnabled  
    Specifies whether dynamic host configuration protocol (DHCP) is enabled for this adapter.  
    CurrentIpAddress  
    Specifies the current IP address for this adapter.  
    IpAddressList  
    Specifies the list of IP addresses associated with this adapter.  
    GatewayList  
    Specifies the IP address of the default gateway for this adapter.  
    DhcpServer  
    Specifies the IP address of the DHCP server for this adapter.  
    HaveWins  
    Specifies whether this adapter uses Windows Internet Name Service (WINS).  
    PrimaryWinsServer  
    Specifies the IP address of the primary WINS server.  
    SecondaryWinsServer  
    Specifies the IP address of the secondary WINS server.  
    LeaseObtained  
    Specifies the time when the current DHCP lease was obtained.  
    LeaseExpires  
    Specifies the time when the current DHCP lease will expire.  
    Requirements  
      Windows NT/2000: Requires Windows 2000. 
      Windows 95/98: Requires Windows 98. 
      Header: Declared in Iptypes.h. 
      

  3.   

    我们可以通过好几种方法来调用SNMP代理,如用C++来调用WINT下SNMP自带的SNMP API FOR WIN32的动态链接 库,也可以利用SNMP PERL来编写CGI程序完成这种功能,也可以用SNMP Java程序来编写。SNMP Java程序类似 于SNMP PERL程序,它是Java程序类扩展库,它提供了Java语言与SNMP代理接口的类库,使程序员通过调用这个 类库很容易地操纵SNMP代理,获取网络路由表中储存的网络设备的各种信息;包括IP地址与MAC地址映射表。下 面我介绍一下SNMP Java获取MAC信息的过程。首先在程序开头加SNMP Jva的扩展类,import Snmp. *; 
    …… 
    OIDStr[0]=new String 
    (“.1.3.6.1.2.1.3.1.1.2.0.0.1.47.”+args[1]);定义IP地址与CAC地址的映射的位置 
    …… 
    api=new SnmpAPI(); //产生一个SNMP代理的接口api. Start(); 
    snmpPDU pdu=new SnmpPDU(api); 
    pdu. Command=apiGET_REQ_MSG; 
    SnmpSEssion session=new SnmpSession(api); 
    Session. version=SnmpAPI. SNMP VERSION_1; 
    Session. Peername=Host[I];//把路由器地址赋给线程SnmpOID odi=new SnmpOID(OIDStr[I], api); //产生 一个对象标识符 
    If(oid. ToValue()!=null)pdu. AddNull(oid); 
    Try{Session. Open(); 
    Pdu=session. SyncSend(pdu);//发出请求单元信息}catch (SnmpException e){} 
    If (pdu= =null){//timeout 
    System. Out. Println(“Request timed out to:”+Host); 
    System. Exit(1):} 
    SnmpVarBind Varbind=(SnmpVarBind) 
    Pdu. Variables. FirstElement();//找到第一满足条件的信息 
    PduStr=varbind. ToString();//存放找到的所需的信息 
      

  4.   

    如果使用程序查的话,你需要去实现一个ARP、RARP协议的软件
    如果只是查看一下对应关系,可以登陆到以太网交换机上查看IP和MAC之间的对应
    在Windows9x中,可按如下思路进行:
    1、建立局域网内IP地址--MAC地址对照表:
      调用icmp.dll中的IcmpSendEcho函数对一个地址发一个ping操作。
      成功后马上调用Iphlpapi.dll中的GetIpNetTable函数来取所有的ARP表,从中找出
    所关心的MAC地址。
      保存IP地址和MAC地址。
      依此类推,取得局域网内所有IP地址和MAC地址的对照表。
      (可取范围为IPADDR and SUBMASK 子网范围内的对照表)
    2、做一个查询界面。Windows2000中,应该可以更简单一些。因为它支持更多的函数如SendARP。
    IP to MAC 较容易:
    方法1. DOS命令: nbtstat -a IP
    方法2. DOS命令: ping IP 然后 arp -a
    方法3. 仿照nbtstat,向对方的137端口发数据包,包的内容如下(C代码)
     BYTE b[50]={0x0,0x00,0x0,0x10,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,
                 0x20,0x43,0x4b,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
                 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
                 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
                 0x41,0x41,0x41,0x0,0x0,0x21,0x0,0x1};
     (这是发nbtstat命令后用IPMan截获的),对方会向你返回一个数据包,
     其中有对方的工作组,主机名,用户名,MAC地址。当然,返回包的格式要
     自己分析,也可以查RFC文档。你自己的端口可以随便取一个值。
    方法4.在win9x下可用IPMan直接发ARP。
    方法5.在win2000下可用SendArp()直接发ARP。
    ----------------------------------------------------------------
    MAC to IP似乎较困难。
    Win9x下IPMan有此项功能,但我试了一下似乎不行。
    来自:zw84611, 时间:2001-10-7 19:37:00, ID:659172 
    发出去了,是用VC写的。因为我不喜欢Delphi5 的udp控件。其实用delphi也一样。很
    简单的,就是向对方137端口发内容为
    BYTE b[50]={0x0,0x00,0x0,0x10,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,
                 0x20,0x43,0x4b,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
                 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
                 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
                 0x41,0x41,0x41,0x0,0x0,0x21,0x0,0x1};
    的UDP包(Delphi中用$代替0x),对方就会给你应答,你接收就是了。
     
    Delphi源码如下,其中UDP用Socket API实现。
    -------------------------------------------------------unit udp;interfaceuses
      Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, winsock,
      StdCtrls;const
      WM_SOCK = WM_USER + 1;     //自定义windows消息
      UDPPORT = 6767;            //设定UDP端口号
      NBTPORT = 137;type
      Tfrmmain = class(TForm)
        Button1: TButton;
        ListBox1: TListBox;
        Edit1: TEdit;
        procedure FormCreate(Sender: TObject);
        procedure FormClose(Sender: TObject; var Action: TCloseAction);
        procedure Button1Click(Sender: TObject);
      private
        { Private declarations }
        s: TSocket;
        addr: TSockAddr;
        FSockAddrIn : TSockAddrIn; //利用消息实时获知UDP消息
        procedure ReadData(var Message: TMessage); message WM_SOCK;
      public
        { Public declarations }
        procedure SendData(b:array of byte);
      end;var
      frmmain: Tfrmmain;implementation{$R *.DFM}procedure Tfrmmain.FormCreate(Sender: TObject);
    var
       TempWSAData: TWSAData;
       //optval: integer;
    begin
         // 初始化SOCKET
         if WSAStartup($101, TempWSAData)=1 then
            showmessage('StartUp Error!');     s := Socket(AF_INET, SOCK_DGRAM, 0);
         if (s = INVALID_SOCKET) then   //Socket创建失败
         begin
              showmessage(inttostr(WSAGetLastError())+'  Socket创建失败');
              CloseSocket(s);
         end;
         //本机SockAddr绑定
         addr.sin_family := AF_INET;
         addr.sin_addr.S_addr := INADDR_ANY;
         addr.sin_port := htons(UDPPORT);
         if Bind(s, addr, sizeof(addr)) <> 0  then
           begin
             showmessage('bind fail');
           end;
         WSAAsyncSelect(s, frmmain.Handle , WM_SOCK, FD_READ);
         //对方SockAddrIn设定
         FSockAddrIn.SIn_Family := AF_INET;
         FSockAddrIn.SIn_Port := htons(NBTPORT);
    end;procedure Tfrmmain.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
        CloseSocket(s);
    end;procedure GetInfo(buf: Array of byte);
    {这个过程是根据返回报文的内容凑出来的,偶尔会出错。如果能根据
    报文格式的定义来做(可查阅相关RFC文档,可惜我没时间,以后做吧),就更好了。
    如果你作出来了,不妨寄给我一个哟。e-mail:[email protected]}
    var bAdd,bMac:boolean;
        str,strHost,strHex,strMac:string;
        tem,i,j:integer;
    begin  bAdd:=true;
      strHost:='';
      strHex:='';
      tem:=0;
      for i:=57 to 500 do //57-72
      begin
        if(Buf[i]=$cc)then break;
        if(Buf[i]=$20)then bAdd:=false;
        if(bAdd)then
        begin
          str:=char(Buf[i]);
          strHost:=strHost+str;      str:=Format('%x.',[Buf[i]]);
          strHex:=strHex+str;
        end;    inc(tem);
        if(tem mod 18=0)then
        begin
          bAdd:=true;
          Trim(strHex);
          bMac:=true;
          for j:=1 to length(strHex) do if strHex[j]>'0' then bMac:=false;
          //showmessage(strHex);
          if(bMac)then
          with frmmain.ListBox1 do
          begin
            Items.Delete(items.count-1);
            Items.add('mac address:');
            Delete(strMac,17,length(strMac)-17);
            Items.Add(strMac);
            break;
          end;
          frmmain.ListBox1.items.Add(strHost);
          strMac:=strHex;
          strHost:='';
          strHex:='';
        end;
      end;end;procedure Tfrmmain.ReadData(var Message: TMessage);
    var
       buffer: Array [1..500] of byte;
       len{,i}: integer;
       flen: integer;
       Event: word;
       value: string;begin
         value:='';
         Event := WSAGetSelectEvent(Message.LParam);
         if Event = FD_READ then
         begin
              len := recvfrom(s, buffer, sizeof(buffer), 0, FSockAddrIn, flen);
              {for i:=1 to len do value:=value+format('%x',[buffer[i]]);
              ListBox1.items.add(value);
              value:='';
              for i:=1 to len do if char(buffer[i])<>#0 then value:=value+char(buffer[i]);
              ListBox1.items.add(value);}
              if len<> 0 then GetInfo(buffer);
         end;
    end;procedure Tfrmmain.SendData(b:array of byte);
    var
       len: integer;
    begin     FSockAddrIn.SIn_Addr.S_addr := inet_addr(pchar(edit1.text));
         len := sendto(s, b[0],50, 0, FSockAddrIn, sizeof(FSockAddrIn));
         //if (WSAGetLastError() <> WSAEWOULDBLOCK) and (WSAGetLastError() <> 0) then showmessage(inttostr(WSAGetLastError()));
         if len = SOCKET_ERROR then
            showmessage('send fail');
         if len <> 50 then
            showmessage('Not Send all');
    end;procedure Tfrmmain.Button1Click(Sender: TObject);
    const NbtstatPacket:array[0..49]of byte
          =($0,$0,$0,$10,$0,$1,
          $0,$0,$0,$0,$0,$0,$20,$43,$4b,
          $41,$41,$41,$41,$41,$41,$41,$41,
          $41,$41,$41,$41,$41,$41,$41,$41,
          $41,$41,$41,$41,$41,$41,$41,$41,
          $41,$41,$41,$41,$41,$41,$0,$0,$21,$0,$1);
    begin  senddata(NbtstatPacket);
      
    end;end.