最近出现一个需求,要获取本机的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;
}