DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: Rogerdib
今日帖子: 1
在线用户: 2
导航: 论坛 -> DELPHI技术 斑竹:liumazi,sephil  
作者:
男 zspwy_007 (zsp) ▲▲▲△△ -
注册会员
2021/4/27 12:37:41
标题:
连接中的Socket如何获取对端的Mac地址? 浏览:321
加入我的收藏
楼主: 请问各位高手,如何通过一个已连接的Socket获取对端的Mac地址?通过iphlpapi.dll的函数SendARP用IP只能获取局域网中的主机,在internet中的服务器如何获取已连接客户端的Mac地址?
----------------------------------------------
-
作者:
男 dmzn (dmzn) ★☆☆☆☆ -
盒子活跃会员
2021/4/27 14:20:50
1楼: MAC地址工作在ISO的物理层,只能在同一IP子网工作.
----------------------------------------------
生活愉快.
作者:
男 xjia (xjia) ★☆☆☆☆ -
盒子活跃会员
2021/4/27 14:47:55
2楼: 除非对方主动上报,否则不可能获取到
----------------------------------------------
-
作者:
男 zspwy_007 (zsp) ▲▲▲△△ -
注册会员
2021/4/27 15:21:43
3楼: 我是看到一段C代码,貌似可以,但其中函数ioctl,delphi中的winsock.pas没有我就使用iocrtsocket替代,返回总是错误,c代码如下
int getPeerMacbySocketFd( int sockfd, char *buf, char* localethname )
{
    int ret =0;
    struct arpreq arpreq;
    struct sockaddr_in dstadd_in;
    socklen_t  len = sizeof( struct sockaddr_in );
    memset( &arpreq, 0, sizeof( struct arpreq ));
    memset( &dstadd_in, 0, sizeof( struct sockaddr_in ));
    if( getpeername( sockfd, (struct sockaddr*)&dstadd_in, &len ) < 0 )
    {
        perror( "get peer name wrong, %s/n", strerror(errno) );
        return -1;
    }
    else
    {
        memcpy( ( struct sockaddr_in * )&arpreq.arp_pa, ( struct sockaddr_in * )&dstadd_in, sizeof( struct sockaddr_in ));
        strcpy(arpreq.arp_dev, localethname);
        arpreq.arp_pa.sa_family = AF_INET;
        arpreq.arp_ha.sa_family = AF_UNSPEC;
        if( ioctl( sockfd, SIOCGARP, &arpreq ) < 0 )
        {
          perror( "ioctl SIOCGARP wrong, %s/n", strerror(errno) );
          return -1;
        }
        else
        {
          unsigned char* ptr = (unsigned char *)arpreq.arp_ha.sa_data;
          sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5));
        }
     }
     return ret;
}
我写的Delphi代码:
type
  TArpReq = packed record
    arp_pa      : TSockAddr; //协议地址
    arp_ha      : TSockAddr; //硬件地址
    arp_flags   : DWORD;     //标志位
    arp_netmask : TSockAddr; //网络掩码
    arp_dev     : array[0..15] of Char
  end;
  TMacAddr = array[0..5] of Byte;

const
  SIOCGARP  = (Ord('i') SHL 8) OR 31;//$8954  //get ARP table entry


function Ioctl(S: TSocket; cmd: DWORD; arg: PInteger): Integer; stdcall; external WINSOCK2 Name 'ioctlsocket';

function GetPeerMac(S: TSocket): TMacAddr;
var arp: TArpReq; addr: TSockAddr; len: Integer;
begin
  len:= SizeOf(addr);
  FillChar(addr, SizeOf(len), 0);
  if GetPeerName(S, addr, len) < 0 then Exit;

  FillChar(arp, SizeOf(arp), 0);
  arp.arp_pa  := addr;
  arp.arp_dev := 'eth1';  //'eth0'
  arp.arp_pa.sa_family:= AF_INET;
  arp.arp_ha.sa_family:= AF_UNSPEC;
  if Ioctl(S, SIOCGARP, @arp) < 0 then Exit;
  Move(arp.arp_ha.sa_data, Result, 6);
end;
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2021/4/27 20:52:39
4楼: 1. 首先是概念:MAC 地址,是局域网(以太网)通讯的地址。IP 在以太网的上一层,可以跨以太网通讯。因此,你要获得对端电脑的 MAC,你的电脑和对端电脑一定是在同一个局域网里面。

2. 看你的代码,使用了 ARP,既然使用 ARP,有个最简单的方法,就是使用命令行 CMD 输入 ARP 命令,就能获得你的电脑访问多的局域网里面的电脑的 IP 和 MAC 地址。

2.1. 既然 ARP 命令能看到,你的 Delphi 程序就可以通过调用 ARP 命令的方式来获得。最简单的办法是在 Dlphi 程序里面使用 TDosCommand 控件 运行一个 CMD 。

写到这里,才想起来我曾经做过这么一个东西,还把源码放到了 CNDEV 上面。

https://download.csdn.net/download/pcplayer/5601925
----------------------------------------------
-
作者:
男 keymark (嬲) ▲▲△△△ -
注册会员
2021/4/27 21:00:12
5楼: 这是人家代码的基本格式
begin
    if( )
    begin
    end
    else
    begin
        if()
        begin
        end
        else
        begn
        end
    end
end

另外微软有提供一个get error 的api 叫啥来着。。
----------------------------------------------
git config --global alias.co 'clone --recurse-submodules'
git config --global alias.up 'submodule update --init --recursive'
懒鬼提速
http://qalculate.github.io/downloads.html
https://www.cctry.com/
作者:
男 bahamut8348 (leonna) ★☆☆☆☆ -
普通会员
2021/4/28 8:53:06
6楼: socketfd
看这种标识符命名法就是linux下的代码,你要确定你找的在win下替代的函数和结构体这些东西能对应。并且你delphi的翻译能正确。起码你的package record要确定是否正确
----------------------------------------------
--
作者:
男 zspwy_007 (zsp) ▲▲▲△△ -
注册会员
2021/4/28 9:30:03
7楼:    谢谢楼下的各位!特别是4楼的分析,我也查阅了arp的相关资料,得出结论:arp的功能最多只能是在同一个局域网内可以做到!我的项目是个Delphi7写的web机房监控网站,由于安全需要,需要黑白名单的功能,IP限制没有问题,但现在的宽带等动态IP很普遍,使用IP就有些不便了,所以想使用MAC地址作为条件。
   我对IP底层通信了解不多,本来认为2个socket无论经过过少个路由最终能够通讯,就说明他们一定有关联,但分析了代码,也就是通过Socket得到地址信息,地址信息也只有协议类型和IP即端口等,用地址信息查阅arp缓存,所以和socket通讯没有关系了,所以得出结论:在internet上的服务器,基本不可能得到客户端的MAC。
   至于局域网中得到Mac,4楼的方法固然可以,但需要解决的问题较多,管道,等待执行完毕等,我的纯delphi代码:

type
  PMacAddr = ^TMacAddr;
  TMacAddr = record
    case Integer of
      0: (FI64: Int64);
      1: (FByt: array [0..5] of Byte)
  end;
  TSendARP = function(DestIP, SrcIP: DWORD; PMacAddr,PAddrLen: Pointer): DWORD; stdcall;

var
  FhIphlpapi: THandle = 0;
  FfnSendARP: TSendARP;

function GetMacByIP(IpAddr: DWORD): TMacAddr;
var len: Integer;
begin
  if FhIphlpapi=0 then begin
    FhIphlpapi:= LoadLibrary('iphlpapi.dll');
    if FhIphlpapi=0 then Exit;
    @FfnSendARP:= GetProcAddress(FhIphlpapi, 'SendARP');
  end;
  len:= SizeOf(TMacAddr);  Result.FI64:= 0;
  if (FfnSendARP(IpAddr, 0, @Result, @len) <> NO_ERROR) or (len <> 6) then
    Result.FI64:= 0;
end;

  6楼说的对那段C代码一定是linux下的,只是winsock下有ioctlsocket函数,功能类似,所以尝试下!谢谢。至于package record以我的经验应该不会错。
----------------------------------------------
-
作者:
男 supermay (supermay) ★☆☆☆☆ -
盒子活跃会员
2021/4/28 9:46:58
8楼: function SendARP(ipaddr: ulong; temp: dword; ulmacaddr: pointer; ulmacaddrleng: pointer): dword;
  stdcall; external 'Iphlpapi.dll' Name 'SendARP';

function GetMACByIP(const ip: AnsiString): string;
var
  MyIp: ulong;
  MyMac: array [0 .. 5] of BYTE;
  MyMacLength: ulong;
  ErrCode: integer;
begin
  MyIp := inet_addr(PAnsiChar(ip));
  MyMacLength := Length(MyMac);
  ErrCode := SendARP(MyIp, 0, @MyMac, @MyMacLength);
  if ErrCode = 0 then
    Result := Format('%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x', [MyMac[0], MyMac[1], MyMac[2], MyMac[3],
      MyMac[4], MyMac[5]])
  else
    Result := ''; // EorrCode: ErrCode
end;

function GetMacPhysicalAddress(Alana: integer = 0): string; overload;
var
  NCB: TNCB; // Netbios控制块
  AdapterStatus: TAdapterStatus; // 取网卡状态
  LanaEnum: TLanaEnum; // LANA枚举值
  I: integer;
begin
  Result := '';
  Try
    { http://blog.csdn.net/sushengmiyan/article/details/8543811
      一、枚举LANA值
      ①.申请分配一个TNCB结构          NCB: TNCB;
      ②.将TNCB结构变量初始化成O        ZeroMemory(@NCB , SizeOf(NCB));
      ③.置命令为NCBENUM          NCB.ncb_Command := chr(NCBENUM);
      ④.为ncb_buffer分配LANA_ENUM      NCB.ncb_buffer := @LANAENUM;
      ⑤.为NCB_length制定长度          NCB.NCB_length := Sizeof(LANAENUM);
      ⑥.调用Netbios函数获取Netbios     CRC := NetBios(@NCB);
      ⑦.返回值NRC_GOODRET表示成功      NCB.ncb_retcode = Chr(NRC_GOODRET)
    }
    ZeroMemory(@NCB, SizeOf(NCB));
    NCB.ncb_Command := Chr(NCBENUM);
    NCB.ncb_buffer := @LanaEnum;
    NCB.NCB_length := SizeOf(LanaEnum);
    NetBios(@NCB);
    if not(NCB.ncb_retcode = Chr(NRC_GOODRET)) then
      Exit;

    { http://blog.csdn.net/sushengmiyan/article/details/8543811
      二、重置计划使用的每个LANA编号
      ①.申请分配一个TNCB结构          NCB: TNCB;
      ②.将TNCB结构变量初始化成O        ZeroMemory(@NCB , SizeOf(NCB));
      ③.置命令为NCBRESET          NCB.ncb_Command := chr(NCBRESET);
      ④.给命令设置LANA编号          NCB.ncb_lana_num := LanaEnum.lana[Alana];
      ⑤.调用Netbios函数获取Netbios     CRC := NetBios(@NCB);
      ⑥.返回值NRC_GOODRET表示成功      NCB.ncb_retcode = Chr(NRC_GOODRET)
    }
    ZeroMemory(@NCB, SizeOf(NCB));
    NCB.ncb_Command := Chr(NCBRESET);
    NCB.ncb_lana_num := LanaEnum.lana[Alana];
    NetBios(@NCB);
    if not(NCB.ncb_retcode = Chr(NRC_GOODRET)) then
      Exit;

    { http://blog.csdn.net/sushengmiyan/article/details/8543811
      三、使用TAdapterStatus结构获取网卡地址
      ①.申请分配一个TNCB结构          NCB: TNCB;
      ②.将TNCB结构变量初始化成O        ZeroMemory(@NCB , SizeOf(NCB));
      ③.置命令为NCBASTAT          NCB.ncb_Command := chr(NCBASTAT);
      ④.为ncb_buffer分配LANA_ENUM      NCB.ncb_buffer := @LANAENUM;
      ⑤.设置ncb_callname          NCB.ncb_callname := '* ' + #0;
      ⑥.为ncb_buffer分配AdapterStatus  NCB.ncb_buffer := @AdapterStatus;
      ⑦.为NCB_length制定长度          NCB.NCB_length := Sizeof(AdapterStatus);
      ⑧.调用Netbios函数获取Netbios     CRC := NetBios(@NCB);
    }
    ZeroMemory(@NCB, SizeOf(NCB));
    NCB.ncb_Command := Chr(NCBASTAT);
    NCB.ncb_lana_num := LanaEnum.lana[Alana];
    NCB.ncb_callname[0] := '*';
    // 不明白为何如此设置,*代表啥子?
    // 有懂的可以邮件分享 429119108@qq.com   O(∩_∩)O谢谢
    NCB.ncb_buffer := @AdapterStatus;
    NCB.NCB_length := SizeOf(AdapterStatus);
    NetBios(@NCB);

    // 获取形如AA-BB-CC-DD-EE-FF形式的mac物理地址字符串
    for I := 0 to 5 do
      Result := Result + IntToHex(integer(AdapterStatus.adapter_address[I]), 2)

  Finally

  End;
end;
function GetMacPhysicalAddress(Alana: TAlanaName = eth0): string; overload;
const
  C_CMD: array [TAlanaName] of String = ('cat /sys/class/net/eth0/address',
    'cat /sys/class/net/wlan0/address');
var
  cmd: AnsiString;
begin
  cmd := C_CMD[Alana];
  Result := runCommand(MarshaledAString(cmd));

  if (Result = '') then
  begin
    cmd := C_CMD[TAlanaName.wlan0];
    Result := runCommand(MarshaledAString(cmd));
  end;

  Result := UpperCase(StringReplace(Result, ':', '', [rfReplaceAll]));
end;
----------------------------------------------
https://shop66090024.taobao.com/?spm=a313o.7775905.1998679131.d0011.6f6f2796Z7e3JX
作者:
男 iamdream (银河恒久远,梦想无止境!) ★☆☆☆☆ -
大贡献会员
2021/4/28 19:22:16
9楼: 你想像一下:如果你可以跨网直接获得MAC地址,那是多么地不安全!
----------------------------------------------
-广袤璀璨的银河,永无止境的梦想(梦无止境游银河) 博客挂了……
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2021/4/28 20:44:37
10楼: 跨网获取 MAC 地址,不是安全的问题。而是违背了网络通讯7层或者 N 层的原理。

网络通讯为啥要分层?

基本原理是,MAC 地址负责物理层。但如果和你通讯的对方机器根本不是基于以太网的,它就没有 MAC 地址,怎么通讯?因此在物理层上面,增加一个 IP 层。IP 层是纯的数字逻辑,和物理层的通讯协议无关。

那么,两个物理层完全不相同的机器如何建立物理上的连接?这就是中间需要一个【路由器】,也可以叫做【协议转换器】。

最简单直接的比方是你家里的上网路由器,一头接了电信局的线路,那个线路是电话线,不是局域网线,上面跑的通讯协议是 ADSL 的拨号协议。这一头,接你的电脑,插的是以太网线,上面跑的是以太网的通讯协议,才会有 MAC 地址。

再比如,和你的电脑连接的一台电脑或者设备,它只有串口,但你的电脑没有串口,而只有以太网接口。那么,你买一个串口转以太网的设备,把你的串口设备接到以太网上。那这个串口转以太网的设备,就是一个【协议转换器】。这样一来,你的电脑,不需要知道串口通讯协议,只需要向设备的 IP 地址发起 IP 通讯就可以了。

IP 发明的目的就是屏蔽掉各种设备的物理通讯连接的差异。
----------------------------------------------
-
作者:
男 delphiqiw (delphi我不会) ▲▲△△△ -
注册会员
2021/4/28 21:36:23
11楼:  通过IP获取局域网MAC地址
http://www.delphitop.com/html/wangluo/327.html
uses winsock;
function sendarp(ipaddr:ulong;temp:dword;ulmacaddr:pointer;ulmacaddrleng:pointer) : Dword; StdCall;
    External 'Iphlpapi.dll' Name 'SendARP';
function GetMACByIP(const Ip:string):string;
var
  MyIp:ulong;
  MyMac:array[0..5] of byte;
  MyMacLength:ulong;
  ErrCode:integer;
begin
  Myip:=inet_addr(PChar(Ip));
  MyMacLength:=Length(MyMac);
  ErrCode:=SendArp(MyIp,0,@MyMac,@MyMacLength);
  if ErrCode = 0 then
     Result := Format('%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x',[MyMac[0],MyMac[1],MyMac[2],MyMac[3],MyMac[4],MyMac[5]])
  else
     Result := ''; //EorrCode: ErrCode
end;
又一函数:
下面的函数以'XX-XX-XX-XX-XX-XX' 的格式返回远程或本地机器的MAC地址。

Function to return the MAC address of a remote or local machine in the format 'XX-XX-XX-XX-XX-XX' 

返回的MAC地址是一个能用在多个方面的唯一标识。使用方法:
ShowMessage(GetMacAddress('\\MHEYDON'); 
输出'00-02-08-E7-99-6B'
// ==========
//返回值是主机AServerName的MAC地址
//AServerName参数的格式为'\\ServerName' 或者 'ServerName'
//参数ServerName为空时返回本机的MAC地址
//MAC地址以'XX-XX-XX-XX-XX-XX'的格式返回
// ==========
function GetMacAddress(const AServerName: string): string;
type
    TNetTransportEnum = function (pszServer: PWideChar;
          Level: DWORD;
          var pbBuffer: pointer;
          PrefMaxLen: LongInt;
          var EntriesRead: DWORD;
          var TotalEntries: DWORD;
          var ResumeHandle: DWORD): DWORD; stdcall;
    TTransportInfo = record
          quality_of_service: DWord;
          number_of_vcs: DWORD;
          transport_name: PWChar;
          transport_address: PWChar;
          wan_ish: boolean;
          end;
    TNetApiBufferFree = function(Buffer: pointer): DWORD; stdcall;
    PTransportInfo = ^TTransportInfo;
var
    E,ResumeHandle,
    EntriesRead,
    TotalEntries: DWORD;
    FLibHandle: THandle;
    sMachineName,
    sMacAddr,
    Retvar: string;
    pBuffer: pointer;
    pInfo: PTransportInfo;
    FNetTransportEnum: TNetTransportEnum;
    FNetApiBufferFree: TNetApiBufferFree;
    pszServer: array[0..128] of WideChar;
    i,ii,iIdx: integer;
begin
  sMachineName := Trim(AServerName);
  Retvar := '00-00-00-00-00-00';
  //Add leading \\ if missing
  if (sMachineName <> '') and (length(sMachineName) >= 2) then  begin
    if copy(sMachineName,1,2) <> '\\' then
        sMachineName := '\\' + sMachineName;
  end;
  //Setup and load from DLL
  pBuffer := nil;
  ResumeHandle := 0;
  FLibHandle := LoadLibrary('NETAPI32.DLL');
  // Execute the external function
  if FLibHandle <> 0 then begin
      @FNetTransportEnum := GetProcAddress(FLibHandle,'NetWkstaTransportEnum');
      @FNetApiBufferFree := GetProcAddress(FLibHandle,'NetApiBufferFree');
      E := FNetTransportEnum(StringToWideChar(sMachineName,pszServer,129),0,
      pBuffer,-1,EntriesRead,TotalEntries,Resumehandle);
    if E = 0 then begin
      pInfo := pBuffer;
      // Enumerate all protocols - look for TCPIP
      for i := 1 to EntriesRead do begin
          if pos('TCPIP',UpperCase(pInfo^.transport_name)) <> 0 then begin
          // Got It - now format result 'xx-xx-xx-xx-xx-xx'
          iIdx := 1;
          sMacAddr := pInfo^.transport_address;
          for ii := 1 to 12 do begin
          Retvar[iIdx] := sMacAddr[ii];
          inc(iIdx);
          if iIdx in [3,6,9,12,15] then inc(iIdx);
          end;
        end;
        inc(pInfo);
      end;
      if pBuffer <> nil then FNetApiBufferFree(pBuffer);
    end;
    try
      FreeLibrary(FLibHandle);
    except
      // 错误处理
    end;
  end;
  result:=Retvar;
end;
----------------------------------------------
-
作者:
男 zspwy_007 (zsp) ▲▲▲△△ -
注册会员
2021/4/29 9:08:14
12楼: 感谢各位网友,特别是10楼的pcplayer,学习了
----------------------------------------------
-
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v2.1 版权所有 页面执行55.66406毫秒 RSS