网络编程,端口号,网络字节序,udp

前面一篇我们讲了网络的基础,网络协议栈是什么样的,数据如何流动传输的;接下来这篇,我们将进行实践操作,真正的让数据跨网络进行传输;

1.网络编程储备知识

1.1 初步认识网络编程

首先我们需要知道我们的网络编程实例化到现实生活中就是使用app进行交互,这样的交互其实就是两台不同机器上的进程在进行通信,这其实也叫做进程间通信,不过是通过网络来进行的罢了;此时两个进程的共享内存叫做网络

1.网络协议栈的下三层主要是用来保证数据的安全传输的问题

2.我们编程主要编写的是应用层代码

3.用户通过使用我们编写好的程序来进行数据发送与接收

 1.2 端口号

认识端口号

我们编写的应用层代码形成程序载入内存时成为进程需要被标识,让网络可以通过IP找到机器通过标识找到当前进程,而这个标识就叫做——端口号;

1.端口号是一个2字节16比特位的整数

2.可以使用端口号找到机器上唯一进程

3.一个端口号只能对应一个进程(但一个进程可以有多个端口号)

端口号与pid 

在我们前面linux系统的学习中,我们知道进程都有自己唯一的pid,那为什么我们不直接使用pid来标识进程呢,为什么还要引出一个端口号? 

因为端口号是属于网络体系的,而pid是属于系统体系的,如果将pid直接作为端口号来标识唯一进程也是可以的,可是这样会存在一些问题:

1.pid是随机变化的,每次进程启动是pid都会发生改变,有些端口号是固定不变的

2.当系统中的pid需要调整时会导致网络中的端口号也随之改变,会增加维护的难度

3.高内聚,低耦合的思想

端口号绑定进程 

我们如何理解将端口号绑定到一个进程上? 

我们可以看作端口号与进程的pcb指针形成了hash键值对,从而可以通过端口号找到相应的进程;

 1.3 网络字节序

世界上的机器是无穷多的,所以设备是个性化的,机器的实现一定是具有差异的,但是为了让机器可以正常通信,要使用网络覆盖底层的实现,让上层的交互规则是一样的;网络字节序就是这样一种覆盖的方式,我们的机器是存在大小端之分的,数据的传输也会存在数据的发送顺序问题,为了解决不同机器数据传输顺序的问题,需要使用网络下的函数,让传输进入网络中的数据按照网络字节序的形式存在;

只要是要传输到网络中的数据都要使用下面的函数对数据进行转换:

记忆方法:h为host(主机),n为network,l为长整型32位,s位短整型16位

htons就是将16位短整型数据由主机字节序转换为网络字节序;

ntohs就是将16位短整型数据由网络字节序转换为主机字节序;

1.4 初步认识tcp与udp 

tcp

理解为打电话形式,数据是一定准确的传输到对方的

1.传输可靠(中性词,可靠但复杂)

2.有连接

3.面向字节流传输

udp 

理解为发电报模式,我们不清楚我们的数据是否成功送达

1.传输不可靠(中性词,不可靠但简单)

2.无连接

3.面向数据报传输

1.5 socket套接字接口

套接字我们可以理解为底层开放给我们的接口,我们可以通过这个接口将数据送入底层进行传输;下面是socket套接字的接口:

1.创建socket套接字(我们可以理解为打开底层的网卡文件)

2.绑定套接字与端口号(也就是将进程和网卡连接起来,让进程可以向网卡发送数据)

3.udp接收数据报接口 

4.udp发送数据报接口

2.实现upd客户端服务器通信

接下来我们通过实践来学习网络编程:

下面是我编写好的一份udp客户端与服务端通信的代码:

network_code/socket_2024_9_17 · future/Linux - 码云 - 开源中国 (gitee.com)

我们使用udp模拟的现象是两个进程可以通过网络进行通信;

下面是对代码关键地方的讲解:

2.1 socket与bind

首先,进程如果想通过网络进行交互,那么肯定需要先连接网络;那么如何连接呢,我们需要使用socket和bind函数;

2.1.1 socket

socket的头文件是<sys/types.h>与<sys/socket.h>

socket的第一个参数用来指定套接字是在哪个域中属于协议家族中的哪个协议,man手册中展示了有这些协议:

socket的第二个参数是指定套接字的数据类型 ,指定通信语义,其中sock_stream是面向字节流的数据类型,sock_dgram是面向数据报的数据类型

最后一个参数一般设置为0即可,可以自动绑定协议,我们也可以显示的设置

 socket函数的返回值是一个文件描述符,这个文件描述符指向的是我们的网卡文件(linux下一切皆文件),当返回值为-1时代表打开socket失败并会设置errno错误码;

2.1.2 bind

我们通过上一步创建了套接字后,我们接下来就要将进程绑定上套接字,使得可以让网络找到当前进程;

bind函数的返回值成功绑定返回0失败返回-1并设置errno错误码,第一个参数是通过socket函数打开的网卡文件的文件描述符,第二参数是一个结构体的指针,第三个参数是这个结构体的大小;这个结构体指针是一个结构类型,可以用来接收两种不同类型的结构体;

 2.1.3 sockaddr

这是bind函数的第二个参数的结构体类型,这个类型作为指针时可以接收两种不同结构体:

这样bind就可以通过一个参数接收不同类型的数据了;

我们在真正编程的时候是需要设置好sockaddr_in的各个成员变量的:

        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_addr.s_addr = inet_addr(_ip.c_str());
        local.sin_port = htons(_port);

上面的代码中我们一个个的设置好了上面各个参数的值,这些值因为要输入网络中,所以都需要是网络字节序,我们要使用htons函数与inet_addr来操作修改为网络字节序;其中htons的头文件是<arpa/inet.h>,inet_addr的头文件是<netinet/in.h>和<arpa/inet.h>;

我们设置好变量后通过bind函数进行绑定即可;

2.2 recvfrom与sendto

我们打开socket并绑定bind好后,就可以向网络中发送数据了,由于我们现在使用的是udp协议的网络编程,我们使用recvfrom与sendto来进行数据接收与发送;

实现示例: 

                            
char buffer[1024];
struct sockaddr_in client;
socklen_t len = sizeof(client);

int n = recvfrom(_fd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&client, &len);
if (n == -1)
{
    log(WARNING, "recvfrom fail!");
}
buffer[n] = '\0';   

将从网络中接收到的数据放入buffer中;

 实现示例:

string info = "get message: ";
info += buffer;

n = sendto(_fd, info.c_str(), info.size(), 0, (const struct sockaddr *)&client, len);
if (n == -1)
{
    log(WARNING, "sendto fail!");
}

 2.3 网络状态查看指令

netstat -naup(-nlup)

 2.4 IP地址绑定细节

进程在绑定自己的ip地址时一般是不需要自己进行绑定的,我们设置绑定ip地址为0.0.0.0即可

原因:

1.云服务器上的ip地址是虚拟地址,无法绑定虚拟ip

2.一台主机可以有多张网卡,当我们显示绑定其中一张的ip时其他ip的信息我们无法收到,所以我们直接设置为0可以接收所有发送到本机ip上的信息

所以我们可以这样绑定ip地址:

sin_addr.s_addr=htonl(INADDR_ANY);

INADDR_ANY这个宏在底层定义的也是0.0.0.0地址

2.5 端口号绑定细节 

对于用户端,绑定端口号时我们不需要显示绑定,当客户端进程,开始发送消息时,系统会帮进程自动绑定本地的某个端口号与本地IP地址;

原因:

1.用户使用的客户端是不同厂家写的,不同厂家在写程序时如果显式的绑定了端口号,无法预测会不会和用户机器上其他的进程冲突,所以会将绑定端口号的操作交给用户机器的操作系统;

2.端口号对于用户来说并不重要,只需要让端口号识别唯一进程即可;

2.6 用户如何知道服务端端口号与ip地址 

其实端口号和地址可以看作我们平时上网用的网站,网站的字符串可以被解析为IP地址与端口号,而浏览器就是通过这个ip地址与端口号找到相应服务器的;而服务器的厂商会通过宣传让人们知道它网站的域名;

2.8 云服务器防火墙

在我们使用云服务器时,我们是无法做到网络连接的,因为我们本主机上的端口号为了安全是被云服务器厂商设置了防火墙的,目的就是为了防止有人通过外部设备访问云服务器主机上的进程;而我们想要进行网络通信就必须得开放这个端口,我的腾讯云服务器是在控制台的防火墙处进行配置,要先配置好端口号才可以让其他进程通过网络连接云服务器上进程;如果你也遇到了明明编写好了程序可是就是无法进行网络交互,你试着把IP地址改为127.0.0.1这是本地环回地址,可以连接本地的网络,如果这样成功了就代表是你的防火墙拦住了你,你去设置一下即可;

2.9 实现的现象

3.udp服务器和客户端升级

3.1 服务器端接收数据处理的封装

上面我们实现了基础的udp客户端和服务器,接下来,我们可以将服务器的功能进行封装,使其成为回调函数,让服务器中的main函数,通过参数传递给服务器类,从而实现一个执行命令,和通信的功能:

network_code/socket_2024_9_17/2_udp_pro · future/Linux - 码云 - 开源中国 (gitee.com)

上面的代码相比与最前面的代码仅仅只是对处理客户端发送来的数据进行了封装,模拟处来了现实生活中,客户端对服务器发送请求,服务器接收请求后对请求进行处理,随后再返回给客户端的情况:

下面是实现的情况:

#include"server.hpp"

string addStr(const string& buffer)
{
    string info = "get message: ";
    info += buffer;
    cout << info << endl;
    return info;
}

bool checksafe(const string& comd)
{
    vector<string> v={
        "rm"
    };
    for(auto word:v)
    {
        if(comd.find(word)!=string::npos)
        {
            return true;
        }
    }
    return false;
}

string command(const string& comd)
{
    cout<<comd<<endl;
    if(checksafe(comd))
    {
        return "command not safe";
    }
    FILE*f=popen(comd.c_str(),"r");
    if(f==nullptr)
    {
        cout<<strerror(errno)<<endl;
        exit(-1);
    }
    string back_info;
    while(true)
    {
        char buffer[4096]={0};
        char * ret=fgets(buffer,sizeof(buffer),f);
        if(ret==nullptr)break;
        back_info+=buffer;
    }
    return back_info;
}



int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        log(ERROR,"argc should be 2");
        exit(-1);
    }
    uint16_t port=stoi(argv[1]);
    unique_ptr<udpserver> ptr(new udpserver(port));
    ptr->run(addStr);

    return 0;
}

通过将函数方法传递实现了对数据的不同方式的处理,上面的addStr和command两个函数就是我们封装好的方法可以通过将这个两个方法进行传递从而,从而改变,服务端对数据的处理,其实在未来的工作中,我们的代码一般都是合作交互的,所以我们设计一个这样的接口交给其他人时,就可以减少耦合度,其他人只需要编写他们想要的接口,不会和我们的代码产生修改的矛盾;

3.2 windows下udp客户端实现

其实这并没有难度,代码实现是与我们linux客户端一样的只不过,我们需要注意windows下的库是如何处理的,我们需要将windows下的网络库先初始化,之后方才可以进行访问,使用库的功能,下面是windows下udp客户端的实现:

#define _WINSOCK_DEPRECATED_NO_WARNINGS 1

#include <WinSock2.h>
#include <iostream>
#pragma comment(lib,"ws2_32.lib")
#include<string>
#include <windows.h> // 用于字符编码转换
using namespace std;


// 将 GBK 编码转换为 UTF-8
string GbkToUtf8(const string& gbkStr)
{
	int len = MultiByteToWideChar(CP_ACP, 0, gbkStr.c_str(), (int)gbkStr.length(), NULL, 0);
	wchar_t* wstr = new wchar_t[len + 1];
	MultiByteToWideChar(CP_ACP, 0, gbkStr.c_str(), (int)gbkStr.length(), wstr, len);
	wstr[len] = '\0';

	len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
	char* str = new char[len + 1];
	WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, len, NULL, NULL);
	str[len] = '\0';

	string utf8Str(str);
	delete[] wstr;
	delete[] str;
	return utf8Str;
}

// 将 UTF-8 编码转换为 GBK
string Utf8ToGbk(const string& utf8Str)
{
	int len = MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, NULL, 0);
	wchar_t* wstr = new wchar_t[len + 1];
	MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, wstr, len);
	wstr[len] = '\0';

	len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL);
	char* str = new char[len + 1];
	WideCharToMultiByte(CP_ACP, 0, wstr, -1, str, len, NULL, NULL);
	str[len] = '\0';

	string gbkStr(str);
	delete[] wstr;
	delete[] str;
	return gbkStr;
}

int main()
{
	// 初始化套接字库
	WORD mVersion;
	WSADATA wsaData;
	int err;

	mVersion = MAKEWORD(1, 1);
	err = WSAStartup(mVersion, &wsaData);
	if (err != 0)
	{
		return err;
	}

	// 创建 UDP 套接字
	SOCKET sockCli = socket(AF_INET, SOCK_DGRAM, 0);
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = inet_addr("111.229.31.168"); // 服务器 IP 地址
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(10000); // 服务器端口

	SOCKADDR_IN addrCli;
	int len = sizeof(SOCKADDR);

	while (true)
	{
		cout << "请输入:";
		string sendBuf;
		getline(cin, sendBuf);
		string sendMsg = GbkToUtf8(sendBuf);
		char recvBuf[100] = { 0 };

		// 发送数据到服务器
		sendto(sockCli, sendMsg.c_str(), sendMsg.size(), 0, (SOCKADDR*)&addrSrv, len);

		// 接收服务器发送的数据
		recvfrom(sockCli, recvBuf, sizeof(recvBuf)-1, 0, (SOCKADDR*)&addrCli, &len);
		string recvMsg = Utf8ToGbk(recvBuf);
		cout << recvMsg << endl;

	}
	// 关闭套接字并清理库
	closesocket(sockCli);
	WSACleanup();

	return 0;
}

未来防止windows上编译器编码格式和linux客户端的不同,我们对数据进行了处理,使得windows客户端上发送的信息发送到服务器上不会变成乱码,我们还加入了两个编码转换函数,当然这两个编码转换函数的实现不是我自己实现的,因为windows下的底层编码,我没有怎么学过;但我知道有编码转换的问题,我通过使用gpt找到编码转换的函数,载入我的代码中,成功的实现了windows和linux下进程的交互工作,下面是实现的现象:

从而,我们也可以理解,我们为什么在linux操作环境下编写代码部署到linux机器上,而大多数使用windows机器的人也都可以享受linux服务器的服务,这便是网络带来的便利;

3.3 服务器与客户端改造形成聊天室

接下来,我们将服务器再改造一下,使其可以将一个客户端发送的数据进行处理后发送给所有的客户端,使得可以形成一个群聊的模式;

network_code/socket_2024_9_17/3_udp_chatRoom · future/Linux - 码云 - 开源中国 (gitee.com)

上面是我代码的完整实现;

其中主要有这几个功能:

1.我们要识别每个不同的机器

2.我们要将接收的信息转发给每个连接了服务器的客户端

3.客户端接收信息和发送信息是并发的

解决方式:

1.将接收到的客户端套接字信息sockaddr中的port和ip获取出来,标识每个不同机器

2.通过存入不同机器的sockaddr和ip信息来分别发送给每个不同的机器

3.通过多线程的方式让客户端的接收和发送功能同时运行

 下面是实现的现象:

 本篇我们实现了udp客户端和服务器的功能下一篇我们将实现tcp服务器;

3.4 补充

补充俩个指令:

sz(文件名) 发送数据到本地主机

rz                     从本地主机上获取数据

 对网络字节序转换函数的提醒:

我们在转换ip地址时,有可能需要将网络字节序转回本机,我们可以使用:

inet_ntoa(可能存在线程问题)

inet_ntop(使用是安全的)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java基础 3. 面向对象

Java基础 3. 面向对象 文章目录 Java基础 3. 面向对象3.1. 面向对象3.2. 对象的创建和使用3.3. 封装3.4. 构造方法3.5. this关键字3.6. static关键字JVM体系结构 [^现阶段不用掌握]3.7. 单例模式 [^初级]3.8. 继承3.9. 方法覆盖3.10. 多态3.11. super关键字3.12. final关键字3.…

你的虚拟猫娘女友,快来领取!--文心智能体平台

文章目录 一、引言二、赛事介绍2.1 简介2.2 比赛时间2.3 大赛具体链接2.4 第一期赛题 三、智能体创建流程3.1 进入文心智能体平台3.1 创建智能体3.1 虚拟猫娘女友特性3.1 智能体调优 四、引言智能体测试五、结语 一、引言 我是热爱生活的通信汪&#xff0c;今天这篇博文记录一…

[CSP-J 2022] 解密

题目来源&#xff1a;洛谷题库 [CSP-J 2022] 解密 题目描述 给定一个正整数 k k k&#xff0c;有 k k k 次询问&#xff0c;每次给定三个正整数 n i , e i , d i n_i, e_i, d_i ni​,ei​,di​&#xff0c;求两个正整数 p i , q i p_i, q_i pi​,qi​&#xff0c;使 n …

C语言 | Leetcode C语言题解之第448题找到所有数组中消失的数字

题目&#xff1a; 题解&#xff1a; int* findDisappearedNumbers(int* nums, int numsSize, int* returnSize) {for (int i 0; i < numsSize; i) {int x (nums[i] - 1) % numsSize;nums[x] numsSize;}int* ret malloc(sizeof(int) * numsSize);*returnSize 0;for (in…

“2024光明多多垂直农业挑战赛”决赛启动成功举办

由光明食品集团所属上花集团的光明花博邨基地&#xff0c;与拼多多携手&#xff0c;联合中国农业大学、浙江大学等共同举办的“2024光明多多垂直农业挑战赛暨第四届多多农研科技大赛”于9月20-21日正式启动决赛。来自上海交大、中国农大、上海农科院、国家农业智能装备工程技术…

基于Node.js+Express+MySQL+VUE科研成果网站发布查看科研信息科研成果论文下载免费安装部署

目录 1.技术选型‌ ‌2.功能设计‌ ‌3.系统架构‌ ‌4.开发流程‌ 5.开发背景 6.开发目标 7.技术可行性 8.功能可行性 8.1功能图 8.2 界面设计 8.3 部分代码 构建一个基于Spring Boot、Java Web、J2EE、MySQL数据库以及Vue前后端分离的科研成果网站&#xff0c;可…

Unity 2D RPG Kit 学习笔记

学习资料&#xff1a; B站教学视频&#xff1a;https://www.bilibili.com/video/BV1dC4y1o7A5?p1&vd_source707ec8983cc32e6e065d5496a7f79ee6 2D RPG Kit Documentation.pdf文档 1、2D RPG Kit Documentation文档 1.1、Scenes/TitleScreen 开始菜单工程 1.2、https://it…

铨顺宏科技携RTLS+RFID技术亮相工博会!

中国国际工业博览会盛大开幕&#xff01; 铨顺宏科技展亮点速递 铨顺宏科技展位号&#xff1a;F117 中国国际博览会今日开幕&#xff0c;铨顺宏科技携创新产品亮相&#xff0c;吸引众多参观者。 我们珍视此次国际盛会&#xff0c;将全力以赴确保最佳体验。 工作人员热情解答…

社交内容电商中的新机遇:2+1链动模式AI智能名片商城小程序

在当今的电商世界里&#xff0c;社交内容电商正蓬勃发展。这种模式基于高质量内容&#xff0c;将有着共同兴趣爱好的用户聚集起来形成社群&#xff0c;随后引导用户进行裂变式的传播与交易。无论是像微信、微博、快手、抖音、今日头条这样的平台形式&#xff0c;还是网红、“大…

【C语言指南】数据类型详解(下)——自定义类型

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《C语言指南》 期待您的关注 目录 引言 1. 结构体&#xff08;Struct&#xff09; 2. 联合体&#xff08;Union&#xff09; 3…

【机器学习】ID3、C4.5、CART 算法

目录 常见的决策树算法 1. ID3 2. C4.5 3. CART 决策树的优缺点 优点&#xff1a; 缺点&#xff1a; 决策树的优化 常见的决策树算法 1. ID3 ID3&#xff08;Iterative Dichotomiser 3&#xff09;算法使用信息增益作为特征选择的标准。它是一种贪心算法&#xff0c;信…

Python 课程20-Scikit-learn

前言 Scikit-learn 是 Python 中最流行的机器学习库之一&#xff0c;它提供了多种用于监督学习和无监督学习的算法。Scikit-learn 的特点是简单易用、模块化且具有高效的性能。无论是初学者还是专业开发者&#xff0c;都可以借助它进行快速原型设计和模型开发。 在本教程中&a…

PFC和LLC的本质和为什么要用PFC和LLC电路原因

我们可以用电感和电容的特性,以及电压和电流之间的不同步原理来解释PFC(功率因数校正)和LLC(谐振变换器)。 电感和电容的基本概念 电感(Inductor): 电感是一种储存电能的组件。它的电流变化比较慢,电流在电感中延迟,而电压变化得比较快。可以把电感想象成一个“滞后…

Tensorflow 2.0 cnn训练cifar10 准确率只有0.1 [已解决]

cifar10 准确率只有0.1 问题描述踩坑解决办法 问题描述 如果你看的是北京大学曹健老师的tensorflow2.0,你在class5的部分可能会遇见这个问题 import matplotlib.pyplot as plt import tensorflow as tf from tensorflow.keras.layers import Dense, Dropout,MaxPooling2D,Fla…

【Verilog学习日常】—牛客网刷题—Verilog企业真题—VL69

脉冲同步器&#xff08;快到慢&#xff09; 描述 sig_a 是 clka&#xff08;300M&#xff09;时钟域的一个单时钟脉冲信号&#xff08;高电平持续一个时钟clka周期&#xff09;&#xff0c;请设计脉冲同步电路&#xff0c;将sig_a信号同步到时钟域 clkb&#xff08;100M&…

长文本溢出,中间位置显示省略号

1.说明 Flutter支持在文本末尾显示溢出省略号。现在想要实现在文本中间位置显示省略号&#xff0c;这里使用的方法是通过TextPainter计算文本宽度。&#xff08;我目前没有找到更好的方法&#xff0c;欢迎大家指教。&#xff09; 2.效果 源码 1.MiddleEllipsisTextPainter …

全球IP归属地查询-IP地址查询-IP城市查询-IP地址归属地-IP地址解析-IP位置查询-IP地址查询API接口

IP地址城市版查询接口 API是指能够根据IP地址查询其所在城市等地理位置信息的API接口。这类接口在网络安全、数据分析、广告投放等多个领域有广泛应用。以下是一些可用的IP地址城市版查询接口API及其简要介绍 1. 快证 IP归属地查询API 特点&#xff1a;支持IPv4 提供高精版、…

TypeScript 算法手册 【数组基础知识】

文章目录 1. 数组简介1.1 数组定义1.2 数组特点 2. 数组的基本操作2.1 访问元素2.2 添加元素2.3 删除元素2.4 修改元素2.5 查找元素 3. 数组的常见方法3.1 数组的创建3.2 数组的遍历3.3 数组的映射3.4 数组的过滤3.5 数组的归约3.6 数组的查找3.7 数组的排序3.8 数组的反转3.9 …

深度学习常见术语介绍

文章目录 数据集&#xff08;Dataset&#xff09;特征&#xff08;Feature&#xff09;标签&#xff08;Label&#xff09;训练集&#xff08;Training Set&#xff09;测试集&#xff08;Test Set&#xff09;验证集&#xff08;Validation Set&#xff09;模型&#xff08;Mo…

什么是文件完整性监控(FIM)

组织经常使用基于文件的系统来组织、存储和管理信息。文件完整性监控&#xff08;FIM&#xff09;是一种用于监控和验证文件和系统完整性的技术&#xff0c;识别用户并提醒用户对文件、文件夹和配置进行未经授权或意外的变更是 FIM 的主要目标&#xff0c;有助于保护关键数据和…