领航Linux UDP:构建高效网络新纪元

文章目录
- 引言
- Udp和Tcp的异同
- 相同点
- 不同点
- 总结
- 1.1、socket
- 1.2、bind
- 1.3、recvfrom
- 1.4、sendto
- 2.1、代码
- 2.1、说明
- 3.1、代码
- 3.2、说明
引言
在前几篇博客中,我们学习了Linux网络编程中的一些概念。从本篇博客开始,我们就正式开始写代码。本篇博客我们将写udp服务器和客户端代码,并实现服务器和客户端通信。这些代码学习成本较高,建议大家多敲几遍。如任何问题,欢迎与我沟通。
Udp和Tcp的异同
UDP协议(User Datagram Protocol,用户数据报协议)和TCP协议(Transmission Control Protocol,传输控制协议)是计算机网络中两种常用的传输层协议,它们在多个方面存在显著的异同。以下是对两者异同点的详细比较:
相同点
- 层次位置:两者都位于OSI模型的第四层——传输层,为上层应用提供数据传输服务。
- 作用:都在网络通信中扮演着重要的角色,用于在网络中的不同设备之间传输数据。
不同点
| UDP协议 | TCP协议 | |
|---|---|---|
| 可靠性 | 不提供可靠性保证,不保证数据包的顺序、完整性和不重复。 | 提供可靠的数据传输,通过序列号、确认机制和重传机制确保数据的完整性和有序性。 |
| 连接性 | 无连接协议,发送数据前不需要建立连接,直接发送数据。 | 面向连接的协议,数据传输前需要建立连接,通过“三次握手”机制确认连接状态。 |
| 传输效率 | 传输效率高,因为不需要建立连接和维持连接状态,开销小。 | 传输效率相对较低,因为需要建立和维护连接,增加了额外的开销。 |
| 实时性 | 实时性较好,适用于对实时性要求较高的应用,如在线游戏、视频通话等。 | 实时性较差,因为需要等待连接建立和确认,以及处理重传等机制。 |
| 数据包大小 | 数据包大小没有限制,但通常受限于网络MTU(最大传输单元)。 | 将数据分割成较小的数据块进行传输,以适应不同的网络环境。 |
| 拥塞控制 | 不使用拥塞控制,网络拥塞时不会降低发送速率。 | 使用拥塞控制机制,根据网络状况调整发送速率,避免网络拥塞。 |
| 应用场景 | 适用于对可靠性要求不高,但对实时性要求较高的场景,如流媒体传输、DNS查询等。 | 适用于对可靠性要求较高的场景,如文件传输、网页浏览等。 |
总结
UDP协议和TCP协议在可靠性、连接性、传输效率、实时性、数据包大小和拥塞控制等方面存在显著的差异。选择哪种协议取决于具体的应用场景和需求。如果对数据传输的可靠性要求较高,应选择TCP协议;如果对实时性要求较高,且可以容忍一定的数据丢失,则可以选择UDP协议。在实际应用中,两种协议经常结合使用,以满足不同的网络需求。
不难发现,Udp代码较简单,写起来相对的简单一些,上手较容易。所以我们写使用Udp协议进行通信。
为了使大家更加容易理解。我们按照创建udp服务端的整个过程的先后顺序来进行讲解。最后写出完整的代码。
1.1、socket
网络通信必须要申请套接字。申请套接字对应的函数为socket。
#include <sys/types.h> /* See NOTES */#include <sys/socket.h>int socket(int domain, int type, int protocol);
参数:
①domain:

domain(协议域/协议族):决定了socket的地址类型。常用的协议族有AF_INET(IPv4)、AF_INET6(IPv6)、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等。在通信中,必须采用与协议族对应的地址。例如,AF_INET决定了要使用IPv4地址(32位)与端口号(16位)的组合。
②type

type(socket类型):指定了socket的类型。常用的socket类型有SOCK_STREAM(流式套接字,用于TCP)、SOCK_DGRAM(数据报套接字,用于UDP)、SOCK_RAW(原始套接字,允许对底层协议如IP或ICMP进行直接访问)等。
③protocol
protocol(协议):通常情况下,可以将其设置为0,让系统自动选择type类型对应的默认协议。
返回值
- 当socket函数成功创建了一个套接字时,它返回一个有效的套接字描述符(socket descriptor)。这个描述符是一个非负整数,用于后续的网络操作,如绑定、监听、连接、发送和接收数据等。
- 如果在创建套接字时发生错误,socket函数返回-1,并设置全局变量errno以指示错误原因。此时,可以调用errno变量或perror()函数来获取具体的错误信息。常见的错误码包括EACCES(权限不足)、EADDRINUSE(地址已经被占用)、EAFNOSUPPORT(地址族不支持)、EINVAL(参数无效)、EMFILE(达到进程允许打开的最大文件数目)、ENFILE(系统打开文件数目过多)、ENOBUFS/ENOMEM(内存不足)、EPROTONOSUPPORT(协议不支持)等。
1.2、bind
bind函数在网络编程中扮演着至关重要的角色,它主要用于将一个本地协议地址(包括IP地址和端口号)赋予一个套接字。以下是关于bind函数的详细解释:
#include <sys/types.h> /* See NOTES */#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
参数
①sockfd:这是由socket()函数返回的文件描述符,代表已经创建的套接字。
②addr:这是一个指向特定协议地址结构的指针,如struct sockaddr_in或struct sockaddr_un,它包含了地址、端口和可能的IP地址信息。
③addrlen:这是地址结构的长度,通常以字节为单位。对于IPv4,通常使用sizeof(struct sockaddr_in);对于IPv6,使用sizeof(struct sockaddr_in6);对于Unix域套接字,使用sizeof(struct sockaddr_un)。
返回值:
- 如果bind函数成功执行,它返回0。
- 如果出现错误,返回-1,并设置全局变量errno以指示错误原因。常见的错误包括EACCES(权限不足)、EADDRINUSE(地址已经被使用)、EADDRNOTAVAIL(地址不可用)、EAFNOSUPPORT(地址族不支持该套接字类型)、EINVAL(套接字未打开)、ENOTSOCK(文件描述符不是套接字)等。
使用场景:
在TCP服务器程序中,bind函数通常用于指定服务器应监听的端口号。服务器在启动时捆绑其众所周知的端口,以便客户端可以连接到它。
对于UDP套接字,bind函数同样用于指定接收数据的端口号。
在Unix域套接字中,bind函数可以用来指定套接字在文件系统中的路径名。
注意事项:
- 在调用bind函数之前,套接字必须处于未连接状态(对于面向连接的套接字如TCP)。
- 如果addr参数中的地址或端口号为0,系统将为套接字自动选择一个可用的地址或端口号。
- 在多线程环境中,应确保对bind函数的调用是线程安全的,避免竞态条件。
- 绑定的本质:将用户态的sockaddr_in设置进内核变为系统态。
- 对于端口号而言,如果用户没有调用bind函数进行显式绑定,那么系统在第一次发送消息时,会随机给套接字绑定一个端口号。
1.3、recvfrom
recvfrom函数是一个在POSIX兼容操作系统(如Linux)中用于接收数据的系统调用。它主要用于从指定的套接字接收数据,并适用于面向无连接的协议,如UDP(用户数据报协议)。
#include <sys/types.h>#include <sys/socket.h>ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
参数
①sockfd:已经创建并绑定的套接字的文件描述符。
②buf:创建好的一块缓冲区的地址。用来承接从网络中读取到的数据。
③len:该块缓冲区的大小。
④flags:读取数据的方式。默认设为0——阻塞式读取。
⑤src_addr:输出型参数,该结构体里面包含着数据发送方的信息,如port、ip等等。如果不需要这些信息,可以设为null。
⑥‘’addrlen:该结构体的大小。
返回值
- 成功时,返回接收到的字符数(字节数)。
- 如果没有可用数据或者连接已经关闭,返回0。
- 如果出现错误,返回-1,并设置errno错误号。此时可以通过perror()函数来打印出错误信息。
注意事项
- 在调用recvfrom函数之前,需要先使用bind函数将socket绑定到一个地址上。
- 如果套接字是非阻塞的,recvfrom函数可能会在没有接收到任何数据时返回-1,并设置errno为EAGAIN或EWOULDBLOCK。
- 如果接收到的数据比缓冲区还大,那么只会取缓冲区大小的数据,并将剩余的数据丢弃。
1.4、sendto
sendto函数是一个系统调用,用于将数据从指定的套接字发送到目标地址。它通常用于UDP(用户数据报协议)通信,因为UDP是无连接的,所以sendto函数允许你向一个特定的地址发送数据报,而不需要事先建立连接。
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
参数
- sockfd:已经创建好的socket文件描述符。
- buf:指向要发送的数据的缓冲区。
- len:要发送的数据长度。
- flags:发送选项标志,可以是0或者像MSG_DONTWAIT这样的选项。MSG_DONTWAIT表示非阻塞发送,如果发送缓冲区满,则不等待直接返回。
- dest_addr:目标地址的sockaddr结构体指针。对于IPv4,这通常是一个指向struct sockaddr_in的指针;对于IPv6,则是一个指向struct sockaddr_in6的指针。
- addrlen:目标地址结构体的长度,例如sizeof(struct sockaddr_in)或sizeof(struct sockaddr_in6)。
返回值:
sendto函数的返回值是一个long类型的整数,表示发送的字节数。具体返回值有以下几种可能:
- 如果返回值大于0,则表示数据已经成功发送到了目标地址。返回值代表实际发送的字节数。
- 如果返回值等于0,表示发送的数据长度为0。这可能是因为buf指向的空间长度为0,或者在使用UDP协议时,sendto函数成功地发送了0字节的数据。
- 如果返回值等于-1,表示发送过程中出现了错误。此时,可以通过检查errno的值来确定具体的错误原因。例如,如果errno为EINTR,表示sendto函数被一个信号中断了;如果errno为EAGAIN或EWOULDBLOCK,表示发送缓冲区已满,无法立即发送数据(这通常发生在使用了MSG_DONTWAIT标志的情况下)。
需要注意的是,sendto函数不保证数据的可靠传输。也就是说,发送的数据可能会丢失,或者接收方可能无法按照发送的顺序接收数据。如果需要可靠的数据传输,应该使用TCP协议而不是UDP。
此外,在使用sendto函数之前,需要确保已经通过socket函数创建了一个套接字,并且(对于面向连接的套接字类型)已经通过connect函数与目标地址建立了连接(尽管对于UDP,连接通常不是必需的,但也可以通过connect建立默认的目标地址)。同时,也需要确保目标地址是有效的,并且发送的数据缓冲区是正确设置的。
2.1、代码
#pragma once#include <iostream>
#include <string>
#include <strings.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>namespace Server
{using namespace std;static const string defaultIp = "0.0.0.0"; //TODOstatic const int gnum = 1024;enum {USAGE_ERR = 1, SOCKET_ERR, BIND_ERR};typedef function<void (string,uint16_t,string)> func_t;class udpServer{public:udpServer(const func_t &cb, const uint16_t &port, const string &ip = defaultIp):_callback(cb), _port(port), _ip(ip), _sockfd(-1){}void initServer(){// 1. 创建socket_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(_sockfd == -1){cerr << "socket error: " << errno << " : " << strerror(errno) << endl;exit(SOCKET_ERR);}cout << "socket success: " << " : " << _sockfd << endl;// 2. 绑定port,ip(TODO)// 未来服务器要明确的port,不能随意改变struct sockaddr_in local; // 定义了一个变量,栈,用户bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port); // 你如果要给别人发消息,你的port和ip要不要发送给对方local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 1. string->uint32_t 2. htonl(); -> inet_addr//local.sin_addr.s_addr = htonl(INADDR_ANY); // 任意地址bind,服务器的真实写法int n = bind(_sockfd, (struct sockaddr*)&local, sizeof(local));if(n == -1){cerr << "bind error: " << errno << " : " << strerror(errno) << endl;exit(BIND_ERR);}// UDP Server 的预备工作完成}void start(){// 服务器的本质其实就是一个死循环char buffer[gnum];for(;;){// 读取数据struct sockaddr_in peer;socklen_t len = sizeof(peer); //必填ssize_t s = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);// 1. 数据是什么 2. 谁发的?if(s > 0){buffer[s] = 0;string clientip = inet_ntoa(peer.sin_addr); //1. 网络序列 2. int->点分十进制IPuint16_t clientport = ntohs(peer.sin_port);string message = buffer;cout << clientip <<"[" << clientport << "]# " << message << endl;// 我们只把数据读上来就完了吗?对数据做处理_callback(clientip, clientport, message);}}}~udpServer(){}private:uint16_t _port;string _ip; // 实际上,一款网络服务器,不建议指明一个IPint _sockfd;func_t _callback; //回调};
}
2.1、说明
- 服务器一旦开始运行,就不会停止。所以服务器本质就是一个死循环。这种一直运行的进程叫做常驻进程。
- 一般来说,服务器不会显式的绑定某一个ip。因为一个主机可能会有不同的ip。但是这台主机内的端口号是唯一的,客户端都是发送信息到特定的端口号上。所以服务器为了可以接收到所有发到这台主机上的信息(不会存在数据丢弃的情况),选择绑定0.0.0.0作为自己的ip。这样就可以接受到任何发送到这台主机指定端口的所有信息。
3.1、代码
#include <iostream>
#include <string>
#include <strings.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>namespace Client
{using namespace std;class udpClient{public:udpClient(const string &serverip, const uint16_t &serverport): _serverip(serverip), _serverport(serverport), _sockfd(-1), _quit(false){}void initClient(){// 创建socket_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd == -1){cerr << "socket error: " << errno << " : " << strerror(errno) << endl;exit(2);}cout << "socket success: "<< " : " << _sockfd << endl;// 2. client要不要bind[必须要的],client要不要显示的bind,需不需程序员自己bind?不需要!!!// 写服务器的是一家公司,写client是无数家公司 -- 由OS自动形成端口进行bind!-- OS在什么时候,如何bind}static void *readMessage(void *args){int sockfd = *(static_cast<int *>(args));pthread_detach(pthread_self());while (true){char buffer[1024];struct sockaddr_in temp;socklen_t temp_len = sizeof(temp);size_t n = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&temp, &temp_len);if (n >= 0)buffer[n] = 0;cout << buffer << endl;}return nullptr;}void run(){pthread_create(&_reader, nullptr, readMessage, (void *)&_sockfd);struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(_serverip.c_str());server.sin_port = htons(_serverport);string message;char cmdline[1024];while (!_quit){//cerr << "# "; // ls -a -l// cin >> message;fprintf(stderr, "Enter# ");fflush(stderr);fgets(cmdline, sizeof(cmdline), stdin);cmdline[strlen(cmdline)-1] = 0;message = cmdline;sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, sizeof(server));}}~udpClient(){}private:int _sockfd;string _serverip;uint16_t _serverport;bool _quit;pthread_t _reader;};
} // namespace Client
3.2、说明
客户端需要绑定端口号吗?客户端需要显式的绑定端口号吗?
端口号是需要绑定端口号的,但是不需要显式的绑定端口号的。绑定端口号的工作交给操作系统自主完成,这个工作由操作系统在客户端初次发送消息时完成。
相对于服务端来说,客户端必须绑定特定的端口号,但是端口号的数值对于客户端来说就显得不太重要。
服务端必须指定特定的端口号以供客户端根据该端口号来向服务端发送消息。但是客户端而言,如果显式指明端口号,必然会出现两个客户端竞争一个端口号的情况。所以在通信时就由操作系统随机分配一个端口号供客户端进行通信。
相关文章:
领航Linux UDP:构建高效网络新纪元
欢迎来到 破晓的历程的 博客 ⛺️不负时光,不负己✈️ 文章目录 引言Udp和Tcp的异同相同点不同点总结 1.1、socket1.2、bind1.3、recvfrom1.4、sendto2.1、代码2.1、说明3.1、代码3.2、说明 引言 在前几篇博客中,我们学习了Linux网络编程中的一些概念。…...
基于MATLAB的均匀面阵MUSIC算法DOA估计仿真
基于MATLAB的均匀面阵MUSIC算法DOA估计仿真 文章目录 前言一、二维MUSIC算法原理二、二维MUSIC算法MATLAB仿真三、MATLAB源代码总结 前言 \;\;\;\;\; 在波达角估计算法中,MUSIC 算法与ESPRIT算法属于特征结构子空间算法,是波达角估计算法中的基石。在前面…...
HTML/CSS中后代选择器
1.作用:选中指定元素中,符合要求的后代元素. 2.语法:选择器1 选择器2 选择器3 ...... 选择器n(使用空格隔开) 3.举例: /* 选中ul中的所有li */ul li{color: red;}/* 选中类名为subject元素中的所有li */.subject li{color: blue;}/* 选中类名为subject元素中的所有类名为f…...
深入解析「卡顿帧堆栈」 | UWA GPM 2.0 技术细节与常见问题
在游戏开发过程中,卡顿问题一直是影响玩家体验的关键因素。UWA GPM 2.0全新推出的「卡顿帧堆栈」功能,专为研发团队提供精准、高效的卡顿分析方案,能够直观呈现游戏运行时的堆栈信息,助力团队迅速找到性能瓶颈。该功能一经上线&am…...
推荐几款较好的开源成熟框架
一. 若依: 1. 官方网站:https://doc.ruoyi.vip/ruoyi/ 2. 若依SpringBootVueElement 的后台管理系统:https://gitee.com/y_project/RuoYi-Vue 3. 若依SpringBootVueElement 的后台管理系统:https://gitee.com/y_project/RuoYi-Cl…...
Mysql全文索引
引言 在MySQL 5.7.6之前,全文索引只支持英文全文索引,不支持中文全文索引,需要利用分词器把中文段落预处理拆分成单词,然后存入数据库。 从MySQL 5.7.6开始,MySQL内置了ngram全文解析器,用来支持中文、日文…...
配置终端代理
普通的魔法开启之后终端下git clone等命令仍然会无法使用,额外需要手动配置终端代理。 sudo vim /etc/apt/apt.conf.d/99proxyAcquire::http::Proxy "http://127.0.0.1:12334"; Acquire::https::Proxy "http://127.0.0.1:12334";在debian安装时…...
51单片机学习之旅——在LCD1602上显示时钟
新建工程 打开软件 LCD1602模块代码添加 因为我们在LCD1602上显示时钟,因此我们需要添加LCD1602的模块代码 跳转到这条博客51单片机学习之旅——模块化编程集_51单片机ruminant-CSDN博客,复制相关代码跳转到这条博客51单片机学习之旅——模块化编程集…...
Jest单元测试
由于格式和图片解析问题,可前往 阅读原文 前端自动化测试在提高代码质量、减少错误、提高团队协作和加速交付流程方面发挥着重要作用。它是现代软件开发中不可或缺的一部分,可以帮助开发团队构建可靠、高质量的应用程序 单元测试(Unit Testi…...
C++字符串处理指南:从基础操作到性能优化——基于std::string的全面解析
博主将从C标准库中的 std::string 出发,详细探讨字符串的处理方法,涵盖常见操作、性能优化和实际应用场景。以下内容将围绕std::string 的使用展开,结合代码示例进行说明。 一、std::string 的基本操作 1.1 创建与初始化 std::string 提供了…...
JVM类加载过程详解:从字节码到内存的蜕变之旅
一、类加载的意义与整体流程 在Java中,每一个.java文件经过编译都会生成.class字节码文件。但字节码本身并不能直接运行,必须通过 类加载(Class Loading)将其转化为JVM内存中的数据结构,才能被程序调用。 类加载过程就…...
【力扣Hot100详解】解锁“字母异位词分组”:用排序魔法一键通关力扣!
字母异位词分组,力扣第49题,看似是“找不同”的排列游戏,实则是哈希表与字符串处理的经典结合。这道题就像是一把钥匙,能帮你打开“如何高效归类数据”的算法大门。今天,我们就用 Java 带你用“排序魔法”轻松破解它&a…...
vite配置scss全局变量
vite配置scss全局变量 创建单独文件variable.scss在其中定义变量 vite.config.ts中配置 import { defineConfig } from vite import vue from vitejs/plugin-vue import path from path// https://vite.dev/config/ export default defineConfig({plugins: [vue()],resolve:…...
Spring Boot01(注解、)---java八股
Spring Boot中常用注解及其底层实现 1、SpringBootApplication注解: SpringBootApplication注解:这个注解标识了一个SpringBoot工程,它实际上是另外三个注解的组合,这三个注解是: aSpringBootConfiguration:…...
2.19学习记录
Web easyupload3.0 这是一道构造.htaccess文件的传马 如下: <FilesMatch "jpg">SetHandler application/x-httpd-php </FilesMatch>.htaccess文件可以作为一个解释器,可以将传进去的图片马改为php马上传之后再传个图片马&#…...
汽车免拆诊断案例 | 2013 款奔驰 S300L 车起步时车身明显抖动
故障现象 一辆2013款奔驰S300L车,搭载272 946发动机,累计行驶里程约为15万km。车主反映,将挡位置于D挡,稍微释放一点制动踏板,车辆蠕动时车身明显抖动,类似气缸失火时的抖动,又类似手动变速器…...
【HeadFirst系列之HeadFirst设计模式】第5天之工厂模式:比萨店的秘密武器,轻松搞定对象创建!
工厂模式:比萨店的秘密武器,轻松搞定对象创建! 大家好,今天我们来聊聊设计模式中的工厂模式。如果你曾经为对象的创建感到头疼,或者觉得代码中到处都是 new 关键字,那么工厂模式就是你的救星!本…...
Redis如何解决热Key问题
目录 **如何解决 Redis 的热 Key(Hot Key)问题?****解决方案** **1. 使用多级缓存****方案** **2. 进行 Key 预分片(Key Sharding)****方案** **3. 使用 Redis 复制机制(主从复制或集群)****方案…...
从开发到部署:EasyRTC嵌入式视频通话SDK如何简化实时音视频通信的集成与应用
嵌入式设备和视频综合管理平台均支持B/S架构。在B/S架构下,传统的视频观看方式依赖于微软的OCX控件,然而OCX控件的使用正面临越来越多的挑战: 首先,用户需要安装浏览器插件、调整浏览器安全级别,并允许ActiveX控件弹出…...
Zookeeper(58)如何在Zookeeper中实现分布式锁?
在 Zookeeper 中实现分布式锁是一种常见的用例。Zookeeper 提供了强一致性、高可用性的分布式协调服务,使得它非常适合用来实现分布式锁。以下是详细的步骤和代码示例,展示如何在 Zookeeper 中实现分布式锁。 1. Zookeeper 分布式锁的基本原理 Zookeep…...
iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...
【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...
Vue 模板语句的数据来源
🧩 Vue 模板语句的数据来源:全方位解析 Vue 模板(<template> 部分)中的表达式、指令绑定(如 v-bind, v-on)和插值({{ }})都在一个特定的作用域内求值。这个作用域由当前 组件…...
Modbus RTU与Modbus TCP详解指南
目录 1. Modbus协议基础 1.1 什么是Modbus? 1.2 Modbus协议历史 1.3 Modbus协议族 1.4 Modbus通信模型 🎭 主从架构 🔄 请求响应模式 2. Modbus RTU详解 2.1 RTU是什么? 2.2 RTU物理层 🔌 连接方式 ⚡ 通信参数 2.3 RTU数据帧格式 📦 帧结构详解 🔍…...
