frameworks 之Socket
frameworks 之Socket
- Socket
- 服务端
- 1.创建Socket。
- 2.绑定socket
- 3.监听socket
- 4.等待客户端连接
- 5.读取或者写入给客户端
- 客户端
- 1.创建Socket。
- 2.连接服务端Socket
- 3.读取或者写入给客户端
- 4.关闭socket
- 演示代码
- Epoll
- 创建Epoll
- 添加或删除Epoll
- 等待消息返回Epoll
- 演示代码
- SocketPair
- 创建socketPair
- 设置对应的属性
- 演示代码
Socket是应用层与TCP/IP协议族通信的中间软件抽象层。他作为Android 进程间的通信。
涉及到的类如下
- frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
Socket
创建一个socket 需要几个步骤
服务端: socket -> bind -> listen -> accept -> read/write -> close
客户端: socket -> connect -> read/write -> close
服务端
1.创建Socket。
第一个参数为协议域,又称为协议族(family)。常用的协议族有,AF_INET、AF_INET6、AF_LOCAL**(或称AF_UNIX,Unix域socket,这个是我们的讲解重点)**、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址
第二个参数 指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET
第三个参数为 指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。
int socketFd = socket(SOCKET_DOMIAN, SOCKET_TYPE, SOCKET_PROTOCOL);
2.绑定socket
bind()函数就是将给这个描述字绑定一个名字。addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。这个地址结构根据地址创建socket时的地址协议族的不同而不同。addrlen:对应的是地址的长度。
int bindResult = bind(socketFd, (struct sockaddr *)&serviceAddr, serviceLen);
地址为抽象和文件类型。文件类型会生成一个文件在本地,抽象需要在地址前面加0,对应会生成@开头。可以用如下命令查询查看
netstat -an | grep 地址名称
3.监听socket
设置监听的最大连接个数
int listenerResult = listen(socketFd, SOCKET_NUMBER);
4.等待客户端连接
传入客户端的对象 和长度 由内核写入。等待客户端的连接。
int connectFd = accept(socketFd, (struct sockaddr *)&clientAddr, &clintLen);
5.读取或者写入给客户端
通过 read 或者 write 函数跟客户端通信
int dataSize = read(connectFd, &buffer, sizeof(buffer));if (dataSize == 0){printf("客户端关闭连接\n");} else if (dataSize == -1){printf("读取异常\n");exit(1);} else {printf("客户端发送了: %s\n" ,buffer);for (int i = 0; i < sizeof(buffer); i++){buffer[i] = toupper(buffer[i]);}write(connectFd, buffer, sizeof(buffer));}
客户端
1.创建Socket。
int serviceFd = socket(SOCKET_DOMIAN, SOCKET_TYPE, SOCKET_PROTOCOL);
2.连接服务端Socket
发起对服务端的连接,连接成功后 服务端 accept 方法将继续执行
int connectResult = connect(serviceFd, (struct sockaddr *)&clientAddr, clientLen);
3.读取或者写入给客户端
int wirteResult = write(serviceFd, winBuffer, sizeof(winBuffer));printf("写入服务端数据 %i", wirteResult);// 阻塞读取服务端数据int readResult =read(serviceFd, winBuffer, sizeof(winBuffer));
4.关闭socket
close(serviceFd);
演示代码
服务端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// scoket相关
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>// 协议域
int SOCKET_DOMIAN = AF_UNIX;
// 类型
int SOCKET_TYPE = SOCK_STREAM;
// 协议,常用tcp udp
int SOCKET_PROTOCOL = 0;
// SOCKETD地址
#define SOCKET_SERVICE_ADDRESS "natvie_socket_test"
// 允许连接的长度
#define SOCKET_NUMBER 20
// 读取数据大小
#define BUFFER_SZIE 80/*** 主文件入口
*/
int main (void) {// socket 文件描述符int socketFd;// 客户端的socket 文件描述符int clintSocetFd;// socket 实体类struct sockaddr_un serviceAddr, clientAddr;// 地址长度socklen_t serviceLen; socklen_t clintLen;// 数据读取char buffer[BUFFER_SZIE];// 创建socketsocketFd = socket(SOCKET_DOMIAN, SOCKET_TYPE, SOCKET_PROTOCOL);if (socketFd == -1){printf("创建 socket 失败\n");exit(1);}// 清空memset(&serviceAddr, 0 ,sizeof(sockaddr_un));serviceAddr.sun_family = AF_UNIX;// 设置地址strncpy(serviceAddr.sun_path, SOCKET_SERVICE_ADDRESS, sizeof(serviceAddr.sun_path) - 1);// 计算长度serviceLen = sizeof(serviceAddr.sun_family) + sizeof(serviceAddr.sun_path);// 绑定地址int bindResult = bind(socketFd, (struct sockaddr *)&serviceAddr, serviceLen);if (bindResult == -1){printf("绑定 socket 失败\n");exit(1);}// 绑定成功后 需要监听int listenerResult = listen(socketFd, SOCKET_NUMBER);if (listenerResult == -1){printf("监听 socket 失败\n");exit(1);}printf("服务端 socket建立 等待连接\n");// 死循环等待连接while (1){// 等待接收客户端连接, 同时客户端的信息会写入该类, 会阻塞在这里clintLen = sizeof(struct sockaddr_un);int connectFd = accept(socketFd, (struct sockaddr *)&clientAddr, &clintLen);if (connectFd == -1) {printf("监听 socket 失败\n");exit(1);}// 创建成功,循环读取消息while (1){// 0 为关闭 -1 为异常int dataSize = read(connectFd, &buffer, sizeof(buffer));if (dataSize == 0){printf("客户端关闭连接\n");} else if (dataSize == -1){printf("读取异常\n");exit(1);} else {printf("客户端发送了: %s\n" ,buffer);for (int i = 0; i < sizeof(buffer); i++){buffer[i] = toupper(buffer[i]);}write(connectFd, buffer, sizeof(buffer));}}}// 退出for循环 关闭socketprintf("服务端关闭连接\n");close(socketFd);return 0;
}
客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// scoket相关
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>// 协议域
int SOCKET_DOMIAN = AF_UNIX;
// 类型
int SOCKET_TYPE = SOCK_STREAM;
// 协议,常用tcp udp
int SOCKET_PROTOCOL = 0;
// SOCKETD地址
#define SOCKET_SERVICE_ADDRESS "natvie_socket_test"
// 允许连接的长度
#define SOCKET_NUMBER 20
// 读取数据大小
#define BUFFER_SZIE 80
// 输入最大行
#define MAXLINE 80int main(void) {// 客户端操作符int serviceFd;sockaddr_un clientAddr;// 地址长度socklen_t clientLen;// 从输入框输入流char winBuffer[BUFFER_SZIE];serviceFd = socket(SOCKET_DOMIAN, SOCKET_TYPE, SOCKET_PROTOCOL);if (serviceFd == -1){printf("创建 socket 失败");exit(0);}// 清空数据memset(&clientAddr, 0, sizeof(struct sockaddr_un));// 赋值地址clientAddr.sun_family = AF_UNIX;strncpy(clientAddr.sun_path, SOCKET_SERVICE_ADDRESS, sizeof(clientAddr.sun_path) - 1);clientLen = sizeof(clientAddr);printf("socket地址: %s\n", clientAddr.sun_path);int connectResult = connect(serviceFd, (struct sockaddr *)&clientAddr, clientLen);if (connectResult == -1){perror("连接 socket 失败");exit(0);}printf("请输入对应的数据:");while (fgets(winBuffer, MAXLINE, stdin)){// 把读到的数据给服务端int wirteResult = write(serviceFd, winBuffer, sizeof(winBuffer));printf("写入服务端数据 %i", wirteResult);// 阻塞读取服务端数据int readResult =read(serviceFd, winBuffer, sizeof(winBuffer));if (readResult <= 0) { printf("服务端异常或者已关闭\n");break;}else { printf("接收到服务端的消息: %s \n",winBuffer);}printf("请输入对应的数据:");}close(serviceFd);return 0;
}
Epoll
上面提到的socket 的 accept 和 read 等方法都是阻塞方法,等多客户端无法实现实时的读取和监听,如果每个客户端开对应的线程则比较耗资源。因此有了Epoll 等待唤醒。Epoll 有如下方法。
创建Epoll
需要传入一个数量,该数量大于0即可。并会返回一个文件描述符
// 创建 epoll, 参数size 实际没作用,但是要大于0
int epollFd = epoll_create(EPOLL_SZIE);
添加或删除Epoll
第二参数控制 添加还是删除
第三个参数为需要监听的文件操作符
第四个参数为 epoll_event 类型
data.fd 可以存储对应的文件ID
events属性表示数据类型 EPOLLIN
int addResult = epoll_ctl(epollFd, EPOLL_CTL_ADD, serviceFd, &epollEvent);
等待消息返回Epoll
events参数传入事件数组,内核会对应写入的数组。可通过 events.data.fd 获取对应的客户端。
size 表示每次可以处理的最大事件数量
最后参数代表等待的事件,0为马上返回,-1为阻塞等待。
int eventNum = epoll_wait(epollFd, events, EPOLL_EVENT_MAX_SZIE, EPOLL_TIEM_OUT);
演示代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// scoket相关
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
// epoll 相关
#include <sys/epoll.h>// 协议域
int SOCKET_DOMIAN = AF_UNIX;
// 类型
int SOCKET_TYPE = SOCK_STREAM;
// 协议,常用tcp udp
int SOCKET_PROTOCOL = 0;
// SOCKETD地址
#define SOCKET_SERVICE_ADDRESS "epoll_socket_test"
// 允许连接的长度
#define SOCKET_NUMBER 20
// 读取数据大小
#define BUFFER_SZIE 80
// epoll大小
#define EPOLL_SZIE 80
// epoll消息大小
#define EPOLL_EVENT_MAX_SZIE 20
// epoll消息超时
#define EPOLL_TIEM_OUT -1// 采用抽象作用域
int main(void) {int serviceFd;sockaddr_un serviceAddr;socklen_t serviceLen, clientLen;char buffer[BUFFER_SZIE];serviceFd = socket(SOCKET_DOMIAN, SOCKET_TYPE, SOCKET_PROTOCOL);if (serviceFd == -1) {perror("创建 socket 失败");exit(0);}memset(&serviceAddr, 0 , sizeof(struct sockaddr) - 1);serviceAddr.sun_family = AF_UNIX;// 这里采用抽象的协议, 第一位要给0serviceAddr.sun_path[0] = 0;strcpy(serviceAddr.sun_path + 1, SOCKET_SERVICE_ADDRESS);// serviceLen = sizeof(serviceAddr.sun_family) + strlen(SOCKET_SERVICE_ADDRESS) + 1;serviceLen = sizeof(serviceAddr.sun_family) + sizeof(serviceAddr.sun_path);printf("绑定的地址为:%s \n", serviceAddr.sun_path);// 绑定int bindResult = bind(serviceFd, (struct sockaddr *)&serviceAddr, serviceLen);if (bindResult == -1) {perror("绑定 socket 失败");exit(0);}int listenResult = listen(serviceFd, SOCKET_NUMBER);if (listenResult == -1) {perror("监听 socket 失败");exit(0);}// 创建 epoll, 参数size 实际没作用,但是要大于0 int epollFd = epoll_create(EPOLL_SZIE);if (epollFd == -1){perror("创建 epoll 失败");exit(0);}// 将对应的文件夹 添加 epoll中epoll_event epollEvent;/* *** 注意****epoll_data 是一个联合体结构成员,所有的成员公用一段内存,因此实际上只能保留一个成员它只会保存最后一个被赋值的成员,所以不要data.fd,data.ptr都赋值*/epollEvent.data.fd = serviceFd;epollEvent.events = EPOLLIN;// 循环检测 委托内核去处理// 当内核检测到事件到来时会将事件写到这个结构体数组里struct epoll_event events[EPOLL_EVENT_MAX_SZIE];int addResult = epoll_ctl(epollFd, EPOLL_CTL_ADD, serviceFd, &epollEvent); if (addResult == -1){perror("添加到 epoll 失败");exit(0);}printf("开始接收消息\n");while (1){// 等待有没消息返回// tiemOut 为0 马上返回 为-1 代表阻塞// maxevents:表示每次能处理的最大事件数,告之内核这个events有多大;这个maxevents的值不能大于创建epoll_create()时的size;int eventNum = epoll_wait(epollFd, events, EPOLL_EVENT_MAX_SZIE, EPOLL_TIEM_OUT);printf("收到消息数量: %i \n", eventNum);for (int i = 0; i < eventNum; i++){// 有连接请求到来,走到这里if (events[i].data.fd == serviceFd){// 客户端信息填充struct sockaddr_un clientAddr;socklen_t clientLen = sizeof(clientAddr);int connectFd = accept(serviceFd, (struct sockaddr *)&clientAddr, &clientLen);if (connectFd == -1){perror("接收客户端失败");exit(0);}printf("接收到新的客户端\n");//将用于通信的文件描述符挂到epoll树上epollEvent.data.fd = connectFd;epollEvent.events = EPOLLIN;epoll_ctl(epollFd, EPOLL_CTL_ADD, connectFd, &epollEvent);} else{// 其他的为客户端发送的值// 通信也有可能是写事件if (events[i].events & EPOLLOUT){//这里先忽略写事件continue;}char buf[1024]={0};int count = read(events[i].data.fd, buf, sizeof(buf));if (count == 0){// 关闭了客户端要关闭printf("收到客户端关闭");close(events[i].data.fd);epoll_ctl(epollFd, EPOLL_CTL_DEL, events[i].data.fd, NULL);continue;}if (count == -1){perror("接收消息异常退出");exit(0);}printf("收到了消息事件:%s \n", buf);for (int i = 0; i < sizeof(buf); i++){buf[i] = toupper(buf[i]);}write(events[i].data.fd, buf, sizeof(buf));}}}close(serviceFd);return 0;
}
SocketPair
如果是单纯1对1通讯,可通过 socketPair 快速建立服务端和客户端
创建socketPair
创建一个数组存储对应的ID,创建的socket 会放在该数组里面
int socketFds[2];
// 创建socke 第一个作为服务端 第二个作为客户端
int result = socketpair(SOCKET_DOMIAN, SOCKET_TYPE, SOCKET_PROTOCOL, socketFds);
设置对应的属性
以下代码设置对应的缓冲区
socklen_t len = sizeof(BUFFER_SZIE);
setsockopt(socketFds[0], SOL_SOCKET, SO_SNDBUF, &BUFFER_SZIE, len);
setsockopt(socketFds[0], SOL_SOCKET, SO_RCVBUF, &BUFFER_SZIE, len);
setsockopt(socketFds[1], SOL_SOCKET, SO_SNDBUF, &BUFFER_SZIE, len);
setsockopt(socketFds[1], SOL_SOCKET, SO_RCVBUF, &BUFFER_SZIE, len);
演示代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <sys/types.h>
#include <sys/socket.h>#include <unistd.h>// 协议域
int SOCKET_DOMIAN = AF_UNIX;
// 类型
int SOCKET_TYPE = SOCK_STREAM;
// 协议,常用tcp udp
int SOCKET_PROTOCOL = 0;
// 缓存区大小
int BUFFER_SZIE = 1024;int main(void) {int socketFds[2];// 创建socke 第一个作为服务端 第二个作为客户端int result = socketpair(SOCKET_DOMIAN, SOCKET_TYPE, SOCKET_PROTOCOL, socketFds);if (result == -1){perror("创建 socketPair 错误");exit(0);}/*** 第一个参数socket是套接字描述符。* 第二个参数level是被设置的选项的级别,如果想要在套接字级别上设置选项,就必须把level设置为 SOL_SOCKET。* option_name指定准备设置的选项,option_name可以有哪些取值,这取决于level,在套接字级别上(SOL_SOCKET)* 主要设置缓存区大小*/socklen_t len = sizeof(BUFFER_SZIE);setsockopt(socketFds[0], SOL_SOCKET, SO_SNDBUF, &BUFFER_SZIE, len);setsockopt(socketFds[0], SOL_SOCKET, SO_RCVBUF, &BUFFER_SZIE, len);setsockopt(socketFds[1], SOL_SOCKET, SO_SNDBUF, &BUFFER_SZIE, len);setsockopt(socketFds[1], SOL_SOCKET, SO_RCVBUF, &BUFFER_SZIE, len);int pid = fork();printf("孵化出来的进程号为 %i \n", pid);// 在子进程为0if (!pid){printf("执行子进程\n");// 为子进程close(socketFds[0]);int count = 0;while (1){// 休眠一秒sleep(1);// 发送给服务端端write(socketFds[1], &count, sizeof(count));int size = read(socketFds[1], &count, sizeof(count));printf("收到服务端数据 %i \n", count);++count;}} else {printf("执行父进程\n");// 为父进程close(socketFds[1]);int count = 0;while (1){ // 读取客户端数据int size = read(socketFds[0], &count, sizeof(count));if (read <= 0){perror("客户端异常退出 \n");exit(0);}printf("收到客户端数据 %i \n", count);++count;// 发送给客户端write(socketFds[0], &count, sizeof(count));}}
}
相关文章:
frameworks 之Socket
frameworks 之Socket Socket服务端1.创建Socket。2.绑定socket3.监听socket4.等待客户端连接5.读取或者写入给客户端 客户端1.创建Socket。2.连接服务端Socket3.读取或者写入给客户端4.关闭socket 演示代码 Epoll创建Epoll添加或删除Epoll等待消息返回Epoll演示代码 SocketPair…...
WEB前端开发中如何实现大文件上传?
大文件上传是个非常普遍的场景,在面试中也会经常被问到,大文件上传的实现思路和流程。在日常开发中,无论是云存储、视频分享平台还是企业级应用,大文件上传都是用户与服务器之间交互的重要环节。随着现代网络应用的日益复杂化&…...
ts给vue中props设置指定类型
interface IBaseObject {[key: string | number]: any; }export default defineComponent({name:xx,props:{data:{type:Object as PropType<IBaseObject>,default:()>({}),required:true},}, })...
模拟实现c++中的list模版
☺☺☺☺☺☺☺☺☺☺ 点击 进入杀马特的主页☺☺☺☺☺☺☺☺☺☺ 目录 一list简述: 二库内常用接口函数使用: 1reverse(): 2.s…...
从信息论的角度看微博推荐算法
引言 在数字时代,推荐系统已成为社交媒体和其他在线服务平台的核心组成部分。它们通过分析用户行为和偏好,为用户提供个性化的内容,从而提高用户满意度和平台的参与度。推荐系统不仅能够增强用户体验,还能显著提升广告投放的效率…...
CISC(复杂指令集)与RISC(精简指令集)的区别
RISC(Reduced Instruction Set Computer)和CISC(complex instruction set computer)是当前CPU的两种架构。 它们的区别在于不同的CPU设计理念和方法。 早期的CPU全部是CISC架构,它的设计目的是要用最少的机器语言指令来完成所需的计算任务。比如对于乘法运算&#x…...
自定义数据库连接的艺术:Laravel中配置多数据库连接详解
自定义数据库连接的艺术:Laravel中配置多数据库连接详解 在现代Web应用开发中,经常需要连接到多个数据库。Laravel,作为PHP界最受欢迎的框架之一,提供了强大的数据库抽象层,支持多种数据库系统,并且允许开…...
力扣高频SQL 50题(基础版)第八题
文章目录 力扣高频SQL 50题(基础版)第八题1581. 进店却未进行过交易的顾客题目说明思路分析实现过程准备数据:实现方式:结果截图:总结: 力扣高频SQL 50题(基础版)第八题 1581. 进店…...
【C++20】从0开始自制协程库
文章目录 参考 很多人对协程的理解就是在用户态线程把CPU对线程的调度复制了一遍,减少了线程的数量,也就是说在一个线程内完成对协程的调度,不需要线程切换导致上下文切换的开销。但是线程切换是CPU行为,就算你的程序只有一个线程…...
Docker 深度解析:从入门到精通
引言 在当今的软件开发领域,容器化技术已经成为一种趋势。Docker 作为容器化技术的代表,以其轻量级、可移植性和易用性,被广泛应用于各种场景。本文将从 Docker 的基本概念入手,详细介绍 Docker 的安装、基本操作、网络配置、数据…...
[C++] 模板编程-02 类模板
一 类模板 template <class T或者typename T> class 类名 { .......... } 1.1 两种不同的实现 在以下的两种实现中,其实第一种叫做成员函数模板,并不能称为类模板因为这种实现,我们在调用时,并不需要实例化为Product这个类指定指定特定类型。 // 实现1 clas…...
嵌入式C++、STM32、树莓派4B、OpenCV、TensorFlow/Keras深度学习:基于边缘计算的实时异常行为识别
1. 项目概述 随着物联网和人工智能技术的发展,智能家居安全系统越来越受到人们的关注。本项目旨在设计并实现一套基于边缘计算的智能家居安全系统,利用STM32微控制器和树莓派等边缘设备,实时分析摄像头数据,识别异常行为(如入侵、跌倒等),并及时发出警报,提高家庭安全性。 系…...
C++ //练习 15.30 编写你自己的Basket类,用它计算上一个练习中交易记录的总价格。
C Primer(第5版) 练习 15.30 练习 15.30 编写你自己的Basket类,用它计算上一个练习中交易记录的总价格。 环境:Linux Ubuntu(云服务器) 工具:vim 代码块: /********************…...
3个方法快速找回忘记的PDF文件密码
为确保PDF文件的重要信息不轻易外泄,很多人都会给PDF文件设置打开密码,但伴随着时间的推移,让我们忘记了原本设置的密码,但这时,我们又非常急需要打开编辑这份文件,这时我们该怎么办呢?下面小编…...
排序算法:选择排序,golang实现
目录 前言 选择排序 代码示例 1. 算法包 2. 选择排序代码 3. 模拟排序 4. 运行程序 5. 从大到小排序 循环细节 外层循环 内层循环 总结 选择排序的适用场景 1. 数据规模非常小 2. 稳定性不重要 3. 几乎全部数据已排序 4. 教育目的 前言 在实际场景中…...
【测试】博客系统的测试报告
项目背景 个人博客系统采用了 SSM 框架与 Redis 缓存技术的组合 ,为用户提供了一个功能丰富、性能优越的博客平台。 在技术架构上 ,SSM 框架确保了系统的稳定性和可扩展性。Spring 负责管理项目的各种组件 ,Spring MVC 实现了清晰的请求处理…...
PointCLIP: Point Cloud Understanding by CLIP
Abstract 近年来,基于对比视觉语言预训练(CLIP)的零镜头和少镜头学习在二维视觉识别中表现出了令人鼓舞的效果,该方法在开放词汇设置下学习图像与相应文本的匹配。然而,通过大规模二维图像-文本对预训练的CLIP是否可以推广到三维识别&#x…...
搜索(剪枝)
定义: 剪枝,就是减少搜索树的规模、尽早排除搜索树中不必要分支的一种手段。 在深度优先搜索中,有以下几类常见的剪枝方法: 优化搜索顺序排除等效冗余可行性剪枝最优性剪枝记忆化剪枝 例题1:AcWing 167.木棒 题目:…...
python基础知识点
最近系统温习了一遍python基础语法,把自己不熟知的知识点罗列一遍,便于查阅~~ python教程 Python 基础教程 | 菜鸟教程 1、python标识符 以单下划线开头 _foo 的代表不能直接访问的类属性,需通过类提供的接口进行访问,不能用 f…...
Android SurfaceFlinger——GraphicBuffer获取内存信息(三十一)
上一篇文章介绍了 GraphicBuffer 初始化的 initWithSize() 函数中的申请内存流程,这里我们看一下另一个比较重要的函数,GraphicBufferMapper. getTransportSize 获取内存信息。该函数通常在需要了解缓冲区的实际内存占用情况时调用,例如在调试内存使用情况或优化性能时。 一…...
【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...
uniapp 开发ios, xcode 提交app store connect 和 testflight内测
uniapp 中配置 配置manifest 文档:manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号:4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...
Chrome 浏览器前端与客户端双向通信实战
Chrome 前端(即页面 JS / Web UI)与客户端(C 后端)的交互机制,是 Chromium 架构中非常核心的一环。下面我将按常见场景,从通道、流程、技术栈几个角度做一套完整的分析,特别适合你这种在分析和改…...
CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!
本文介绍了一种名为AnomalyAny的创新框架,该方法利用Stable Diffusion的强大生成能力,仅需单个正常样本和文本描述,即可生成逼真且多样化的异常样本,有效解决了视觉异常检测中异常样本稀缺的难题,为工业质检、医疗影像…...
c# 局部函数 定义、功能与示例
C# 局部函数:定义、功能与示例 1. 定义与功能 局部函数(Local Function)是嵌套在另一个方法内部的私有方法,仅在包含它的方法内可见。 • 作用:封装仅用于当前方法的逻辑,避免污染类作用域,提升…...
Docker拉取MySQL后数据库连接失败的解决方案
在使用Docker部署MySQL时,拉取并启动容器后,有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致,包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因,并提供解决方案。 一、确认MySQL容器的运行状态 …...
