C-S模式之实现一对一聊天
天天开心!!!
文章目录
- 一、如何实现一对一聊天?
- 1. 服务器设计
- 2. 客户端设计
- 3. 服务端代码实现
- 4. 客户端代码实现
- 5. 实现说明
- 6.实验结果
- 二、改进
- 常见的服务器高并发方案
- 1. 多线程/多进程模型
- 2. I/O多路复用
- 3. 异步I/O(Asynchronous I/O)
- 4. 事件驱动框架
- 5. Reactor模式
- 6. Proactor模式
- 7. 协程(Coroutine)
- 三、使用epoll改进Server服务端代码
- 1. epoll基本工作流程
- 2. 服务端的实现思路
- 3. 改良后的具体实现代码
- 实验结果:
- 4. 实现说明
- 5. 优势
一、如何实现一对一聊天?
在C++的Socket编程中,实现一对一聊天的基本思路是构建一个客户端(Client)和一个服服务端(Server),并让每个客户端之间通过服务器进行消息的转发,具体步骤如下:
1. 服务器设计
服务器愮接受多个客户端的连接,并为每对用户建立专属的通信通道,实现流程如下:
- 服务端启动并监听某个端口
- 每当有客户端连接时,服务端接受连接并创建一个独立的线程或使用I/O多路复用(如select、epoll)来处理客户端请求。
- 服务端维护一个客户端的连接表(这里我们使用map来存储),当两个客户端匹配时,将彼此的消息进行转发
- 实现聊天消息的收发和转发逻辑
2. 客户端设计
客户端需要与服务器保持连接,并能够持续地发送和接收消息。实现流程如下:
- 客户端启动后,连接到服务端指定的IP和端口
- 客户端可以发送消息给服务端,服务端将消息转发给目标用户
- 客户端持续接收从服务器发送来的消息,显示在用户界面或控制台
3. 服务端代码实现
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <thread>
#include <map>#define PORT 8080
#define BUFFER_SIZE 1024std::map<int,int> client_map; //存储客户端的配对关系//处理客户端通信
void handle_client(int client_socket){char buffer[BUFFER_SIZE];int target_socket=client_map[client_socket]; //获取配对的客户端socketwhile(true){memset(buffer,0,sizeof(buffer));//清空缓冲区ssize_t bytes_received=recv(client_socket,buffer,BUFFER_SIZE,0);//接收数据的长度if(bytes_received<=0){//如果接收失败,则关闭连接std::cerr<<"Error receiving data from client"<<std::endl;//输出错误信息close(client_socket);return;}std::cout<<"收到消息:"<<buffer<<std::endl;//将消息转发给目标客户端if(client_map.find(target_socket)!=client_map.end()){send(target_socket,buffer, strlen(buffer),0);}else{std::cerr<<"Error sending data to target client"<<std::endl;}}
}int main()
{int server_socket;struct sockaddr_in server_addr;//创建服务器套接字server_socket=socket(AF_INET,SOCK_STREAM,0);if(server_socket==0){std::cerr<<"Error creating server socket"<<std::endl;return -1;}//初始化地址结构server_addr.sin_family=AF_INET;server_addr.sin_addr.s_addr=INADDR_ANY;server_addr.sin_port=htons(PORT);//绑定地址和端口if(bind(server_socket,(struct sockaddr*)&server_addr,sizeof(server_addr))<0){std::cerr<<"Error binding server socket"<<std::endl;return -1;}//监听客户端连接if(listen(server_socket,3)<0){std::cerr<<"Error listening for connections"<<std::endl;return -1;}std::cout<<"等待客户端连接..."<<std::endl;while(true){struct sockaddr_in client_addr;socklen_t addr_len=sizeof(client_addr);int new_socket=accept(server_socket,(struct sockaddr*)&client_addr,&addr_len);if(new_socket<0){std::cerr<<"Error accepting connection"<<std::endl;continue;}std::cout<<"新客户端连接"<<inet_ntoa(client_addr.sin_addr)<<std::endl;//这里为了简化,我们直接假设是两客户端配对,client_map存储配对关系if(client_map.empty()){client_map[new_socket]=-1;//第一个客户端,暂时没有配对}else{for(auto &pair:client_map){if(pair.second==-1){client_map[new_socket]=pair.first;//第二个客户端与第一个客户端配对client_map[pair.first]=new_socket;//第一个客户端与第二个客户端配对std::cout<<"客户端配对成功"<<std::endl;break;}}}//创建线程处理新客户端std::thread client_thread(handle_client,new_socket);client_thread.detach();//线程分离,主线程不阻塞}close(server_socket);return 0;
}
4. 客户端代码实现
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <thread>#define PORT 8080
#define BUFFER_SIZE 1024//处理服务器消息
void receive_message(int socket){char buffer[BUFFER_SIZE];while(true){memset(buffer, 0, sizeof(buffer));//清空缓冲区ssize_t bytes_received = recv(socket, buffer, BUFFER_SIZE, 0);if(bytes_received <= 0){std::cout << "服务器断开连接..." << std::endl;close(socket);return;}std::cout<<"收到消息:"<<buffer<<std::endl;}
}int main()
{int client_socket;//客户端套接字struct sockaddr_in server_addr;//创建客户端套接字client_socket=socket(AF_INET, SOCK_STREAM, 0);if(client_socket<0){std::cout<<"创建套接字失败"<<std::endl;return -1;}//初始化服务器地址server_addr.sin_family=AF_INET;server_addr.sin_port=htons(PORT);server_addr.sin_addr.s_addr=inet_addr("127.0.0.1");//服务器IP//连接到服务器if(connect(client_socket,(struct sockaddr*)&server_addr,sizeof(server_addr))<0){std::cerr<<"连接服务器失败"<<std::endl;return -1;}std::cout<<"连接到服务器成功"<<std::endl;//创建线程接受服务器消息std::thread receive_thread(receive_message,client_socket);//创建线程(函数,函数的形参)receive_thread.detach();//线程分离,主线程结束后,子线程也结束//发送消息给服务器char message[BUFFER_SIZE];while(true){std::cout<<"请输入消息:";std::cin.getline(message,BUFFER_SIZE);send(client_socket,message,strlen(message),0);}close(client_socket);return 0;
}
5. 实现说明
- 服务端:服务器监听客户端连接并维护一个客户端配对表client_map;当两个客户端配对后,消息就可以在它们之间转发。这里使用了多线程来处理每个客户端的通信
- 客户端:客户端连接到服务器并启动一个线程用于接收来自服务器的消息,用户可以输入消息并发送给服务器,服务器负责转发消息给配对的客户端
6.实验结果

二、改进
我们可以使用epoll或者select替代多线程处理,提高服务器的并发性能。也可以增加心跳机制来检测客户端是否断开连接。如果要完善,还可以增加实现身份验证和聊天室功能,使得用户可以自由选择与谁聊天。
常见的服务器高并发方案
1. 多线程/多进程模型
每个连接由一个独立的线程或进程处理,能够比较简单的实现并发处理
- 优点:代码易于理解,编写较为简单
- 缺点:线程或进程的开销较大,在高并发场景下,大量的线程/进程会带来系统资源消耗和性能瓶颈,特别是在数千甚至数万个连接的时候
2. I/O多路复用
I/O多路复用可通过少量的线程处理大量并发连接,常用的方法包括:
-
select:通过一个文件描述符集合监视多个文件描述符是否有I/O事件
(1)优点:简单、易用、跨平台支持好
(2) 缺点:性能不佳,处理大量连接时,每次调用select都要遍历整个描述符集合,效率很低 -
poll:与select类似,但没有文件描述符限制
(1)优点:避免了select的文件描述符限制
(2) 缺点:与select雷系,性能仍然不高,遍历整个描述符集合 -
epoll(Linux专用):epoll是Linux特有的I/O多路复用机制,性能更好,适合处理大量并发连接
(1)优点:不会遍历所有文件描述符,性能优异,适用于高并发场景
(2) 缺点:仅限于Linux系统
3. 异步I/O(Asynchronous I/O)
异步I/O通过事件驱动机制,程序不需要等待I/O操作的完成,而是注册事件,事件触发时进行处理。常见的异步I/O实现包括:
- Windows:使用IOCP(I/O Completion Port)实现异步I/O处理
- Linux:可以使用libaio或者基于epoll实现的异步I/O
- 优点:真正的异步,无需阻塞等待I/O操作,性能高,适合高并发
- 缺点:编写异步代码比较复杂,调试很困难
4. 事件驱动框架
利用现成的事件驱动框架来处理高并发来凝结,常见的库包括
- libevent:基于事件的异步I/O库,支持epoll、kqueue等多种I/O多路复用机制,适合处理大量并发连接
- libuv:跨平台异步I/O库,Node.js就是基于libuv实现的
- Moduo:C++高性能网络库,基于epoll和线程池,适用于Linux下的高并发场景
- 优点:封装好、使用方便,能够提高并发效率
- 缺点:引入了额外的依赖,性能调优相对不够灵活
5. Reactor模式
Reactor模式是I/O多路复用的一种常见的实现模式,它通过注册I/O事件,将事件分发给事件处理器
- 典型实现:使用epoll或select监听事件,再结合事件处理回调函数进行处理
- 优点:能够较好地处理大量并发连接,灵活性高
- 缺点:编写和理解较为复杂,需要维护事件循环和回调函数
6. Proactor模式
Proactor模式是异步I/O的常见实现,区别于Reactor,Proactor是I/O操作完成后再进行回调
- 典型实现:Windows上的ICP就是Proactor模式实现的
- 优点:异步操作更加彻底。I/O操作由操作系统处理,减少了用户态的干预
- 缺点:实现较为复杂,调试难
7. 协程(Coroutine)
协程是一种轻量级的线程,能够在用户态进行切换,使用协程可以避免线程切换的开销,同时实现高并发。可以结合I/O多路复用技术,如epoll,来实现高效的协程并发
- 典型框架:如Boost.Asio支持协程、libgo协程库等
- 优点:切换开销小、性能高,代码易于理解
- 缺点:调试比较复杂,尤其实在上下文切换时容易出现问题
三、使用epoll改进Server服务端代码
使用epoll实现一对一聊天的服务端,可以大大提高服务器的并发处理能力,相比于传统的多线程或select,epoll更适合处理大量客户端的连接,尤其是在高并发场景下。
1. epoll基本工作流程
- 创建epoll文件描述符:使用epoll_create创建epoll实例
- 注册事件:通过epoll_ctl将套接字添加到epoll实例中,并设置要监听的事件(如EPOLLIN,表示有数据可读)
- 等待事件:使用epoll_wait等待事件发生
- 处理事件:一旦事件发生,处理相应的客户端读写操作
2. 服务端的实现思路
- 启动epoll实例:监听客户端的连接请求
- 当有新的客户端连接时,将其注册到epoll实例中
- 维护客户端配对关系:服务端为每个客户端建立配对表
- 消息的收发和转发:当接收到某个客户端的消息时,通过配对表找到对应的目标客户端,并将消息转发过去
3. 改良后的具体实现代码
#include <iostream>
#include <string>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <map>
#include <sys/epoll.h> // epoll头文件#define PORT 8080
#define BUFFER_SIZE 1024
#define MAX_EVENTS 10std::map<int,int> client_map;//客户端配对表int main(){int server_socket,epoll_fd;// 服务器套接字,epoll文件描述符struct sockaddr_in server_addr; // 服务器地址结构体//创建服务器套接字server_socket = socket(AF_INET, SOCK_STREAM, 0);if(server_socket <0){std::cerr << "创建服务器套接字失败" << std::endl;return -1;}//设置地址复用int opt=1;setsockopt(server_socket,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//设置地址复用//初始化服务器地址server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = INADDR_ANY;server_addr.sin_port = htons(PORT);//绑定地址到服务器套接字if(bind(server_socket,(struct sockaddr*)&server_addr,sizeof(server_addr))<0){std::cerr << "绑定地址到服务器套接字失败" << std::endl;return -1;}//监听端口if(listen(server_socket,10)<0){std::cerr << "监听端口失败" << std::endl;return -1;}//创建epoll实例epoll_fd = epoll_create1(0);if(epoll_fd ==-1){std::cerr<<"创建epoll实例失败"<<std::endl;return -1;}//将服务器套接字加入到epoll实例中struct epoll_event event;event.events = EPOLLIN;event.data.fd = server_socket;epoll_ctl(epoll_fd,EPOLL_CTL_ADD,server_socket,&event);std::cout<<"服务器启动成功,等待客户端连接...."<<std::endl;struct epoll_event events[MAX_EVENTS]; // epoll事件数组, 用于存储就绪事件while(true){//无限等待事件int event_count=epoll_wait(epoll_fd,events,MAX_EVENTS,-1);for(int i=0;i<event_count;i++){if(events[i].data.fd==server_socket){//处理新的客户端连接struct sockaddr_in client_addr;socklen_t clinet_len=sizeof(client_addr);int client_socket=accept(server_socket,(struct sockaddr*)&client_addr,&clinet_len);//接受客户端连接if(client_socket<0){std::cerr<<"接受客户端连接失败"<<std::endl;continue;}std::cout<<"新的客户端连接:"<<inet_ntoa(client_addr.sin_addr)<<std::endl;//将新客户端连接加入到epoll监听中event.events=EPOLLIN;event.data.fd=client_socket;epoll_ctl(epoll_fd,EPOLL_CTL_ADD,client_socket,&event);//客户端配对处理if(client_map.empty()){client_map[client_socket]=-1;//第一个客户端,暂时没有配对}else{for(auto &pair:client_map){if(pair.second==-1){client_map[client_socket]=pair.first;//找到第一个没有配对的客户端,进行配对client_map[pair.first]=client_socket;//更新第一个没有配对的客户端的配对信息break;}}}}else{//处理客户端的消息转发int client_socket=events[i].data.fd;char buffer[BUFFER_SIZE];memset(buffer,0,sizeof(buffer));//清空缓冲区ssize_t bytes_received=recv(client_socket,buffer,sizeof(buffer),0);//接收客户端消息if(bytes_received<=0){std::cerr<<"客户端断开连接"<<std::endl;epoll_ctl(epoll_fd,EPOLL_CTL_DEL,client_socket,NULL);//从epoll中删除该客户端close(client_socket);continue;}std::cout<<"收到的消息:"<<buffer<<std::endl;//转发消息给配对的客户端int target_socket=client_map[client_socket];if(target_socket!=-1){send(target_socket,buffer, strlen(buffer), 0);}else{std::cerr<<"目标客户端未连接"<<std::endl;}}}}close(server_socket);return 0;
}
实验结果:

4. 实现说明
- epoll创建:通过epoll_create创建一个epoll实例,epoll_ctl用于将服务器套接字添加到epoll监听中
- 事件处理:使用epoll_wait等待客户端连接事件和消息事件,一旦有事件发生,处理新连接或消息收发
- 客户端配对:与之前的多线程版本类似,使用client_map维护客户端之间的配对关系
5. 优势
- 高并发支持:epoll适合大量客户端并发连接,比select或多线程处理效率更高
- 事件驱动:基于事件通知的机制,而不是轮询,降低了CPU使用率
- 资源高效:无需为每个连接创建独立线程,减少了上下文切换的开销
相关文章:
C-S模式之实现一对一聊天
天天开心!!! 文章目录 一、如何实现一对一聊天?1. 服务器设计2. 客户端设计3. 服务端代码实现4. 客户端代码实现5. 实现说明6.实验结果 二、改进常见的服务器高并发方案1. 多线程/多进程模型2. I/O多路复用3. 异步I/O(…...
[Deep-ML]Transpose of a Matrix(矩阵的转置)
Transpose of a Matrix(矩阵的转置) 题目链接: Transpose of a Matrix(矩阵的转置)https://www.deep-ml.com/problems/2 题目描述: 难度: easy(简单)。 分类&#…...
Java的Selenium的特殊元素操作与定位之select下拉框
如果页面元素是一个下拉框,我们可以将此web元素封装为Select对象 Select selectnew Select(WebElement element); Select对象常用api select.getOptions();//获取所有选项select.selectBylndex(index);//根据索引选中对应的元素select.selectByValue(value);//选…...
前端精度计算:Decimal.js 基本用法与详解
一、Decimal.js 简介 decimal.js 是一个用于任意精度算术运算的 JavaScript 库,它可以完美解决浮点数计算中的精度丢失问题。 官方API文档:Decimal.js 特性: 任意精度计算:支持大数、小数的高精度运算。 链式调用:…...
智慧节能双突破 强力巨彩谷亚VK系列刷新LED屏使用体验
当前全球节能减排趋势明显,LED节能屏作为显示技术的佼佼者,正逐渐成为市场的新宠。强力巨彩谷亚万境VK系列节能智慧屏凭借三重技术保障、四大智能设计以及大师臻彩画质,在实现节能效果的同时,更在智慧显示领域树立新的标杆。 …...
html 给文本两端加虚线自适应
效果图: <div class"separator">文本 </div>.separator {width: 40%;border-style: dashed;display: flex;align-items: center;color: #e2e2e2;font-size: 14px;line-height: 20px;border-color: #e2e2e2;border-width: 0; }.separator::bef…...
C#:is关键字
目录 is 关键字的核心是什么? 1. 什么是 is 关键字,为什么要用它? 2. 如何使用 is 关键字? 3. is 的作用和场景 4. is 与 as 的区别 5. 模式匹配的扩展(C# 8.0) 6. 常见陷阱和注意事项 总结&#x…...
leetcode4.寻找两个正序数组中的中位数
思路源于 LeetCode004-两个有序数组的中位数-最优算法代码讲解 基本思路是将两个数组看成一个数组,然后划分为两个部分,若为奇数左边部分个数多1,若为偶数左边部分等于右边部分个数。i表示数组1划分位置(i为4是索引4也表示i的左半…...
0101安装matplotlib_numpy_pandas-报错-python
文章目录 1 前言2 报错报错1:ModuleNotFoundError: No module named distutils报错2:ERROR:root:code for hash blake2b was not found.报错3:**ModuleNotFoundError: No module named _tkinter**报错4:UserWarning: Glyph 39044 …...
Qt之QHostInfo
简介 QHostInfo表示主机信息,即主机名称 常用接口 static QHostInfo fromName(const QString &name); QString hostName() const; QList<QHostAddress> addresses() const;结构 #mermaid-svg-HTJ95sEk8JwO4uCy {font-family:"trebuchet ms",…...
OSCP - Proving Grounds- SoSimple
主要知识点 wordpress 插件RCE漏洞sudo -l shell劫持 具体步骤 依旧是nmap 起手,只发现了22和80端口,但80端口只能看到一张图 Nmap scan report for 192.168.214.78 Host is up (0.46s latency). Not shown: 65533 closed tcp ports (reset) PORT …...
JVM虚拟机篇(五):深入理解Java类加载器与类加载机制
深入理解Java类加载器与类加载机制 深入理解Java类加载器与类加载机制一、引言二、类加载器2.1 类加载器的定义2.2 类加载器的分类2.2.1 启动类加载器(Bootstrap ClassLoader)2.2.2 扩展类加载器(Extension ClassLoader)2.2.3 应用…...
Golang的Goroutine(协程)与runtime
目录 Runtime 包概述 Runtime 包常用函数 1. GOMAXPROCS 2. Caller 和 Callers 3. BlockProfile 和 Stack 理解Golang的Goroutine Goroutine的基本概念 特点: Goroutine的创建与启动 示例代码 解释 Goroutine的调度 Gosched的作用 示例代码 输出 解…...
C语言求3到100之间的素数
一、代码展示 二、运行结果 三、感悟思考 注意: 这个题思路他是一个试除法的一个思路 先进入一个for循环 遍历3到100之间的数字 第二个for循环则是 判断他不是素数 那么就直接退出 这里用break 是素数就打印出来 在第一个for循环内 第二个for循环外...
【2025】物联网发展趋势介绍
目录 物联网四层架构感知识别层网络构建层管理服务层——**边缘存储**边缘计算关键技术:综合应用层——信息应用 物联网四层架构 综合应用层:信息应用 利用获取的信息和知识,支持各类应用系统的运转 管理服务层:信息处理 对数据进…...
如何查看 MySQL 的磁盘空间使用情况:从表级到数据库级的分析
在日常数据库管理中,了解每张表和每个数据库占用了多少磁盘空间是非常关键的。这不仅有助于我们监控数据增长,还能为性能优化提供依据。 Google Gemini中国版调用Google Gemini API,中国大陆优化,完全免费!https://ge…...
AI平台初步规划实现和想法
要实现一个类似Coze的工作流搭建引擎,可以结合SmartEngine作为后端工作流引擎,ReactFlow作为前端流程图渲染工具,以及Ant Design作为UI组件库。以下是实现的步骤和关键点: ### 1. 后端工作流引擎(SmartEngine…...
ARXML文件解析-2
目录 1 摘要2 常见ARXML文件注意事项以及常见问题2.1 注意事项2.2 常见问题2.3 答疑 3 ARXML解读/编辑指南3.1 解读ARXML文件的步骤3.2 编辑ARXML文件的方法3.3 验证与调试 4 总结 1 摘要 本文主要对ARXML文件的注意事项、常见问题以及解读与编辑进行详细介绍。 上文回顾&…...
汇编学习之《移位指令》
这章节学习前需要回顾之前的标志寄存器的内容: 汇编学习之《标志寄存器》 算数移位指令 SAL (Shift Arithmetic Left)算数移位指令 : 左移一次,最低位用0补位,最高位放入EFL标志寄存器的CF位(进位标志) OllyDbg查看…...
Nature Communications上交、西湖大学、复旦大学研发面向机器人多模式运动的去电子化刚弹耦合高频自振荡驱动单元
近年来,轻型仿生机器人因其卓越的运动灵活性与环境适应性受到国际机器人领域的广泛关注。然而,现有气动驱动器普遍受限于低模量粘弹性材料的回弹滞后效应与能量耗散特性,加之其"非刚即柔"的二元结构设计范式,难以同时满…...
对备忘录模式的理解
对备忘录模式的理解 一、场景1、题目【[来源](https://kamacoder.com/problempage.php?pid1095)】1.1 题目描述1.2 输入描述1.3 输出描述1.4 输入示例1.5 输出示例 2、理解需求 二、不采用备忘录设计模式1、代码2、问题3、错误的备忘录模式 三、采用备忘录设计模式1、代码1.1 …...
【国产突围!致远电子ZXDoc如何打破Vector垄断,成为新能源车研发“神器”?】
摘要:在汽车“新四化”浪潮下,国产汽车总线工具链软件正迎来高光时刻!广州致远电子推出的ZXDoc以全栈自主化技术硬核国产芯片生态,斩获2024金辑奖“最佳技术实践应用奖”,成为新能源车企研发工程师的“效率倍增器”。本…...
【数据结构】图的基本概念
图的定义 通俗来说一堆顶点被一堆线连在一起,这一坨顶点与线的集合 目录 图的定义 术语 有向图与无向图 简单图与多重图 度、入度与出度 路径与回路 路径长度与距离 子图 连通、连通图与连通分量 强连通、强连通图与强连通分量 完全图 生成树与生成森林 权…...
常见的HR面问题汇总
⚠️注意:以下仅是个人对问题的参考,具体情况视个人情况而定~ 1. 你觉得你有哪些优点和缺点? 优点:学习能力强,遇到问题会主动思考和查找解决方案;有责任心,对待工作认真负责&#…...
【微知】ARM CPU是如何获取某个进程的页表的?(通过TTBR寄存器,MMU进行处理)
ARM CPU 中用于存储访问某个进程的页表的寄存器是 TTBR(Translation Table Base Register)。有TTBR0和TTBR1。TTBR0用户空间的一级页表基址,1是内核页表。cpu访存获取物理地址流程 如果mmu发现tlb里面miss就通过pdbg拿pa物理地址。Intel是CR3…...
从零开始:在Qt中使用OpenGL绘制指南
从零开始:在Qt中使用OpenGL绘制指南 本文只介绍基本的 QOpenGLWidget 和 QOpenGLFunctions 的使用,想要学习 OpenGL 的朋友,建议访问经典 OpenGL 学习网站:LearnOpenGL CN 本篇文章,我们将以绘制一个经典的三角形为例&…...
激光加工中平面倾斜度的矫正
在激光加工中,加工平面的倾斜度矫正至关重要,直接影响加工精度和材料处理效果。以下是系统的矫正方法和步骤: 5. 验证与迭代 二次测量:加工后重新检测平面度,确认残余误差。 反馈优化:根据误差分布修正补偿…...
Android学习总结之应用启动流程(从点击图标到界面显示)
一、用户交互触发:Launcher 到 AMS 的跨进程通信 1. Launcher 处理点击事件(应用层) 当用户点击手机桌面上的应用图标时,Launcher(桌面应用)首先捕获点击事件。每个图标对应一个启动 Intent(通…...
rdiff-backup备份
目录 1. 服务器备份知识点 1.1 备份策略 1.2 备份步骤和宝塔面板简介 1.3 CentOS7重要目录 2. 备份工具 2.1 tar -g 备份演示 2. rsync 备份演示 3. rdiff-backup 备份演示 4. 差异和优缺点 3. rdiff-backup安装和使用 3.1 备份命令rdiff-backup 3.2 恢复命令--…...
PE结构(十五)系统调用与函数地址动态寻找
双机调试 当需要分析一个程序时,这个程序一定是可以调试的,操作系统也不例外。在调试过程中下断点是很重要的 当我们对一个应用程序下断点时,应用程序是挂起的。但当我们对操作系统的内核程序下断点时,被挂起的不是内核程序而是…...
