当前位置: 首页 > news >正文

Linux UDP 编程详解

一、引言

在网络编程领域,UDP(User Datagram Protocol,用户数据报协议)作为一种轻量级的传输层协议,具有独特的优势和适用场景。与 TCP(Transmission Control Protocol,传输控制协议)相比,UDP 不提供可靠的连接保证、数据顺序传递以及流量控制等功能,但它具有更低的开销和更高的传输效率,适用于对实时性要求较高、能容忍少量数据丢失的应用场景,如视频流传输、音频广播、在线游戏等。在 Linux 系统中,提供了丰富的函数库和工具来支持 UDP 编程,使得开发者能够轻松构建基于 UDP 的网络应用。本文将深入探讨 Linux 环境下的 UDP 编程,从基本概念、原理到详细的代码示例,帮助读者全面掌握 UDP 编程技术。

二、UDP 协议基础

2.1 UDP 协议特点

  1. 无连接性:UDP 在发送数据之前不需要像 TCP 那样建立连接,发送方可以直接将数据报发送给目标地址,接收方随时准备接收数据。这种特性使得 UDP 的传输过程更加简单、快捷,减少了建立连接所需的时间和资源开销。
  2. 不可靠性:UDP 不保证数据报一定能够正确、完整地到达接收方,也不保证数据报的顺序。在网络传输过程中,数据报可能会因为网络拥塞、链路故障等原因丢失或乱序。应用程序需要根据自身的需求来处理这些可能出现的问题,例如通过校验和、重传机制等方式来确保数据的完整性和正确性。
  3. 面向数据报:UDP 以数据报为单位进行数据传输,每个数据报都是独立的,包含了目标地址、源地址和数据等信息。发送方每次调用发送函数(如sendto)发送的数据都会被封装成一个独立的数据报,接收方通过接收函数(如recvfrom)接收一个个独立的数据报。
  4. 头部开销小:UDP 的头部固定为 8 字节,相比 TCP 的 20 字节(不包含选项)头部开销更小,这使得 UDP 在传输大量小数据时具有更高的效率。UDP 头部包含源端口号、目的端口号、长度和校验和字段。

2.2 UDP 应用场景

  1. 实时多媒体传输:如视频会议、在线直播、网络电话等应用对实时性要求极高,少量的数据丢失可能只会导致短暂的画面卡顿或声音不清晰,但不会对整体的用户体验造成严重影响。使用 UDP 可以避免 TCP 的重传机制带来的延迟,保证媒体流的流畅传输。
  2. 网络监控与管理:在网络监控系统中,需要实时收集网络设备的状态信息、流量数据等。由于监控数据通常量较大且对实时性要求较高,使用 UDP 可以快速地将数据发送到监控中心,即使部分数据丢失也不会影响对网络整体状态的判断。
  3. 在线游戏:游戏中的实时状态更新、玩家操作指令等数据需要及时传输给服务器和其他玩家。UDP 的低延迟特性使得游戏能够更及时地响应用户操作,提供流畅的游戏体验。例如,在多人在线射击游戏中,玩家的移动、射击等操作需要快速传输到服务器,UDP 能够满足这种实时性需求。

三、Linux UDP 编程基础函数

3.1 socket 函数

socket函数用于创建一个套接字描述符,它是进行网络通信的基础。其函数原型如下:

#include <sys/socket.h>
int socket(int domain, int type, int protocol);
  • domain参数指定协议族,对于 IPv4 网络,通常使用AF_INET;对于 IPv6 网络,使用AF_INET6
  • type参数指定套接字类型,UDP 编程使用SOCK_DGRAM,表示数据报套接字。
  • protocol参数通常设置为 0,让系统根据domaintype选择默认的协议。对于 UDP,默认协议为 UDP 协议。

函数成功时返回一个非负整数的套接字描述符,失败时返回 -1,并设置errno错误码以指示错误原因。例如

int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1) {perror("socket creation failed");exit(EXIT_FAILURE);
}

3.2 bind 函数

bind函数用于将套接字绑定到一个特定的地址和端口上。其函数原型如下:

#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • sockfd是通过socket函数创建的套接字描述符。
  • addr是一个指向struct sockaddr结构体的指针,该结构体包含了要绑定的地址信息。在 IPv4 中,通常使用struct sockaddr_in结构体来填充地址信息,然后将其强制转换为struct sockaddr类型。
  • addrlen参数指定addr结构体的长度。

对于 IPv4,struct sockaddr_in结构体的定义如下:

struct sockaddr_in {sa_family_t sin_family; /* 地址族,AF_INET */in_port_t sin_port;     /* 端口号 */struct in_addr sin_addr; /* 32位IPv4地址 */char sin_zero[8];       /* 填充字节,使其与struct sockaddr大小相同 */
};
struct in_addr {in_addr_t s_addr; /* 32位IPv4地址 */
};

在使用bind函数时,需要正确填充struct sockaddr_in结构体的各个字段。例如:

struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(8080); // 绑定到8080端口,htons用于将主机字节序转换为网络字节序
servaddr.sin_addr.s_addr = INADDR_ANY; // 绑定到所有可用的网络接口if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {perror("bind failed");close(sockfd);exit(EXIT_FAILURE);
}

3.3 sendto 函数

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是要发送数据的套接字描述符。
  • buf是指向要发送数据缓冲区的指针。
  • len是要发送数据的长度,以字节为单位。
  • flags参数通常设置为 0,用于指定一些额外的发送选项,如 MSG_DONTROUTE 表示不查找路由表。
  • dest_addr是一个指向目标地址结构体的指针,指定数据报的接收方地址。
  • addrlen参数指定目标地址结构体的长度。

函数成功时返回实际发送的字节数,失败时返回 -1,并设置errno错误码。例如:

char buffer[] = "Hello, UDP!";
struct sockaddr_in cliaddr;
memset(&cliaddr, 0, sizeof(cliaddr));
cliaddr.sin_family = AF_INET;
cliaddr.sin_port = htons(9090);
inet_pton(AF_INET, "192.168.1.100", &cliaddr.sin_addr); // 设置目标IP地址ssize_t n = sendto(sockfd, buffer, sizeof(buffer) - 1, 0,(struct sockaddr *)&cliaddr, sizeof(cliaddr));
if (n == -1) {perror("sendto failed");
}

3.4 recvfrom 函数

recvfrom函数用于从套接字接收数据报,并获取发送方的地址信息。其函数原型如下:

#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,用于指定一些额外的接收选项,如 MSG_PEEK 表示只是查看数据而不真正从接收队列中移除。
  • src_addr是一个指向结构体的指针,用于存储发送方的地址信息。
  • addrlen是一个指向size_t类型变量的指针,用于指定src_addr结构体的长度,函数返回时会更新该变量为实际接收到的地址长度。

函数成功时返回实际接收到的字节数,失败时返回 -1,并设置errno错误码。例如:

char buffer[1024];
struct sockaddr_in cliaddr;
socklen_t len = sizeof(cliaddr);
ssize_t n = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0,(struct sockaddr *)&cliaddr, &len);
if (n == -1) {perror("recvfrom failed");
} else {buffer[n] = '\0';printf("Received: %s\n", buffer);
}

四、Linux UDP 编程示例

4.1 UDP 服务器示例

下面是一个简单的 UDP 服务器示例代码,该服务器绑定到指定的端口,接收客户端发送的数据,并将接收到的数据回显给客户端。

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>#define BUFFER_SIZE 1024void usage(const char *prog_name) {printf("Usage: %s <listen_ip> <listen_port>\n", prog_name);printf("Example: %s 127.0.0.1 8080\n", prog_name);exit(EXIT_FAILURE);
}int main(int argc, char *argv[]) {int sockfd;struct sockaddr_in servaddr, cliaddr;socklen_t len;char buffer[BUFFER_SIZE];ssize_t n;// 检查命令行参数数量是否正确if (argc!= 3) {usage(argv[0]);}// 创建 UDP 套接字sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd == -1) {perror("socket creation failed");exit(EXIT_FAILURE);}// 初始化服务器地址结构体memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(atoi(argv[2]));if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) {perror("Invalid IP address");close(sockfd);exit(EXIT_FAILURE);}// 绑定套接字到服务器地址if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {perror("bind failed");close(sockfd);exit(EXIT_FAILURE);}printf("UDP server is listening on %s:%d...\n", argv[1], atoi(argv[2]));while (1) {// 接收客户端数据len = sizeof(cliaddr);n = recvfrom(sockfd, buffer, BUFFER_SIZE - 1, 0,(struct sockaddr *)&cliaddr, &len);if (n == -1) {perror("recvfrom failed");continue;}buffer[n] = '\0';printf("Received from client (%s:%d): %s\n",inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port), buffer);// 将接收到的数据回显给客户端n = sendto(sockfd, buffer, strlen(buffer), 0,(struct sockaddr *)&cliaddr, len);if (n == -1) {perror("sendto failed");}}close(sockfd);return 0;
}

在这个示例中,服务器首先创建一个 UDP 套接字,然后将其绑定到指定的端口。通过一个无限循环,服务器不断调用recvfrom函数接收客户端发送的数据,并在接收到数据后调用sendto函数将数据回显给客户端。

4.2 UDP 客户端示例

以下是与上述服务器对应的 UDP 客户端示例代码,客户端向服务器发送数据,并接收服务器回显的数据。

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>#define BUFFER_SIZE 1024void usage(const char *prog_name) {printf("Usage: %s <server_ip> <port> <message>\n", prog_name);printf("Example: %s 127.0.0.1 8080 \"Hello, server!\"\n", prog_name);exit(EXIT_FAILURE);
}int main(int argc, char *argv[]) {int sockfd;struct sockaddr_in servaddr;char buffer[BUFFER_SIZE];ssize_t n;socklen_t len;// 检查命令行参数数量是否正确if (argc!= 4) {usage(argv[0]);}// 创建 UDP 套接字sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd == -1) {perror("socket creation failed");exit(EXIT_FAILURE);}// 初始化服务器地址结构体memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(atoi(argv[2]));if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) {perror("Invalid IP address");close(sockfd);exit(EXIT_FAILURE);}// 向服务器发送数据n = sendto(sockfd, argv[3], strlen(argv[3]), 0,(struct sockaddr *)&servaddr, sizeof(servaddr));if (n == -1) {perror("sendto failed");close(sockfd);exit(EXIT_FAILURE);}printf("Sent %ld bytes to server: %s\n", n, argv[3]);// 接收服务器回显的数据len = sizeof(servaddr);n = recvfrom(sockfd, buffer, BUFFER_SIZE - 1, 0,(struct sockaddr *)&servaddr, &len);if (n == -1) {perror("recvfrom failed");close(sockfd);exit(EXIT_FAILURE);}buffer[n] = '\0';printf("Received from server: %s\n", buffer);close(sockfd);return 0;
}

客户端同样先创建一个 UDP 套接字,然后初始化服务器的地址信息。通过sendto函数向服务器发送数据,接着使用recvfrom函数接收服务器回显的数据。

代码解释

  • 服务器端代码
    • socket(AF_INET, SOCK_DGRAM, 0):创建一个 UDP 套接字。AF_INET 表示使用 IPv4 地址族,SOCK_DGRAM 表示使用数据报套接字类型,0 表示使用默认协议(对于 AF_INETSOCK_DGRAM 组合,即为 UDP 协议)。
    • memset(&servaddr, 0, sizeof(servaddr)):将 servaddr 结构体的内存清零,确保其成员变量初始化为零。
    • servaddr.sin_family = AF_INET:设置地址族为 IPv4。
    • servaddr.sin_port = htons(PORT):将端口号转换为网络字节序并存储在 sin_port 中。
    • servaddr.sin_addr.s_addr = INADDR_ANY:将套接字绑定到所有可用的网络接口。
    • bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)):将套接字绑定到本地地址和端口,使得服务器可以接收发送到该端口的数据。
    • recvfrom(sockfd, buffer, BUFFER_SIZE - 1, 0, (struct sockaddr *)&cliaddr, &len):从客户端接收数据,存储在 buffer 中,并将发送方的地址存储在 cliaddr 中。
    • sendto(sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&cliaddr, len):将接收到的数据发送回客户端。
  • 客户端代码解释
    • socket(AF_INET, SOCK_DGRAM, 0):创建一个 UDP 套接字,与服务器端相同。
    • memset(&servaddr, 0, sizeof(servaddr)):将 servaddr 结构体的内存清零。
    • servaddr.sin_family = AF_INET:设置地址族为 IPv4。
    • servaddr.sin_port = htons(PORT):将端口号转换为网络字节序。
    • inet_pton(AF_INET, SERVER_IP, &servaddr.sin_addr):将服务器的 IP 地址字符串转换为二进制格式存储在 sin_addr 中。
    • sendto(sockfd, msg, strlen(msg), 0, (struct sockaddr *)&servaddr, sizeof(servaddr)):向服务器发送数据。
    • recvfrom(sockfd, buffer, BUFFER_SIZE - 1, 0, (struct sockaddr *)&servaddr, &len):接收服务器回显的数据。

五、编译指令和测试过程

5.1 编译

gcc udp_server.c -o udp_server
gcc udp_client.c -o udp_client

5.2 测试过程

启动服务端 监听端口

./udp_server 127.0.0.1 8080 

客户端发送数据

./udp_client 127.0.0.1 8080 helloworld123

在这里插入图片描述

六、UDP 编程中的常见问题与解决方案

6.1 数据丢失问题

由于 UDP 的不可靠性,数据在传输过程中可能会丢失。为了解决这个问题,可以在应用层实现数据校验和重传机制。例如,发送方在每个数据报中添加一个校验和字段,接收方在接收到数据报后计算校验和并与发送方发送的校验和进行比较,如果不一致则请求发送方重传该数据报。另外,可以设置一个重传定时器,当发送方在一定时间内未收到接收方的确认消息时,自动重传数据报。

6.2 数据报大小限制

UDP 数据报的大小受到网络 MTU(Maximum Transmission Unit,最大传输单元)的限制。在 IPv4 网络中,MTU 通常为 1500 字节(不包括链路层头部),UDP 数据报的总长度(包括头部)不能超过 MTU。如果需要发送的数据超过 MTU 大小,需要将数据进行拆分,分成多个较小的数据报进行发送。接收方在接收到多个数据报后,需要按照正确的顺序进行重组。

6.3 网络拥塞问题

虽然 UDP 没有像 TCP 那样的拥塞控制机制,但在网络拥塞严重的情况下,大量的数据报可能会被丢弃。为了减轻网络拥塞对 UDP 应用的影响,可以在应用层实现一些简单的拥塞控制策略,如根据网络状况动态调整发送数据的速率。例如,当发现丢包率增加时,适当降低发送速率;当网络状况良好时,逐渐提高发送速率。

6.4 可能遇到的问题及解决方法

  • 权限问题:如果在运行时遇到权限问题,可能是因为使用了低端口号(小于 1024),可以使用 sudo 命令来运行可执行文件,或者将端口号修改为大于 1024 的端口。
  • 地址冲突:如果服务器绑定的端口已被其他程序占用,会导致绑定失败,可以修改服务器的端口号。

相关文章:

Linux UDP 编程详解

一、引言 在网络编程领域&#xff0c;UDP&#xff08;User Datagram Protocol&#xff0c;用户数据报协议&#xff09;作为一种轻量级的传输层协议&#xff0c;具有独特的优势和适用场景。与 TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff0…...

【2024年华为OD机试】(B卷,100分)- 计算最接近的数 (Java JS PythonC/C++)

一、问题描述 题目解析 我们需要找到一个下标 i&#xff0c;使得表达式 X[i] - X[i 1] - ... - X[i K - 1] 的结果最接近于数组的中位数。如果有多个 i 满足条件&#xff0c;则返回最大的 i。 关键点&#xff1a; 中位数计算&#xff1a; 将数组排序后&#xff0c;中位数…...

Pytorch 自学笔记(三):利用自定义文本数据集构建Dataset和DataLoader

Pytorch 自学笔记&#xff08;三&#xff09; 1. Dataset与DataLoader1.1 torch.utils.data.Dataset1.2 torch.utils.data.DataLoader Pytorch 自学笔记系列的第三篇。针对Pytorch的Dataset和DataLoader进行简单的介绍&#xff0c;同时&#xff0c;介绍如何使用自定义文本数据集…...

QT 使用QSqlTableModel对数据库进行创建,插入,显示

文章目录 效果图概述功能点代码分析初始数据插入数据数据显示 总结 效果图 概述 本案例用于对数据库中的数据进行显示等其他操作&#xff0c;其他表格筛选&#xff0c;过滤等功能可看此博客 框架&#xff1a;数据模型使用QSqlTableModel&#xff0c;视图使用QTableView&#x…...

如何学习Transformer架构

Transformer架构自提出以来&#xff0c;在自然语言处理领域引发了革命性的变化。作为一种基于注意力机制的模型&#xff0c;Transformer解决了传统序列模型在并行化和长距离依赖方面的局限性。本文将探讨Transformer论文《Attention is All You Need》与Hugging Face Transform…...

浅谈云计算22 | Kubernetes容器编排引擎

Kubernetes容器编排引擎 一、Kubernetes管理对象1.1 Kubernetes组件和架构1.2 主要管理对象类型 二、Kubernetes 服务2.1 服务的作用与原理2.2 服务类型 三、Kubernetes网络管理3.1 网络模型与目标3.2 网络组件3.2.1 kube-proxy3.2.2 网络插件 3.3 网络通信流程 四、Kubernetes…...

计算 SAMOut V3 在将词汇表从1万 增加到6千万的情况下能够减少多少参数

当我们将词汇表从 60,000,000&#xff08;六千万&#xff09;减少到 10,000 时&#xff0c;实际上是在缩小模型的词嵌入层及其共享的语言模型头&#xff08;LM Head&#xff09;的规模。这将导致参数量显著减少。我们可以通过以下步骤来计算具体的参数减少量。 参数量减少计算…...

03.选择排序

一、题目思路 选择排序是一种简单直观的排序算法。它的工作原理是&#xff1a;首先在未排序序列中找到最小&#xff08;或最大&#xff09;元素&#xff0c;存放到排序序列的起始位置&#xff0c;然后&#xff0c;再从剩余未排序元素中继续寻找最小&#xff08;或最大&#xff…...

02_登录窗口

新建场景 重命名为GameRoot 双击GameRoot进入新场景 同样摄像机清除格式 删除平行光并关闭渲染灯光的天空盒 新建空节点重命名为GameRoot GameRoot为游戏的根节点 在整个游戏中都不会被删除 在游戏的根节点下创建UI的根节点Canvas 创建一个空节点 作为UI根节点下的 登录场景UI…...

NodeJS | 搭建本地/公网服务器 live-server 的使用与安装

目录 介绍 安装 live-server 安装方法 安装后的验证 环境变量问题 Node.js 环境变量未配置正确 全局安装的 live-server 路径未添加到环境变量 运行测试 默认访问主界面 访问文件 报错信息与解决 问题一&#xff1a;未知命令 问题二&#xff1a;拒绝脚本 公网配置…...

SystemUI 实现音量条同步功能

需求&#xff1a;SystemUI 实现音量条同步功能 具体问题 以前在SystemUI 下拉框添加了音量条控制&#xff0c;目前发现在SystemUI下拉框显示状态的情况下&#xff0c; 按键或者底部虚拟导航点击音量加减时候&#xff0c;SystemUI音量条不更新。 如下图&#xff1a;两个Syste…...

嵌入式知识点总结 C/C++ 专题提升(一)-关键字

针对于嵌入式软件杂乱的知识点总结起来&#xff0c;提供给读者学习复习对下述内容的强化。 目录 1.C语言宏中"#“和"##"的用法 1.1.(#)字符串化操作符 1.2.(##)符号连接操作符 2.关键字volatile有什么含意?并举出三个不同的例子? 2.1.并行设备的硬件寄存…...

基础入门-传输加密数据格式编码算法密文存储代码混淆逆向保护安全影响

知识点&#xff1a; 1、传输格式&传输数据-类型&编码&算法 2、密码存储&代码混淆-不可逆&非对称性 一、演示案例-传输格式&传输数据-类型&编码&算法 传输格式 JSON XML WebSockets HTML 二进制 自定义 WebSockets&#xff1a;聊天交互较常…...

几个Linux系统安装体验(续): 统信桌面系统

本文介绍统信桌面系统&#xff08;uos&#xff09;的安装。 下载 下载地址&#xff1a; https://www.chinauos.com/resource/download-professional 下载文件&#xff1a;本文下载文件名称为uos-desktop-20-professional-1070-amd64.iso。 下载注意事项&#xff1a;可直接下…...

算法日记6.StarryCoding P52:我们都需要0(异或)

一、题目 二、题解&#xff1a; 1、对于这道题&#xff0c;题意为让我们寻找一个数x使得 b[i]a[i]^x&#xff0c; 并且b[1]^b[2]^b[3]^ b[4]^b[5]....0 2、我们把b[i]给拆开&#xff0c;可以得到 3、又因为^满足结合律&#xff0c;因此&#xff0c;可以把括号给拆开 4、接着…...

【网络协议】RFC3164-The BSD syslog Protocol

引言 Syslog常被称为系统日志或系统记录&#xff0c;是一种标准化的协议&#xff0c;用于网络设备、服务器和应用程序向中央Syslog服务器发送日志消息。互联网工程任务组&#xff08;IETF&#xff09;发布的RFC 3164&#xff0c;专门定义了BSD Syslog协议的规范和实现方式。通…...

SpringCloud -根据服务名获取服务运行实例并进行负载均衡

Nacos注册中心 每个服务启动之后都要向注册中心发送服务注册请求&#xff0c;注册中心可以和各个注册客户端自定义协议实现服务注册和发现。 pom.xml <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-na…...

CentOS 安装Redis

1. 安装 Redis 安装 EPEL 仓库&#xff08;对于 CentOS/RHEL 系统&#xff09;&#xff1a; 首先安装 EPEL 仓库&#xff0c;因为 Redis 存在于 EPEL 仓库中&#xff1a; yum install epel-release安装 Redis 数据库&#xff1a; yum install redis2. 修改 Redis 配置文件 …...

Linux网络 TCP socket

TCP简介 TCP&#xff08;Transmission Control Protocol&#xff09;是一种面向连接的、可靠的、基于字节流的传输层通信协议。它位于OSI模型的第四层&#xff0c;主要为应用层提供数据传输服务。TCP通过三次握手建立连接&#xff0c;确保数据在发送和接收过程中的准确性和顺序…...

(一)相机标定——四大坐标系的介绍、对应转换、畸变原理以及OpenCV完整代码实战(C++版)

一、四大坐标系介绍 1&#xff0c;世界坐标系 从这个世界&#xff08;world&#xff09;的视角来看物体 世界坐标系是3D空间坐标&#xff0c;每个点的位置用 ( X w , Y w , Z w ) (X_w,Y_w,Z_w) (Xw​,Yw​,Zw​)表示 2&#xff0c;相机坐标系 相机本身具有一个坐标系&…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

短视频矩阵系统文案创作功能开发实践,定制化开发

在短视频行业迅猛发展的当下&#xff0c;企业和个人创作者为了扩大影响力、提升传播效果&#xff0c;纷纷采用短视频矩阵运营策略&#xff0c;同时管理多个平台、多个账号的内容发布。然而&#xff0c;频繁的文案创作需求让运营者疲于应对&#xff0c;如何高效产出高质量文案成…...

关于uniapp展示PDF的解决方案

在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项&#xff1a; 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库&#xff1a; npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...

OD 算法题 B卷【正整数到Excel编号之间的转换】

文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的&#xff1a;a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...

Spring Security 认证流程——补充

一、认证流程概述 Spring Security 的认证流程基于 过滤器链&#xff08;Filter Chain&#xff09;&#xff0c;核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤&#xff1a; 用户提交登录请求拦…...