Toc
  1. Windows
    1. 1. 通过 SendARP 函数来获取。
    2. 2. 通过 PIP_ADAPTER_INFO 结构体与 GetAdaptersInfo 函数来获取
  2. Linux
Toc
0 results found
C++ 获取本机 MAC 地址
2012/12/21 Development Linux c/c++

最近出现一个需求,要获取本机的 MAC 地址,就找了一些资料。

Windows

在 Windows 下,有两种方法可以获取 MAC 地址:

1. 通过 SendARP 函数来获取。

SendARP 的函数原型:

DWORD SendARP(
  _In_     IPAddr DestIP,
  _In_     IPAddr SrcIP,
  _Out_    PULONG pMacAddr,
  _Inout_  PULONG PhyAddrLen
);

根据函数原型可以看出,可以通过 IP 地址来获取,也可以无视 IP 地址,这样就获得第一个网卡的 MAC 地址。

第三个参数 pMacAddr 与第四个参数 PhyAddrLen 类型相同,是指向 ULONG 类型数组的指针,可以通过强转为 unsigned char *,用来显示等。
示例如下:

int GetOtherMacAddr(char *szIP,char *szBuf,int *pnBufLen)
{
	HRESULT hr;
	IPAddr  ipAddr;
	ULONG   pulMac[2];
	ULONG   ulLen;
	char strMacAddr[100]={0};
	ipAddr = inet_addr (szIP);
	memset (pulMac, 0xff, sizeof (pulMac));
	ulLen = 6;
	hr = SendARP (ipAddr, 0, pulMac, &ulLen);
	if(hr!=NO_ERROR)
		return 1;

	unsigned char * mac_addr=(unsigned char*)pulMac;
	sprintf(strMacAddr,"%02X:%02X:%02X:%02X:%02X:%02X",mac_addr[0],mac_addr[1],
		mac_addr[2],mac_addr[3],mac_addr[4],mac_addr[5]);
	if (*pnBufLen <= (int)strlen(strMacAddr) )
		return 2;
	strcpy(szBuf,strMacAddr);
	*pnBufLen = strlen(szBuf);

	return 0;
}

int GetLocalMacAddr(char *szMac,int *pnMacLen,char *szIP /*=NULL */)
{
	// 如果指定了 IP,则直接按 IP 获取 MAC
	// 否则,需要先获取本机名称,再获取 IP,再获取 MAC
	if (szIP != NULL)
		return GetOtherMacAddr(szIP,szMac,pnMacLen);

	char szHostName[256] = {0};
	int nRet = gethostname(szHostName,256);
	if (nRet == SOCKET_ERROR)
		return 1;

	// 获取本机名称
	struct hostent* hHost = gethostbyname(szHostName);
	if (hHost == NULL ||  hHost->h_addr_list[0] == NULL )
		return 2;

	// 获取 IP 地址
	memset(szHostName,0,256);
	strcpy(szHostName,inet_ntoa(*(struct in_addr *)hHost->h_addr_list[0]));

	// 获取 MAC
	return  GetOtherMacAddr(szHostName,szMac,pnMacLen);
}

2. 通过 PIP_ADAPTER_INFO 结构体与 GetAdaptersInfo 函数来获取

PIP_ADAPTER_INFO 结构体的结构如下:

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;

可以看到,有网卡名称、描述信息、MAC 地址、IP 地址列表、DHCP 服务器等信息。这次我们用到的就是 MAC 地址 BYTE[MAX_ADAPTER_ADDRESS_LENGTH。而 struct _IP_ADAPTER_INFO* Next 则告诉我们所有的网卡信息将会构成一个链表,通过 Next 来得到下一个网卡的信息。

void GetMAC(BSTR* pVal)
{
	CString sMAC;
	PIP_ADAPTER_INFO pAdapterInfo;
	PIP_ADAPTER_INFO pAdapter = NULL;
	DWORD dwRetVal = 0;
	ULONG ulOutBufLen;
	pAdapterInfo=(PIP_ADAPTER_INFO)malloc(sizeof(IP_ADAPTER_INFO));
	ulOutBufLen = sizeof(IP_ADAPTER_INFO);

	if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW)
	{free(pAdapterInfo);
		pAdapterInfo = (IP_ADAPTER_INFO *) malloc (ulOutBufLen);
	}

	if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR)
	{
		pAdapter = pAdapterInfo;
		if (pAdapter){sMAC.Format(_T(" 本机 MAC 地址为:%02x-%02x-%02x-%02x-%02x-%02x"),
				pAdapter->Address[0],
				pAdapter->Address[1],
				pAdapter->Address[2],
				pAdapter->Address[3],
				pAdapter->Address[4],
				pAdapter->Address[5]);
			//pAdapter = pAdapter->Next;  // 如果有多网卡,这儿就可以使用此条语句转到下一个网卡
		}
	}

	*pVal = ::SysAllocString(sMAC.MakeUpper());
}

Linux

在 Linux 下也类似。有一个名为 ifreq 的结构体,包含了网卡的所有信息。
ifreq 的结构如下:

struct ifreq
{char ifr_name[IFNAMSIZ];
	union
	{
		struct sockaddr ifru_addr;
		struct sockaddr ifru_dstaddr;
		struct sockaddr ifru_broadaddr;
		struct sockaddr ifru_netmask;
		struct sockaddr ifru_hwaddr;
		short int ifru_flags;
		int ifru_ivalue;
		int ifru_mtu;
		struct ifmap ifru_map;
		char ifru_slave[IFNAMSIZ]; /* Just fits the size */
		char ifru_newname[IFNAMSIZ];
		__caddr_t ifru_data;
	} ifr_ifru;
};

# define ifr_name ifr_ifrn.ifrn_name /* interface name */
# define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */
# define ifr_addr ifr_ifru.ifru_addr /* address */
# define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */
# define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
# define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */
# define ifr_flags ifr_ifru.ifru_flags /* flags */
# define ifr_metric ifr_ifru.ifru_ivalue /* metric */
# define ifr_mtu ifr_ifru.ifru_mtu /* mtu */
# define ifr_map ifr_ifru.ifru_map /* device map */
# define ifr_slave ifr_ifru.ifru_slave /* slave device */
# define ifr_data ifr_ifru.ifru_data /* for use by interface */
# define ifr_ifindex ifr_ifru.ifru_ivalue /* interface index */
# define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */
# define ifr_qlen ifr_ifru.ifru_ivalue /* queue length */
# define ifr_newname ifr_ifru.ifru_newname /* New name */
# define _IOT_ifreq _IOT(_IOTS(char),IFNAMSIZ,_IOTS(char),16,0,0)
# define _IOT_ifreq_short _IOT(_IOTS(char),IFNAMSIZ,_IOTS(short),1,0,0)
# define _IOT_ifreq_int _IOT(_IOTS(char),IFNAMSIZ,_IOTS(int),1,0,0)

稍微复杂点,但还是能找到我们所需要的信息的 - ifru_hwaddr。

通过下面的代码,就能完成工作了:

int GetLocalMacAddr(char *szMac,int *pnMacLen)
{
	int   sock;
	struct   ifreq   ifr;
	unsigned   char   mac[6];

	sock=socket(AF_INET,SOCK_DGRAM,0);    // 生成一个 TCP 的 socket
	if (sock==-1)
	{perror("socket");
		return 1;
	}

	strncpy(ifr.ifr_name,"eth0",sizeof(ifr.ifr_name));
	ifr.ifr_name[IFNAMSIZ-1]   =   0;

	memset(mac,0,sizeof(mac));
	if (ioctl(sock,SIOCGIFHWADDR,&ifr)< 0)
	{perror("ioctl");
		return 2;
	}

	memcpy(mac,&ifr.ifr_hwaddr.sa_data,sizeof(mac));
	char curmacstr[64];
	memset(curmacstr,0,sizeof(curmacstr));
	sprintf(curmacstr,"%.2X:%.2X:%.2X:%.2X:%.2X:%.2X",mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]);
	strcpy(szMac,curmacstr);
	return 0;
}
打赏
支付宝
微信
本文作者:CodingRabbit
版权声明:本文首发于CodingRabbit的博客,转载请注明出处!