TCP_SOCKET编程实现

文章目录

  • 与UDP_SOCKET的区别
  • 第一代Tcp_Server
  • Tcp_Client
  • 第二代Tcp_Server
  • 第三代Tcp_server
  • 多线程版本Tcp_Server
  • 线程池版的Tcp_Server
    • 使用inet_ntop来解决线程安全问题
  • 业务逻辑编写
  • 总结
  • 补充说明&&业务代码完成
    • ping的真实作用
    • Translate编写
    • Transform业务代码
  • 整体总结

与UDP_SOCKET的区别

与udp大同小异
多了一些步骤
udp是不可靠的, 不连接的协议
tcp是面向连接的
c/s

谁来建立这个链接?

是client主动建立链接
如手机上的抖音打开, 淘宝打开等
服务器是在一直等待链接的到来, 好比餐厅老板等待客人到来

所以与udp的区别

  1. 设置socket为监听状态
    函数接口:
    在这里插入图片描述
    参数2后续解释

第一代Tcp_Server

tcp初始化操作, 都是固定套路
初始化代码实现:

  void Init(){// 1. 创建socket, file fd, 本质是文件_sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建tcp套接字, 第一个参数不变, 第二个参数是tcp, 面向字节流if (_sockfd < 0){lg.LogMessage(Fatal, "create socket error, errno code: %d, error string: %s\n", errno, strerror(errno));exit(Fatal);}lg.LogMessage(Debug, "create socket success, sockfd: %d\n", _sockfd);// 2. 填充网络信息并绑定struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_addr.s_addr = htonl(INADDR_ANY);//必须是0, 表示任意local.sin_family = AF_INET;//IPv4的网络套接字local.sin_port = htons(_port);local.sin_zero[0] = 0;//填充剩余部分, 必须置零if(bind(_sockfd, CONV(&local), sizeof(local) != 0)){lg.LogMessage(Fatal, "bind socket error, errno code: %d, error string: %s\n", errno, strerror(errno));exit(Bind_Err);}lg.LogMessage(Debug, "bind socket success, sockfd: %d\n", _sockfd);// 3.设置socket为监听状态if(listen(_sockfd, default_backlog) != 0){lg.LogMessage(Fatal, "listen socket error, errno code: %d, error string: %s\n", errno, strerror(errno));exit(Listen_Err);}lg.LogMessage(Debug, "listen socket success, sockfd: %d\n", _sockfd);
  1. tcp_server获取链接
    在这里插入图片描述

后两个参数是输入输出型, 更强调输出,
这个参数相当于udp的recvfrom

在这里插入图片描述

他的返回值成功会返回一个非0的文件描述符
udp里面, sockfd只有一个

  1. 但是在tcp这里, 会新增一个文件描述符
    eg: 门口拉客的 = 旧的sockfd
    店内传菜的 = 新创建的sockfd
    使用旧的sockfd和client进行获取链接
    使用新的sockfd和client进行通信
    这样看来, 旧的sockfd只用于listen
    新的sockfd才是真的sockfd

  2. udp是面向数据报, 而tcp是面向字节流, 这和文件, 管道的特性一模一样
    Start启动完成

 void Start(){_isrunning = true;while(_isrunning){// 4. 获取链接struct sockaddr_in peer;socklen_t len = sizeof(peer);int sockfd = accept(_listensock, CONV(&peer), &len);// 进行获取链接if(sockfd < 0){lg.LogMessage(Warning, "accept socket error, errno code: %d, error string: %s\n", errno, strerror(errno));continue;// 获取失败则继续获取}lg.LogMessage(Debug, "accept socket success, get a new sockfd: %d\n", sockfd);// 5. 提供服务Service(sockfd);close(sockfd);} }// 这个sockfd表示连接是全双工的void Service(int sockfd){char buffer[1024];while(true){ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);if(n > 0){buffer[n] = 0;std::cout << "client say# " << buffer << std::endl;std::string echo_string = "server echo# ";echo_string += buffer;write(sockfd, echo_string.c_str(), echo_string.size());}else if(n == 0)// read如果为0, 文件中表示读到了结尾, 这边表示对端关闭了连接(与管道的情况一模一样){lg.LogMessage(Info, "client quit...\n");break;}else{lg.LogMessage(Error, "read error, errno code: %d, error string: %s\n", errno, strerror(errno));break;}}}
  1. 查看结果
    在这里插入图片描述
    tcp_client编写
    与udp的差别, tcp在创建了sockfd之后, 只需要建立连接

Tcp_Client

与udp的差别, tcp在创建了sockfd之后, 只需要建立连接

  1. connect连接
    在这里插入图片描述

  2. 文件流进行消息读写

 // 2. client必须要ip和port, 但是不需要显示绑定, client 系统随机端口// tcp发起链接的时候, 被OS自动进行本地绑定// 建立连接connectstruct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);// server.sin_addr.s_addr = inet_addr(serverip.c_str());// 换一个接口 ipv4转二进制 p当成process n是网络 -- 不太准确, 但是好记inet_pton(AF_INET, serverip.c_str(), &server.sin_addr); // 1. IPV4--> 四字节ip 2. 进程转网络序列int n = connect(sockfd, CONV(&server), sizeof(server)); // 这里自动进行bindif (n < 0){cerr << "connect error" << endl;return 2;}// 3. 发送数据// 与服务器进行持续通信的循环while (true){// 用户输入的消息string message;// 提示用户输入信息cout << "Please Enter# ";// 读取用户输入的整行文本getline(cin, message);// 尝试向服务器发送消息ssize_t n = write(sockfd, message.c_str(), message.size());// 检查消息是否成功发送if (n > 0){// 用于接收服务器响应的缓冲区char buffer[1024];// 从服务器读取响应, sockfd读写都用, 更加说明是全双工ssize_t m = read(sockfd, buffer, sizeof(buffer) - 1);// 检查是否接收到响应if (m > 0){// 确保响应字符串以空字符结尾buffer[m] = 0;// 打印服务器的响应std::cout << "get a echo message# " << buffer << std::endl;}// 检查服务器是否关闭连接else if (m == 0){// 服务器关闭时打印信息并退出循环std::cout << "server close!" << std::endl;break;}}// 发送消息时发生错误else{// 打印错误信息并退出循环cerr << "write error" << endl;break;}}close(sockfd);

进行测试:

在这里插入图片描述

结果说明
1. 运行后查看netstat -ntp 有两个服务, 不是一个吗?
原因是, 现在client和server在一台机器上, 所有有两个, 将来如果是在一台机器上, 那就只有一个
在这里插入图片描述2. 这样的新创建的client因为是单进程, 所以新建的client 会阻塞, 发的消息也会阻塞, 当旧的client关闭, 新的client会瞬间显示阻塞的信息, 这个阻塞的极限是多少, 由我们服务器的缓冲区决定
在这里插入图片描述

可以看到, 消息被输出后, 一部分成功输出, 另一部分write失败, 导致溢出到shell中输出
3. 服务器断线重连实现:

a. 将上述的工作放到visitServer函数中
然后, 进行断线重连的操作

int count = 1;while(count <= ReTry_count){bool result = visitServer(serverip, serverport);if(result) break;else{// 重连操作sleep(1);cout << "reconnecting..., count: " << count++ << endl;}}if(count > ReTry_count) {cout << "reconnect failed" << endl;}

b. 有时client端会在创建后, server退出, 此时client重连, 让他实现从1次重连开始
在这里插入图片描述

像这样,就需要修改代码
只需让他多加一个参数, 在这个函数内返回false时, 让count = 1即可, 这个意义不太大, 有了解即可, 因为现实情况是远比当前复杂, 有可能是客户端断线…等等
在这里插入图片描述

  1. tcp_server启动有时候会出现绑定失败的情况
    在这里插入图片描述

原理先不解释, 先说解决方式
setsockopt
在这里插入图片描述

写在server创建listensockfd那里
在这里插入图片描述

第二代Tcp_Server

多进程版本的相互通信
对于一个父进程, 他有他自己的文件描述符表, 且表中的每个信息指向它对应的struct file, 创建子进程之后, 会有多个表中的信息指向同一个struct file, 所以可以像管道那样, 对于父子进程关闭不需要的文件fd
在这里插入图片描述

第三代Tcp_server

信号版本的Tcp_server
在linux 中, 如果对SIGCHLD设置为SIG_IGN, 则会自动回收子进程, 不会wait阻塞住
所以开局设置signal
在这里插入图片描述

然后创建子进程方面默认执行
在这里插入图片描述

因为设置了信号机制, 所以也不用wait

子进程的退出会自动OS回收, 如果不设置信号, 单纯使用上述的代码, 会引起僵尸进程问题

每次创建进程都会消耗时间, 推荐使用进程池来使用, 这边就不进行讲述
问题:
先创建的子进程, 再进行的链接, 进程看不到sockfd,怎么办?
在父子进程之间传递文件描述符的实现, 一个比较老的功能, 可以通过这个解决问题

多线程版本Tcp_Server

相比多进程, 更加的方便, 也不需要文件描述符的传递, 也不需要关闭所谓的fd, 主子线程共享文件描述符表
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

完成之后, 为什么有报错?

在这里插入图片描述

主要是因为类型不匹配导致的
可以考虑使用std::bind来适配成员函数的调用格式,但是更推荐重构成员函数为静态函数或全局函数
传参
在这里插入图片描述

定义实现
在这里插入图片描述

封装
在这里插入图片描述

结果演示

在这里插入图片描述
每加一个线程, 文件描述符表也会增加
链接来了, 才创建线程, 还是慢, 所以使用线程池, 且加上client的信息, 通过accept函数获取, 后两个参数也有输出功能

线程池版的Tcp_Server

先加client信息的处理
使用InetAddr.hpp获取属性
相关代码改动

在这里插入图片描述

在这里插入图片描述
完成之后, 进行验证
在这里插入图片描述

相关问题
在这里插入图片描述

使用inet_ntop来解决线程安全问题

这个转IP的函数返回值是一个char*, 未来的操作可能会有线程安全问题

在这里插入图片描述
他会在函数内部申请一个静态的内存地址, 然后返回, 所以他是一个可重入函数, 存在线程安全问题
应当减少使用, 推荐使用inet_ntop, 这个函数允许我们自己维护一个缓冲区, 而不是函数内部自己去申请
可以有效避免线程安全问题
在这里插入图片描述
在这里插入图片描述
多线程修改:
防止命名冲突, 将threadpool放在ThreadNs里面, 然后再TcpServer的构造函数里面
在这里插入图片描述

下面进行修改代码, 让server能被所有线程就进行访问
往后的任务都是线程池中的线程来接受任务, 进行处理, 而不是创建新的线程
下面实现代码让线程来执行任务

这个任务可以使用Task类来进行处理, 但是也可以使用function来进行处理
这样的方式比较简单
在这里插入图片描述
下面用这个包装器来完成任务
未来获取新的文件文件描述符信息等后, 可以直接进行如下构建
在这里插入图片描述再这样修改完成之后, 因为线程池中的线程数目是固定的, 所以我们的client链接数目有限, 当超过限制的client数目时, 就会出现, 连不上的情况. 同时, 为别人提供的服务是死循环式对我们的server端的压力是有点大的, 所以在一般我们很少去这样写代码

现在对他进行调整, 让它变得实际有用一点, 而不是像现在这样进行通信
也就是说, 不能让我们的服务像是死循环这样一直执行, 而是实现一个业务逻辑
这边的策略是实现一个英译汉的功能,要利用I/O, 同时引入unordered_map
定义回调函数(后期可能进行修改)
在这里插入图片描述
现在只需要Tcp_server进行IO就可以了
然后构建业务逻辑

业务逻辑编写

在这里插入图片描述
在这里存放所有的任务
接下来给Tcp_Server一个register功能
在这里插入图片描述
Read方式是进行读取用户第一次输入必须是输入他想要的功能
在 ping Translate Transform之间三选一
read看是读取了哪个结果
在这里插入图片描述进行一个Routine 的路由功能, 实现read到对应的内容给type, 好实现对应的功能, 对应的功能是放在funcs中的
在这里插入图片描述然后, 线程在启动执行的时候, 将对应的sockfd 的功能和Routine进行绑定

在这里插入图片描述
然后, 服务器创建出来之后(这一步在Main.cc文件中), 执行注册服务
在这里插入图片描述
注册之后, 执行对应的服务, 以便调用时执行对应的回调函数

这个register进行添加对应的name和方法到map
然后在进行Start的时候, 将要执行的方法和对应的用户输入名称之间的匹配调用, 此时Ping之类的函数才算是被真正的执行

Ping函数要做的事就是之前在Tcp_server里面, server方法要做的事

在这里插入图片描述然后在修改Tcp_Client

在这里插入图片描述
完成代码进行执行查看, 可以正常执行
server-log
在这里插入图片描述

client-log
在这里插入图片描述
现在补充其他的路由信息,进行基本的完善
在这里插入图片描述
在这里插入图片描述具体功能后续进行补齐

现在ping的作用事是想实现一下一次ping之后的结果, 所以还要进行修改代码
在这里插入图片描述然后因为之前是将Sockfd的关闭放在tcp_server的ThreadData类的析构函数中的, 但是现在已经不需要这个类了, 所以还要进行文件描述符的关闭操作, 这个操作可以放在这个回调函数执行完毕的后面

在这里插入图片描述
下面继续执行代码

在这里插入图片描述
可以看到在一次运行结束之后, 这个client会进行退出, 主要是因为当期的ping服务已经变成了短服务,只需要执行一次, 而不是像之前那样的死循环

现在进行总结一下

总结

未来服务可能部署在云服务器上面, 如何在未来的某一时刻知道这个服务器是否健康的呢?

可以定期(30s)给服务器发送一个最小服务请求, 如果得到回复, 那么这个服务就是正常的

这个机制, 我们称为心跳机制, 我们可以通过client->服务器, 同时也可以反向的得到server->client
而这个编写的Ping, 其实就是对心跳的一个响应机制, 用于检测服务器是否是正常的

补充说明&&业务代码完成

ping的真实作用

在这里插入图片描述Interact是进行交互功能, 它可以进行心跳机制的检测, 读消息是out, 发消息是in, 以此完成交互
在这里插入图片描述
下面进行Translate

Translate编写

首先, 我们到此为止,所有的代码都是网络
在这里插入图片描述
下面是业务代码, 顺便完成Translate
为了方便, 可以将单词加载在resource的dict.txt文件内, 进行文件操作

在这里插入图片描述

然后构建Translate.hpp业务逻辑代码
在这里插入图片描述
定义正常的常变量
在这里插入图片描述
字典路径, 当前字典, 当前字典读取的内容
然后是构造函数和加载字典的方法
在这里插入图片描述然后是内部函数

在这里插入图片描述
现在可以DEBUG一下, 看看有没有读取成功
推荐使用条件编译
在这里插入图片描述
但是这个是.hpp文件, 所以不能这样进行条件编译操作
可以创建一个test.cc来进行debug在这里插入图片描述
在这里插入图片描述
说明我们在Translate的构造函数, 读取字典函数, private变量都没有问题

然后进行分割功能
在这里插入图片描述做完分割, 那么可以对这个Debug进行修改

在这里插入图片描述
在这里插入图片描述
未来的加载词库, 只需要往dict.txt中加就行了. 当然更推荐将这个Translate类改为单例模式, 这边就不做处理

那么如何将这个Translate服务于网络进行结合呢?
那就到了Translate调用
首先定义全局变量(改为单例最好)

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
所以未来服务首先都是进行注册的

Transform业务代码

在这里插入图片描述
在这里插入图片描述
将1按照OP的格式转化为result存到1里面
在这里插入图片描述
代码完成, 对于defaultserver的部分修改就不演示

整体总结

  1. IO类函数, write/read其实他们已经做了转网络序列的处理
  2. udp :数据包 tcp: 面向字节流
    a. 区别:
    1). TCp代码中我们使用read/write目前是有BUG的(下节讲解), TCP要进行正确读写, 必须结合用户协议
    2). udp-----数据和数据是有边界的, sendto只发了一次, 对端对应对端, recvfrom一次----面向数据报
    3). tcp------write->1,10, 100->接收方可能一次收完, 也可能是很多次, 但无论是多少次,都和对方发送无关—面向字节流
    b.udp就好比是一封信件, 只能一封一封去读
    c. tcp是全读, 然后进行手动分割
  3. 网络服务在真正运行时, 必须在Linux中以守护进程(精灵进程)的形式进行运行

要完成上述工作, 应当利用守护进程, 下篇讲解~~~~~

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

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

相关文章

三星SCX-4321NS卡纸、复印打印浅淡、扫描仪锁定故障检修

故障描述: 复印及打印过程中会偶尔卡纸、纸张褶皱(每次纸都卡在进纸传感器摇臂附近,然后纸就顶不上去了),复印或打印出来的文件整体偏淡;扫描仪锁定是在维修过程中突发的。 检修流程描述: 拆机检测 卡纸的问题是进纸器塑料部件断裂需要更换整套进纸器组件;经过更换整套…

Ansible 工具从入门到使用

1. Ansible概述 Ansible是一个基于Python开发的配置管理和应用部署工具&#xff0c;现在也在自动化管理领域大放异彩。它融合了众多老牌运维工具的优点&#xff0c;Pubbet和Saltstack能实现的功能&#xff0c;Ansible基本上都可以实现。 Ansible能批量配置、部署、管理上千台主…

STM32F103C8T6 - 定时器

一、定时器简介 定时器总共分为4部分&#xff0c;8小结。 第一部分&#xff08;定时中断、内外时钟源选择&#xff09;&#xff1a;定时器基本定时计数功能&#xff0c;定一个时间&#xff0c;让定时器每隔一段时间定时中断一次 。 第二部分&#xff08;输出比较&#xff09…

使用正则表达式删除文本的奇数行或者偶数行

用智谱清言和kimi搜出来的结果都没法在notepad生效&#xff0c;后面在overflow上找到的答案比较靠谱。 查找&#xff1a;^[^\n]*\n([^\n]*) 替换&#xff1a;\1 删除偶数行 查找&#xff1a;^([^\n]*)\n[^\n]* 替换&#xff1a;\1 代码解释 ^&#xff1a;这个符号代表字符…

Java面试题——第九篇(JVM)

1. Java中的强引用、软引用、弱引用和虚引用分别是什么 强引用 最常见的引用类型&#xff0c;在Java中&#xff0c;默认情况下&#xff0c;任何普通的对象引用都是强引用只要一个对象有强引用指向他&#xff0c;垃圾回收器永远不会回收该对象&#xff0c;即使系统内存紧张。 …

Linux的图形系统概述 (TODO)

&#xff08;TODO&#xff09; Linux graphics stack 现代 Linux 图形栈由多个子系统和层次组成&#xff0c;从应用程序到硬件之间的各个层面协同工作来处理图形显示和硬件加速。随着时间的推移&#xff0c;Linux 从传统的 **X Window System** 逐步过渡到 **Wayland**&#x…

三 星 SCX-4521F 硒 鼓 清 零 及 一 般 故 障 维 修 浅 谈

基本参数 耗材容量:SCX-4521D3/XIL(3000页) 功 率:平均功率350W、休眠模式10W 一般故障讲解 一、三星SCX-4521F打印机更换硒鼓(或加粉)后仍显示墨粉用尽 (加粉清零、关闭碳粉通知) 按菜单------#1934(快速按完)------屏幕会有TECH字母显示------菜单------向…

LeetCode-2608. 图中的最短环【广度优先搜索 图,腾讯面试真题】

LeetCode-2608. 图中的最短环【广度优先搜索 图&#xff0c;腾讯面试真题】 题目描述&#xff1a;解题思路一&#xff1a;【一图秒懂】枚举起点跑 BFS解题思路二&#xff1a;背诵版解题思路三&#xff1a; 题目描述&#xff1a; 现有一个含 n 个顶点的 双向 图&#xff0c;每个…

二分图的判定-染色法

二分图 如果一张无向图的N个节点可以分成A.B两个不相交的非空集合&#xff0c;并且同一集合内的点之间没有边相连&#xff0c;那么称该无向图为二分图(BipartiteGraph)。 定理&#xff1a;二分图不存在奇环(长度为奇数的环)。 因为每一条边都是从一个集合走到另一个集合&#…

构建宠物咖啡馆:SpringBoot框架的实现策略

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理基于Spring Boot的宠物咖啡馆平台的设计与…

malloc(0)

malloc(0) 在操作系统底层的实现涉及内存分配管理的多个方面。下面是对 malloc(0) 的实现原理的详细解释&#xff1a; 1. 内存分配管理 操作系统通过内存管理子系统来处理内存分配请求&#xff0c;包括 malloc 函数。内存分配通常使用以下几种策略&#xff1a; 堆管理&#…

卫星测绘AI技术-立哥尖端科研

分布式微波干涉测绘卫星是以多颗满足一定编队构形的卫星为平台&#xff0c;以合成孔径雷达 和高精度星间相对状态测量设备等为有效载荷&#xff0c;具备全天时、全天候获取雷达干涉影像数 据&#xff0c;快速测制全球数字表面模型、数字雷达正射影像等测绘产品能力的卫星系统…

论文解析三: D2-Net 用于联合描述和检测局部特征的可训练CNN

目录 1.D2-Net摘要2.D2-Net关键点介绍3. Joint Detection and Description (联合检测和描述)3.1 Feature Extraction3.2 Feature Detection3.2.1 Hard feature detection &#xff08;硬特征检测&#xff09;3.2.1 Soft Feature Detection&#xff08;软特征检测&#xff09; 3…

BUU刷题-Pwn-jarvisoj_typo(ARM符号表恢复技术,Rizzo,FLIRT)

解题所涉知识点&#xff1a; 泄露或修改内存数据&#xff1a; 堆地址&#xff1a;栈地址&#xff1a;libc地址&#xff1a;BSS段地址&#xff1a; 劫持程序执行流程&#xff1a;ARM_ROP 获得shell或flag&#xff1a;调用程序中的system 题目类型&#xff1a; ARM_Pwn arm32 …

Spring Boot 学习之路 -- Thymeleaf 模板引擎

前言 最近因为业务需要&#xff0c;被拉去研究后端的项目&#xff0c;代码框架基于 Spring Boot&#xff0c;后端对我来说完全小白&#xff0c;需要重新学习研究…出于个人习惯&#xff0c;会以 Blog 文章的方式做一些记录&#xff0c;文章内容基本来源于「 Spring Boot 从入门…

Docsify基础配置

一、激活加载动画 轻松修改index.html文件&#xff1a;<div id"app">内容加载中&#xff0c;请稍候...</div>二、设定文档标题与Github链接 <script>window.$docsify {name: 王涵的博客文档,repo: http://baidu.com,} </script>效果展示&…

需求7———通过一个简单的小需求来理清修改后端的思路

我今天下午刚刚完成了睿哥早上说的几个小问题&#xff0c;现在距离下班时间还有两个小时&#xff0c;已经没啥可干的了&#xff0c;然后我发现我之前做的很多需求还没有写文章来总结&#xff0c;所以现在趁着有空&#xff0c;我先写一下总结。这么多需求中&#xff0c;我挑了一…

【leetcode】238.除自身以外数组的乘积

由于该题不能使用除法&#xff0c;所以参考题解写一个左右乘积列表的方法 创建两个新的数组pef,suf 一个用于记录从左到右的乘积&#xff08;类似于动态规划的思想&#xff09;pef 另一个记录从右到左的乘积 bsuf&#xff08;注意suf是从右到左进行累乘&#xff09; 而pef的最左…

【3dgs】3DGS**(3D Geometry Sensing)与 **NeRF**(Neural Radiance Fields)对比

以下是 3DGS&#xff08;3D Geometry Sensing&#xff09;与 NeRF&#xff08;Neural Radiance Fields&#xff09;对比表格&#xff1a; 更加详细的资料&#xff0c;轻参考&#xff1a; NERF/3DGS 对比维度3DGS (3D Geometry Sensing)NeRF (Neural Radiance Fields)基本原理…

Linux之shell详解(Linux Shell Detailed Explanation)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…