C/C++ 获取主机网卡MAC地址

news2025/1/13 7:36:22

MAC地址(Media Access Control address),又称为物理地址或硬件地址,是网络适配器(网卡)在制造时被分配的全球唯一的48位地址。这个地址是数据链路层(OSI模型的第二层)的一部分,用于在局域网(LAN)中唯一标识网络设备。获取网卡地址主要用于网络标识和身份验证的目的。MAC地址是一个唯一的硬件地址,通常由网卡的制造商在制造过程中分配。通过获取MAC地址可以判断当前主机的唯一性可以与IP地址绑定并实现网络准入控制。

在Windows平台下获取MAC地址的方式有很多,获取MAC地址的常见方式包括使用操作系统提供的网络API(如Windows的GetAdaptersAddresses和GetAdaptersInfo),NetBIOS API,系统命令(如ipconfig /all),ARP缓存表查询,第三方库(如WinPcap或Libpcap),以及在编程语言中使用网络库。

首先第一种获取方法封装GetMacByGetAdaptersAddresses函数,该功能的实现通过调用系统中的GetAdaptersAddresses获取计算机的MAC地址。

该函数首先分配内存来存储适配器信息,然后调用 GetAdaptersAddresses 函数获取适配器信息。如果内存不足,它会重新分配足够的内存并再次调用该函数。接着,它遍历返回的适配器信息,找到第一个物理地址长度为6的适配器,然后将其MAC地址以格式化字符串的形式存储在传入的 macOUT 变量中。最后,释放分配的内存,并返回一个布尔值。

#include <iostream>
#include <winsock2.h>
#include <iphlpapi.h>
#include <string>

#pragma comment(lib, "Netapi32.lib")
#pragma comment(lib, "IPHLPAPI.lib")

using namespace std;

bool GetMacByGetAdaptersAddresses(std::string& macOUT)
{
	bool ret = false;

	ULONG outBufLen = sizeof(IP_ADAPTER_ADDRESSES);
	PIP_ADAPTER_ADDRESSES pAddresses = (IP_ADAPTER_ADDRESSES*)malloc(outBufLen);
	if (pAddresses == NULL)
		return false;

	if (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, pAddresses, &outBufLen) == ERROR_BUFFER_OVERFLOW)
	{
		free(pAddresses);
		pAddresses = (IP_ADAPTER_ADDRESSES*)malloc(outBufLen);
		if (pAddresses == NULL)
			return false;
	}

	if (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, pAddresses, &outBufLen) == NO_ERROR)
	{
		for (PIP_ADAPTER_ADDRESSES pCurrAddresses = pAddresses; pCurrAddresses != NULL; pCurrAddresses = pCurrAddresses->Next)
		{
			// 确保MAC地址的长度为 00-00-00-00-00-00
			if (pCurrAddresses->PhysicalAddressLength != 6)
				continue;
			char acMAC[32];
			sprintf(acMAC, "%02X-%02X-%02X-%02X-%02X-%02X",
				int(pCurrAddresses->PhysicalAddress[0]),
				int(pCurrAddresses->PhysicalAddress[1]),
				int(pCurrAddresses->PhysicalAddress[2]),
				int(pCurrAddresses->PhysicalAddress[3]),
				int(pCurrAddresses->PhysicalAddress[4]),
				int(pCurrAddresses->PhysicalAddress[5]));
			macOUT = acMAC;
			ret = true;
			break;
		}
	}

	free(pAddresses);
	return ret;
}

int main(int argc, char *argv[])
{
	std::string refBuffer;

	GetMacByGetAdaptersAddresses(refBuffer);
	std::cout << "Mac地址: " << refBuffer << std::endl;

	system("pause");
	return 0;
}

第二种方式GetMacByGetAdaptersInfo函数,通过调用系统的GetAdaptersInfo获取计算机的主网卡的MAC地址。函数首先分配内存来存储适配器信息,然后调用GetAdaptersInfo获取适配器信息。如果内存不足,它会重新分配足够的内存并再次调用该函数。接着,它遍历返回的适配器信息,找到第一个类型为以太网且物理地址长度为6的适配器,然后将其MAC地址以格式化字符串的形式存储在传入的macOUT变量中。最后,释放分配的内存,并返回一个布尔值。

#define _CRT_SECURE_NO_WARNINGS
#define _WIN32_DCOM
#define _CRT_NONSTDC_NO_DEPRECATE

#include <iostream>
#include <winsock2.h>
#include <iphlpapi.h>
#include <string>

#pragma comment(lib, "Netapi32.lib")
#pragma comment(lib, "IPHLPAPI.lib")

using namespace std;

bool GetMacByGetAdaptersInfo(std::string& macOUT)
{
	bool ret = false;

	ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
	PIP_ADAPTER_INFO pAdapterInfo = (IP_ADAPTER_INFO*)malloc(sizeof(IP_ADAPTER_INFO));
	if (pAdapterInfo == NULL)
		return false;

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

	if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == NO_ERROR)
	{
		for (PIP_ADAPTER_INFO pAdapter = pAdapterInfo; pAdapter != NULL; pAdapter = pAdapter->Next)
		{
			// 确保是以太网
			if (pAdapter->Type != MIB_IF_TYPE_ETHERNET)
				continue;
			// 确保MAC地址的长度为 00-00-00-00-00-00
			if (pAdapter->AddressLength != 6)
				continue;
			char acMAC[32];
			sprintf(acMAC, "%02X-%02X-%02X-%02X-%02X-%02X",
				int(pAdapter->Address[0]),
				int(pAdapter->Address[1]),
				int(pAdapter->Address[2]),
				int(pAdapter->Address[3]),
				int(pAdapter->Address[4]),
				int(pAdapter->Address[5]));
			macOUT = acMAC;
			ret = true;
			break;
		}
	}

	free(pAdapterInfo);
	return ret;
}

int main(int argc, char *argv[])
{
	std::string refBuffer;

	GetMacByGetAdaptersInfo(refBuffer);
	std::cout << "Mac地址: " << refBuffer << std::endl;

	system("pause");
	return 0;
}

第三种封装一个GetMacByNetBIOS函数,其使用NetBIOS API获取指定适配器号(adapterNum)的MAC地址。函数首先通过NCBRESET命令重置指定网卡以便进行查询。接着,使用NCBASTAT命令获取接口卡的状态块,其中包含了适配器的物理地址。如果NetBIOS调用成功,将适配器的MAC地址以格式化字符串的形式存储在传入的macOUT变量中,最后返回一个布尔值。

#include <iostream>
#include <winsock2.h>
#include <iphlpapi.h>
#include <string>

#pragma comment(lib, "Netapi32.lib")
#pragma comment(lib, "IPHLPAPI.lib")

using namespace std;

bool GetAdapterInfo(int adapterNum, std::string& macOUT)
{
	NCB Ncb;
	memset(&Ncb, 0, sizeof(Ncb));

	// 重置网卡 以便我们可以查询
	Ncb.ncb_command = NCBRESET;
	Ncb.ncb_lana_num = adapterNum;
	if (Netbios(&Ncb) != NRC_GOODRET)
		return false;

	// 准备取得接口卡的状态块
	memset(&Ncb, sizeof(Ncb), 0);
	Ncb.ncb_command = NCBASTAT;
	Ncb.ncb_lana_num = adapterNum;
	strcpy((char*)Ncb.ncb_callname, "*");
	struct ASTAT
	{
		ADAPTER_STATUS adapt;
		NAME_BUFFER nameBuff[30];
	}adapter;
	memset(&adapter, sizeof(adapter), 0);
	Ncb.ncb_buffer = (unsigned char*)&adapter;
	Ncb.ncb_length = sizeof(adapter);
	if (Netbios(&Ncb) != 0)
		return false;

	char acMAC[32];
	sprintf(acMAC, "%02X-%02X-%02X-%02X-%02X-%02X",
		int(adapter.adapt.adapter_address[0]),
		int(adapter.adapt.adapter_address[1]),
		int(adapter.adapt.adapter_address[2]),
		int(adapter.adapt.adapter_address[3]),
		int(adapter.adapt.adapter_address[4]),
		int(adapter.adapt.adapter_address[5]));
	macOUT = acMAC;
	return true;
}

bool GetMacByNetBIOS(std::string& macOUT)
{
	// 取得网卡列表
	LANA_ENUM adapterList;
	NCB Ncb;
	memset(&Ncb, 0, sizeof(NCB));
	Ncb.ncb_command = NCBENUM;
	Ncb.ncb_buffer = (unsigned char*)&adapterList;
	Ncb.ncb_length = sizeof(adapterList);
	Netbios(&Ncb);

	// 取得MAC
	for (int i = 0; i < adapterList.length; ++i)
	{
		if (GetAdapterInfo(adapterList.lana[i], macOUT))
			return true;
	}

	return false;
}

int main(int argc, char *argv[])
{
	std::string refBuffer;

	GetMacByNetBIOS(refBuffer);
	std::cout << "Mac地址: " << refBuffer << std::endl;

	system("pause");
	return 0;
}

三种方式均可以输出系统的MAC地址,可根据自己的需求选择;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1225968.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

端口号大揭秘:网络世界的“门牌号”有多牛?

大家好&#xff0c;今天我们来聊一聊网络中的端口号。如果你以为端口号只是冷冰冰的数字&#xff0c;那你就大错特错了。端口号&#xff0c;这些看似枯燥的数字背后&#xff0c;隐藏着一个个生动的故事。 目录 大家好&#xff0c;今天我们来聊一聊网络中的端口号。如果你以为端…

odoo17前端js框架的演化

odoo17发布了&#xff0c;从界面上看&#xff0c;变化还是很明显的&#xff0c;比16更漂亮了&#xff0c;本来以为源码不会发生太大的变化&#xff0c;结果仔细一瞧&#xff0c;变化也不小。 1、打包好的文件数量和大小发生了变化 打包好的文件从两个变成了一个&#xff0c;在…

适用于全部安卓手机的 5 大免费 Android 数据恢复

您是否面临这样一种情况&#xff0c;即在Android设备上丢失了一些重要文件&#xff0c;但不知道应该选择哪种Android数据恢复来取回它们&#xff1f; 如果您以前从未备份过Android数据&#xff0c;则很难解决问题。 本文将介绍排名前5位的免费Android数据恢复软件。 您可以获…

同花顺,通达信,东方财富股票竞价,早盘板块、概念、题材竞价数据接口

早盘板块、概念、题材竞价数据接口 量化接口地址&#xff1a;https://stockapi.com.cn 通过分析每天早盘的板块竞价&#xff0c;从而判断出今日主力资金的看好方向 地址&#xff1a; https://stockapi.com.cn/v1/base/bkjjzq?tradeDate2023-11-08再结合个股竞价数据筛选出自…

科大讯飞会议笔记本、GoodNotes、E人E本 功能及体验对比

科大讯飞会议笔记本、GoodNotes、E人E本功能及体验对比 【旧文档&#xff0c;怕失传】 通过对科大讯飞会议笔记本、基于iPad的GoodNotes以及E人E本的各项功能指标进行了实际对比&#xff0c;得出了以下结果&#xff1a; 在实际体验中&#xff0c;科大讯飞笔记本在录音方面表…

知道数字孪生发展的四个阶段,你就能明白数字孪生的真正价值了

数字孪生的发展经历了四个阶段。以下是每个阶段的详细描述&#xff1a; 1、数字孪生萌芽期&#xff1a;这个阶段以模型仿真驱动为特征。20世纪80年代以来&#xff0c;CAD、CAE、CAM等计算机建模、模拟仿真技术开始迅猛发展&#xff0c;并在制造业领域广泛应用。该阶段主要通过…

JSP基本表单和Request对象使用例子

表单的jsp&#xff1b; <%page contentType"text/html;charsetgbk" pageEncoding"UTF-8"%> <!DOCTYPE html> <html><head><meta http-equiv"Content-Type" content"text/html; charsetUTF-8"><titl…

2023全球边缘计算大会深圳站-核心PPT资料下载

一、峰会简介 边缘计算&#xff0c;是指在靠近物或数据源头的一侧&#xff0c;采用网络、计算、存储、应用核心能力为一体的开放平台&#xff0c;就近提供最近端服务。其应用程序在边缘侧发起&#xff0c;产生更快的网络服务响应&#xff0c;满足行业在实时业务、应用智能、安…

excel中设置图表图例的位置

例如&#xff0c;在excel中已经做好了一个折线图&#xff1a; 可以看到&#xff0c;默认图例是在图表的右侧&#xff0c;减小了图的横向展示区域。我们可以把图例放到图的上边、或者下边。 双击图表&#xff1a; 鼠标放在图例上方&#xff0c;出现了浮动文字“图例”&#…

代码随想录算法训练营第二十七天|39 组合总和 40 组合总和II 131分割回文串

目录 39 组合总和 40 组合总和II 131分割回文串 39 组合总和 class Solution {List<List<Integer>>res new ArrayList<>();List<Integer>newList new LinkedList<>();public List<List<Integer>> combinationSum(int[] candid…

【算法挨揍日记】day22——面试题 17.16. 按摩师、213. 打家劫舍 II

面试题 17.16. 按摩师 面试题 17.16. 按摩师 题目描述&#xff1a; 一个有名的按摩师会收到源源不断的预约请求&#xff0c;每个预约都可以选择接或不接。在每次预约服务之间要有休息时间&#xff0c;因此她不能接受相邻的预约。给定一个预约请求序列&#xff0c;替按摩师找…

系列七、GC垃圾回收【四大垃圾算法-标记压缩算法】

一、原理 在整理压缩阶段&#xff0c;不再对标记的对象回收&#xff0c;而是通过所有存活对象都向一端移动。可以看到&#xff0c;标记的存活对象将会被整理&#xff0c;按照内存地址依次排列。如此一来&#xff0c;当我们需要给新对象分配内存时&#xff0c;JVM只需要持有一个…

记录-2023/11/18

1. 序列器中可以定义update方法 2. 分页之后前端获取数据的方式要进行改变 JavaScript的switch语法2 situation "a"switch (situation) {case "a":console.log("a")breakcase "b":console.log("b")breakcase "c&quo…

0基础编程教学,编程零基础该学什么,中文编程工具下载

0基础编程教学&#xff0c;编程零基础该学什么&#xff0c;中文编程工具下载 给大家分享一款中文编程工具&#xff0c;零基础轻松学编程&#xff0c;不需英语基础&#xff0c;编程工具可下载。 这款工具不但可以连接部分硬件&#xff0c;而且可以开发大型的软件&#xff0c;象…

深度学习系列53:mmdetection上手

1. 安装 使用openmim安装&#xff1a; pip install -U openmim mim install "mmengine>0.7.0" mim install "mmcv>2.0.0rc4"2. 测试案例 下载代码和模型&#xff1a; git clone https://github.com/open-mmlab/mmdetection.git mkdir ./checkpoi…

python 计算最大回撤

1. 什么是最大回撤 最大回撤是评估金融产品收益的一个非常重要的风险指标&#xff0c;它指的是在选定历史周期内任一历史时点往后推&#xff0c;产品净值走到最低点时的收益率回撤幅度的最大值。 以上图为例&#xff0c; 最大回撤 ( V a l u e A − V a l u e B ) V a l u e …

取数游戏2(动态规划java)

取数游戏2 题目描述 给定两个长度为n的整数列A和B&#xff0c;每次你可以从A数列的左端或右端取走一个数。假设第i次取走的数为ax&#xff0c;则第i次取走的数的价值vibi⋅ax&#xff0c;现在希望你求出∑vi的最大值。 输入格式 第一行一个数T &#xff0c;表示有T 组数据。…

如何使用websocket+node.js实现pc后台与小程序端实时通信

如何使用websocketnode.js实现pc后台与小程序端实时通信 一、使用node.js创建一个服务器二、pc后台连接ws三、小程序端连接ws四、实现效果 实现功能:实现pc后台与小程序端互发通信能够实时检测到 一、使用node.js创建一个服务器 1.安装ws依赖 npm i ws2.创建index.js const…

(免费领源码)python+django+mysql线上兼职平台系统83320-计算机毕业设计项目选题推荐

摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对线上兼职等问题&#xff0c;对线上兼职进行…

【狂神说Java】Docker概述 | Docker安装 | Docker的常用命令

✅作者简介&#xff1a;CSDN内容合伙人、信息安全专业在校大学生&#x1f3c6; &#x1f525;系列专栏 &#xff1a;【狂神说Java】 &#x1f4c3;新人博主 &#xff1a;欢迎点赞收藏关注&#xff0c;会回访&#xff01; &#x1f4ac;舞台再大&#xff0c;你不上台&#xff0c…