【Linux系统化学习】网络套接字(编写简单的UDP服务端和客户端)
目录
理解源IP地址和目的IP地址
认识端口号
端口号和进程ID的区别
源端口号和目的端口号
认识TCP和UDP协议
TCP协议
UDP协议
网络字节序
socket编程接口
socket常见API
sockaddr结构
简单的UDP网络程序
UDP服务端
创建套接字
填充本地网络信息
绑定
收取消息
完整的服务端封装代码
UDP客户端
创建套接字
填充服务端信息
操作系统自动绑定
发送消息
客户端完整代码
一些补充
本地回环测试
netstat指令
理解源IP地址和目的IP地址
-
源IP地址(Source IP Address):源IP地址是指发送数据包的设备或主机的IP地址。它是数据包的来源地址,标识了数据包从哪个设备发送出来。当你发送数据到网络上的其他设备时,你的设备会将数据包标记上源IP地址,以便接收设备知道数据来自哪里。
-
目的IP地址(Destination IP Address):目的IP地址是指接收数据包的设备或主机的IP地址。它是数据包的目标地址,标识了数据包应该被发送到哪个设备。当你发送数据到网络上的另一个设备时,你的设备会将数据包标记上目的IP地址,以便网络路由器和接收设备知道将数据包传送到哪里。
认识端口号
- 端口号(port)是传输层协议的内容.
- 端口号是一个2字节16位的整数;
- 端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理;
- IP地址 + 端口号能够标识网络上的某一台主机的某一个进程;
- 一个端口号只能被一个进程占用.
注:上篇文章我们说了两台主机可以通过网络进行通信,在准确一点就是两台主机中的进程通过网络进行通信,就像我们可以在QQ给自己的基友发消息,通过网络你的基友机会在自己主机的QQ上收到你的消息;两台主机上的两个QQ进程就通过网络这同一份资源进行远距离通信。
今天我们是通过网络,对于双方而言:
- 数据首先要到达目标主机(IP)
- 找到指定的进程
- IP地址的是用来表示互联网中唯一的主机,端口号用来标识该指定的机器中进程的唯一性;因此IP加端口号可以用来表示互联网中唯一的一个进程。IP加端口就是套接字(socket)
端口号和进程ID的区别
端口号和进程ID是两个不同层次、不同领域的标识符。端口号用于网络通信中标识不同的应用程序或服务,而进程ID用于操作系统中标识不同的进程。在某些情况下,可以将端口号和进程ID关联起来,例如查看特定端口上运行的进程,但它们仍然是不同的概念。
注:一个进程可以有多个端口号,但是一个端口号只能代表一个进程
源端口号和目的端口号
-
源端口号(Source Port Number):源端口号是发送数据包的设备或主机上的应用程序或服务使用的端口号。在建立连接时,发送端的应用程序会随机选择一个空闲端口作为源端口号,并将其包含在发送的数据包中。源端口号帮助目标设备知道从哪个端口接收到了数据包,以便回复响应。
-
目的端口号(Destination Port Number):目的端口号是接收数据包的设备或主机上的应用程序或服务期望接收数据包的端口号。在发送数据包时,发送端会指定目标设备的IP地址和目标端口号。接收设备根据目的端口号确定将数据包传递给哪个应用程序或服务。
其实我们可以简单理解为:就是在描述 "数据是谁发的, 要发给谁";
认识TCP和UDP协议
我们先对TCP(Transmission Control Protocol 传输控制协议)、UDP(User Datagram Protocol 用户数据报协议)有一个直观的认识; 后面的文章我们再详细讨论TCP、UDP的一些细节问题.
TCP协议
- 传输层协议
- 有连接
- 可靠传输
- 面向字节流
UDP协议
- 传输层协议
- 无连接
- 不可靠传输
- 面向数据报
网络字节序
我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?
- 发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;
- 接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;
- 因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.
- TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节.
- 不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据;
- 如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可;
为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。
- 这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数。
- 例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
- 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;
- 如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。
注:简单来说就是通过网络必须传送的是大端序,因此在传送之前不清楚自己主机是大端还是小端必须进行转换为大端序
socket编程接口
socket常见API
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address,socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
网络编程的时候,socket是有很多分类的:
- unix socket(域间套接字) :同一台机器上的文件路径,类似于命名管道,用于本主机内部进行通信
- 网络socket:ip+port用于网络通信
- 原始socket:跳过运输层,直接访问网络层;用于编写一些网络工具
sockaddr结构
网络编程的时候,有不同的应用场景,理论上而言我们应该给每一种场景都设计一套编程接口,但是设计者想使用一套接口,因此sockaddr是一个通用的接口;
sockaddr就相当与基类,当传入不同的类型是判断每个结构体的前两个字节进行类型的强转换。
IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型, 16位端口号和32位IP地址.
IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6. 这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容.
socket API可以都用struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数;
简单的UDP网络程序
UDP服务端
初始化服务器
创建套接字
int socket(int domain, int type, int protocol);
返回值
socket
函数的返回值代表了新创建的套接字的文件描述符,如果创建失败则返回 -1。- 在成功创建套接字后,
socket
函数会返回一个非负整数,表示新创建的套接字的文件描述符。
参数
domain
(地址族):
- 类型:
int
- 意义:指定套接字的地址族,即通信所采用的协议族。
- 常见取值:
AF_INET
:IPv4 地址族。AF_INET6
:IPv6 地址族。AF_UNIX
或AF_LOCAL
:Unix 域(本地)套接字。
上面提到有三种套接字的分类,我们是网络套接字并且只介绍IPv4因此选择第一个取值
type
(套接字类型):
- 类型:
int
- 意义:指定套接字的类型,即套接字的通信模式。
- 常见取值:
SOCK_STREAM
:流套接字,提供可靠的、面向连接的、基于字节流的服务,使用 TCP 协议。SOCK_DGRAM
:数据报套接字,提供不可靠的、无连接的、基于数据报的服务,使用 UDP 协议。SOCK_RAW
:原始套接字,允许对底层协议进行直接访问,常用于网络监控和特殊应用。
编写的是UDP服务器,因此选择第二个参数
protocol
(协议):
- 类型:
int
- 意义:指定套接字所使用的协议,通常为 0,表示使用默认的协议。
- 对于
SOCK_STREAM
和SOCK_DGRAM
类型的套接字,协议通常可以省略,因为它们分别与 TCP 和 UDP 相关联。 - 对于
SOCK_RAW
类型的套接字,可以指定底层的协议,如 IP、ICMP 等。
一般为0;
填充本地网络信息
因为我们是网络套接字,因此需要一个struct sockaddr_in的结构体;将我们的IP地址、端口号和套接字类型填充到结构体中;
注:
- 创建好结构体后需要对结构体里的内容清零
- 填充端口号时我们要将我们的主机序列转为网络序列
- 填充IP地址时我我们要将原始的点分十进制的字符串转换为四字节的网络序列
- 我们不可以使用我们的服务器的IP地址作为我们程序的IP地址,下一步绑死后只能收到该IP发送的报文。
- 我更推荐使用任意IP地址,
INADDR_ANY
是一个特殊的 IP 地址,在网络编程中经常用于绑定套接字到本地计算机的所有网络接口上。- 具体来说,
INADDR_ANY
表示接受任何来自本地计算机所有网络接口(包括所有网卡)的数据包。这在服务器编程中非常有用,因为服务器通常需要监听来自所有网络接口的连接请求。
绑定
上篇文章我们提到传输层作用于内核中,我们上一步填充的各种信息只存在于栈中,因此我们需要告诉操作系统内核,某个特定的套接字对应于网络上的某个地址。这个过程就是绑定;
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
返回值
bind
函数的返回值代表了函数执行的结果,它通常有以下两种可能:
如果绑定成功,
bind
函数返回值为 0。这表示套接字已成功绑定到指定的地址和端口上。如果绑定失败,
bind
函数返回值为 -1,并且会设置相应的错误码以指示失败的原因。这种情况可能
参数
套接字描述符:
- 类型:
int
(整数) - 意义:要绑定的套接字的文件描述符。
- 在调用绑定函数时,需要传递已经创建好的套接字的文件描述符。
地址结构指针:
- 类型:
const struct sockaddr *
- 意义:指向存储目标地址信息的结构体的指针。
- 绑定函数需要知道要绑定的目标地址和端口号。通常使用的是
sockaddr
结构体或其派生结构体,如sockaddr_in
(IPv4 地址)或sockaddr_in6
(IPv6 地址)等。 - 使用
const
关键字修饰指针,表示绑定函数不会修改该地址结构。
地址结构的长度:
- 类型:
socklen_t
(整数) - 意义:地址结构的长度。
- 用于指定地址结构的实际长度,以确保绑定函数能够正确地解析地址结构。
启动服务器
收取消息
启动服务器就是启动一个程序且程序没收到特定的指令不退出,即就是一个死循环。
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
recvfrom
函数用于从指定的套接字接收数据,并将数据存储到指定的缓冲区中,同时还可以获取数据发送方的地址信息。这个函数通常在使用 UDP 协议进行通信时使用,因为 UDP 是面向数据报的,每个数据包都有自己的源地址和目标地址。
返回值
recvfrom
函数的返回值是接收到的数据的字节数,即实际读取到缓冲区中的数据量。如果发生错误,返回值为 -1。
参数
套接字描述符:
- 类型:
int
(整数) - 意义:要接收数据的套接字的文件描述符。
- 在调用
recvfrom
函数时,需要传递已经创建好的套接字的文件描述符。
缓冲区指针:
- 类型:
void *
- 意义:指向存储接收数据的缓冲区的指针。
- 接收到的数据将被存储到这个缓冲区中。
缓冲区大小:
- 类型:
size_t
(无符号整数) - 意义:缓冲区的大小,即接收数据的最大长度。
- 在调用
recvfrom
函数之前,应该确保缓冲区足够大,以容纳接收到的数据。
标志:
- 类型:
int
(整数) - 意义:用于指定接收数据的选项。
- 可以设置为 0,表示没有特殊的选项。
发送方地址结构指针:
- 类型:
struct sockaddr *
- 意义:用于存储发送方的地址信息的结构体指针。
- 如果不需要获取发送方的地址信息,可以将这个参数设置为
NULL
。
发送方地址结构的长度指针:
- 类型:
socklen_t *
- 意义:发送方地址结构的长度。
- 在调用
recvfrom
函数之前,需要将这个参数设置为一个指向长度变量的指针,用于接收实际的地址结构长度。
完整的服务端封装代码
Udpserver.hpp
#pragma once
#include<iostream>
#include<string>
#include<sys/types.h>
#include<sys/socket.h>
#include<cerrno>
#include<unistd.h>
#include<cstring>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<strings.h>
using namespace std;
static const uint16_t defaultport = 8888;
static const int sockfd=-1;
static const int size=1024;
class UdpServer
{
public:UdpServer(uint16_t port=defaultport):_port(port),_sockfd(sockfd){}//初始化服务器void Init(){//创建套接字_sockfd = socket(AF_INET,SOCK_DGRAM,0);if(_sockfd<0){//表示创建失败cout<<"Fatal Error"<<errno<<strerror(errno)<<endl;exit(2);}else{cout<<"socket success "<<"sockfd : "<<_sockfd<<endl;}//绑定套接字struct sockaddr_in local;//初始化local //指定的一块内存清零bzero(&local,sizeof(local));//填充//进行网络通信local.sin_family = AF_INET;//端口号//转为网络序列local.sin_port = htons(_port);//1.4字节点分十进制字符串 2. 转为网络序列//local.sin_addr.s_addr = inet_addr(_ip.c_str());//使用地址任意//实现IP动态绑定local.sin_addr.s_addr = INADDR_ANY;//到此还没有设置到内核中,只存在于栈//绑定//强制类型转换int n = ::bind(_sockfd,(struct sockaddr*)&local,sizeof(local));if(n!=0){cout<<"Fatal Error , bind error "<<errno<<" "<<strerror(errno)<<endl;exit(3);}}//启动服务器//服务器永远不退出//是一个死循环void Start(){//收发消息//返回值实际收到的消息//第一个参数为文件描述符//第二个//第三个为期望//第四个参数为收数据的模式通常设置为0 阻塞式//最后两个参数为输出型参数//保存我们客户端的信息 ip portchar buffer[size];while(1){//预留一个\0struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t n = recvfrom(_sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);if(n>0){//拿到客户端的信息//网络序列转主机序列uint16_t clientport = ntohs(peer.sin_port);string clientip = inet_ntoa(peer.sin_addr);string info = clientip;info+=":";info+=std::to_string(clientport);buffer[n]=0;cout<<"["<<info<<"]"<<buffer<<endl;//返回消息sendto(_sockfd,buffer,n,0,(struct sockaddr*)&peer,len);}}}~UdpServer(){if(_sockfd!=-1){close(_sockfd);}}
private:uint16_t _port;//端口号int _sockfd;
};
Main.cc
#include<iostream>
#include<memory>
#include<string>
#include"Udpserver.hpp"
using namespace std;
enum comm
{Usage_Err=1
};
void Usage(string proc)
{cout<<"usage: \n\t"<<proc<<"locak_port"<<endl;
}
int main(int argc , char * argv[])
{//告诉如何使用if(argc!=2){Usage(argv[0]);return Usage_Err;}// string ip = argv[1];uint16_t port= stoi(argv[1]);UdpServer* usvr = new UdpServer(port);usvr->Init();usvr->Start();delete usvr;return 0;
}
UDP客户端
作为客户端我们一定知道服务端的IP和端口号,作为服务端IP和端口号是不可能随便改变的;
创建套接字
和服务端类似
填充服务端信息
和服务端类似
操作系统自动绑定
客户端不需要显示使用bind函数进行绑定,而是操作系统自动隐式绑定;因为如果客户端能够绑定特定的端口号,那么可能会导致端口冲突的问题。假设多个客户端应用都试图绑定到相同的固定端口号上,那么在同一台计算机上运行这些应用时就会发生冲突。为了避免这种情况,操作系统会自动为客户端分配一个可用的临时端口。
发送消息
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
返回值
- 如果成功,
sendto
函数返回发送的字节数。这通常与len
参数相同,但也可能更少(例如,如果套接字是非阻塞的,并且没有足够的缓冲区空间来容纳整个消息)。- 如果发生错误,
sendto
函数返回 -1,并设置全局变量errno
以指示错误类型。可能的错误包括ECONNREFUSED
(连接被拒绝)、EHOSTUNREACH
(主机不可达)、EMSGSIZE
(消息太大)、ENOBUFS
(没有可用的缓冲区空间)等。
参数
sockfd
:这是一个打开的 socket 文件描述符,用于标识一个打开的 socket。
buf
:这是一个指向要发送数据的缓冲区的指针。
len
:这是要发送的数据的字节数。
flags
:这是可选的标志参数,可以用来指定不同的操作行为。常见的标志包括:
MSG_CONFIRM
:告知底层传输层协议确认数据。MSG_DONTROUTE
:告知底层传输层协议不要路由数据。MSG_EOR
:表示发送的数据是一个消息的末尾。- 等等。具体的标志因操作系统和网络协议栈的不同而有所不同。
dest_addr
:这是一个指向目标地址信息的结构体的指针。它是一个 sockaddr
结构体,可以是 sockaddr_in
或 sockaddr_in6
结构体,具体取决于使用的网络协议版本。
addrlen
:这是目标地址结构体的大小,以字节为单位。通常情况下,它可以通过 sizeof(struct sockaddr)
来获取。
客户端完整代码
Client.cc
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <cerrno>
#include <string.h>
#include <string>
#include <unistd.h>
#include<arpa/inet.h>
#include<netinet/in.h>using namespace std;
void Usage(const string &process)
{cout<<"Usage : "<<process<<" server_ip server_port"<<endl;
}
int main(int argc, char*argv[])
{if (argc != 3){Usage(argv[0]);return 1;}string serverip = argv[1];uint16_t serverport = stoi(argv[2]);int sock = socket(AF_INET, SOCK_DGRAM, 0);if (sock < 0){cout << "socket error : " << strerror(errno) << endl;return 1;}cout << "socket success!" << endl;// 作为客户端必须知道服务器的ip和端口号// 作为客户端一定要进行绑定,但是不是显示绑定,客户端会在首次发送数据的时候进行自动绑定// 服务端端口号,一定是总所周知的不可改变的,// 客户端需要prot,绑定随机端口// why?// 因为客户端非常的多// 绑定确定的端口号,可能会在某一时刻启动失败// 让本地操作系统自动随机绑定,随机选择端口struct 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());// 发消息while (1){//我们要发的数据string inbuffer;cout << "plase Entre#: ";std::getline(std::cin, inbuffer);//发给谁ssize_t n = sendto(sock,inbuffer.c_str(),inbuffer.size(),0,(struct sockaddr*)&server,sizeof(server));if(n>0) {//收消息//UDP支持全双工通信char buffer[1024];struct sockaddr_in temp;socklen_t len = sizeof(temp);ssize_t m = recvfrom(sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&temp,&len);if(m>0){buffer[m]='\0';cout<<"server echo# "<<buffer<<endl;}else{break;}}else {break;}}//套接字类型为文件描述符//使用结束后需要关闭close(sock);return 0;
}
一些补充
本地回环测试
我们可以让封装的服务端带上IP地址,使用127.0.0.1这个IP地址在本地的一台服务器上开上两个窗口启动服务端和客户端进行本地回环测试,检查代码和程序;上面的代码可以做到一些简单的交互联动。
netstat指令
netstat
命令,你可以查看本地计算机上的网络连接、路由表、网络接口统计信息等
常用选项
-a
:显示所有连接和监听端口。-t
:显示 TCP 协议的连接。-u
:显示 UDP 协议的连接。-l
:显示监听状态的连接。-n
:以数字形式显示地址和端口。-p
:显示与连接相关的进程 ID。-r
:显示路由表。-i
:显示网络接口的统计信息。-s
:显示网络协议的统计信息。
今天对网络套接字的分享到这就结束了,希望大家读完后有很大的收获,也可以在评论区点评文章中的内容和分享自己的看法;个人主页还有很多精彩的内容。您三连的支持就是我前进的动力,感谢大家的支持!!!
相关文章:

【Linux系统化学习】网络套接字(编写简单的UDP服务端和客户端)
目录 理解源IP地址和目的IP地址 认识端口号 端口号和进程ID的区别 源端口号和目的端口号 认识TCP和UDP协议 TCP协议 UDP协议 网络字节序 socket编程接口 socket常见API sockaddr结构 简单的UDP网络程序 UDP服务端 创建套接字 填充本地网络信息 绑定 收取消息 …...

MFC 列表控件修改实例(源码下载)
1、本程序基于前期我的博客文章《MFC下拉菜单打钩图标存取实例(源码下载)》 2、程序功能选中列表控件某一项,修改这一项的按钮由禁止变为可用,双击这个按钮弹出对话框可对这一项的记录数据进行修改,点击确定保存修改数…...

QT设计模式:模板模式
基本概念 模板模式(Template Pattern)是一种行为型设计模式,它定义了一个操作中的算法的模板,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。 实现的模块有࿱…...

8.k8s中网络资源service
目录 一、service资源概述 二、service资源类型 1.ClusterIP类型 2.service的nodeport类型 3.service的loadbalancer类型(了解即可) 4.service的externalname类型(了解即可) 三、nodeport的端口范围设置和svc的endpoint列表 1.修…...

51单片机keil编程中遇到的问题(持续更新)
字符无法打印报错 查看特殊功能寄存器名字的时候也会报错,因为无法编译通过,导致头文件的定义内容无法查找 keil编译中 error C127: ‘xx’: invalid storage class 这种一般是在编写头文件或源文件时,在声明函数的结尾没有添加分号&…...

C++类和对象详解(一)
目录 面向过程和面向对象初步认识类的引入类的定义类的两种定义方式声明和定义全部放在类体中 声名定义分离 类的作用域成员变量命名规则建议访问限定符 类的封装类的实例化类对象模型类的对象大小的计算扩展 结构体内存对齐规则 感谢各位大佬对我的支持,如果我的文章对你有用,…...

SCI论文检索报告长什么样?怎么出具?一文了解!
1、SCI检索报告是什么 SCI数据库收录最能反映基础学科研究水平和论文质量,该检索系统收录的科技期刊比较全面,可以说它是集中各个学科高质优秀论文的精粹,评职晋升、项目申报、评奖评优等很多关键时期,都需要开具已经在SCI发表的论…...

UE4_Water插件_Buoyancy组件使用
water插件提供了一个浮力Actor蓝图类。 需要注意的几个问题: 1、StaticMesh需要替换根组件。 2、需要模拟物理设置质量。 3、需要添加浮力组件,设置浮力点,应用水中牵引力。...

OceanBase学习1:分布式数据库与集中式数据库的差异
目录 1. 传统集中式数据库 2. 数据库中间件的分库分表 3. 分布式数据库的基本特点及对比分析 4. OceanBase和传统数据库的对比 5. 小结 1. 传统集中式数据库 优点 成熟稳定:经过近40年的发展,应用到各行各业,产品技术非常成熟稳定行业适配性强:适配…...

计算机网络技术主要学什么内容,有哪些课程
计算机网络技术专业是一个涉及理论与实践紧密结合的学科,主要学习内容有计算机网络基础、网络设备技术、网络编程等内容,以下是上大学网(www.sdaxue.com)整理的计算机网络技术主要学什么内容,供大家参考! 基…...

Mac下安装ffmpeg
1、安装gedit brew install gedit2、配置环境变量,打开~/.zshrc,在末尾添加语句 export PATH$PATH:/usr/local/ffmpeg/bin3、执行语句,使环境变量生效 source ~/.zshrc 4、终端输入 ffmpeg ,看环境变量是否配置成功。 至此&a…...

自制AI:Park_01修改bug
修改了一下不能存东西,不能打开东西的bug #include<bits/stdc.h> #include<windows.h> using namespace std; double mem10737418240; map<string,string> jishiben; string mulu"朴同学给你的一封信.memo\n"; int cntnote1; void sta…...

解救应用启动危机:Spring Boot的FailureAnalyzer机制
目录 一、走进FailureAnalyzer 二、在Spring Boot中如何生效 三、为什么可能需要自定义FailureAnalyzer 四、实现自定义基本步骤 (一)完整步骤要求 (二)注册方式说明 通过Spring Boot的spring.factories文件(建…...

win11个性化锁屏界面怎么关闭?
win11个性化锁屏界面关闭方法对于win11用户来说,关闭个性化锁屏界面是一个常见问题。本文将由php小编苹果详细介绍如何执行此操作,分步指导并提供操作截图。继续阅读以了解具体步骤。 win11个性化锁屏界面关闭方法 第一步,点击底部Windows图…...

白酒:白酒香型与品质消费的关系及影响
云仓酒庄的豪迈白酒作为中国白酒的品牌,其白酒香型与品质消费的关系及影响备受关注。随着消费者对品质的重视程度不断提高,了解白酒香型与品质之间的关系对于云仓酒庄和消费者都具有重要意义。 经云仓酒庄豪迈白酒分析,白酒香型与品质消费的关…...

智能BI(后端)-- 系统优化(安全性,数据存储,限流)
文章目录 安全性todo 数据存储限流限流的几种算法限流粒度限流的实现本地限流(单机限流)Redisson实现分布式限流(多机限流) 安全性 问题引入:如果用户上传一个超大的文件怎么办?比如1000G? 预防: 只要涉及…...

探索数字社交的奇迹:解读Facebook的革命性影响
1. 社交互动的全新模式 Facebook的出现不仅仅是一个社交媒体平台的诞生,更是一种全新的社交互动模式的开启。传统的社交模式主要依赖于面对面的交流,而Facebook则将社交推向了全新的数字化平台,使得人们可以在虚拟的世界里建立和维系社交关系…...

FileCodeBox-Lite:轻量级文件分享解决方案
在数字时代,文件分享是一个常见的需求,无论是个人用户还是企业团队。FileCodeBox-Lite提供了一个简单、高效且安全的文件分享解决方案。以下是对FileCodeBox-Lite项目的详细介绍。 项目简介 FileCodeBox-Lite是一个轻量级的文件分享系统,…...

【ARM】ARM寄存器和异常处理
目录 1.指令的执行过程 2. ARM处理器概述 3.ARM指令集 4.ARM存储模型 5. ARM工作模式 6.ARM寄存器组织 (1)寄存器 (2) ARM寄存器 (3)CPSR寄存器 7. ARM异常处理 (1&am…...

数仓建模【埋点设计与管理】
埋点设计与管理 埋点的作用 分析用户转化以及留存:分析用户偏好收集市场反馈保障用户数据安全定位异常其他作用 埋点数仓设计 数据进入数仓之前我们就需要设计好数仓表,埋点表的数据有几个特点: 数据量非常大,可能是所有数据集…...

Spring Clound介绍
Spring Cloud 是一系列框架的集合,它利用 Spring Boot 的开发便利性简化了分布式系统(例如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话和集群状态)的开发。Spring Cloud 旨在为开发者…...

Redhat Linux忘记密码解决方案
1、重启系统 2、将光标移动到要启动的内核 3、按e编辑当前条目 4、将光标移动以Linux开头的行,此为内核命令行 5、在末尾添加人的rd.break 6、按ctrlx继续启动,如果发现输入的rd.break不能进入到伪系统,那么改为 rd.break consoletty0 (按ctr…...

对于子数组问题的动态规划
前言 先讲讲我对于这个问题的理解吧 当谈到解决子数组问题时,动态规划(DP)是一个强大的工具,它在处理各种算法挑战时发挥着重要作用。动态规划是一种思想,它通过将问题分解成更小的子问题并以一种递归的方式解决它们,然后利用这些…...

Instal IIS on Windows Server 2022 Datacenter
和以往版本一样,没有什么不同,So easy! WinR - ServerManager.exe 打开服务器管理器,点击【添加角色和功能】,选择自己想要的角色和功能。 一、开始之前:帮助说明,点击【下一步】;…...

飞天使-k8s知识点30-kubernetes安装1.28.0版本-使用containerd方式
文章目录 安装前准备containerd 配置内核参数优化安装nerdctl以上是所有机器全部安装开始安装初始化,这步骤容易出问题! 安装前准备 内核升级包的md5,本人已验证,只要是这个md5值,放心升级 1ea91ea41eedb35c5da12fe7030f4347 ke…...

Oracle 误操作insert delete update 数据回滚
查询回滚数据 select * from tablename AS OF TIMESTAMP TO_TIMESTAMP(2023-12-29 10:29:00,yyyy-mm-dd hh24:mi:ss) where not exists (select 1 from tablename A where A.xh tablename.xh and A.TIME tablename.TIME); TO_TIMESTAMP(2023-12-29 10:29:00,yyyy-mm-dd h…...

Linux系统(CentOS)下安装配置 Nginx 超详细图文教程
一、下载并安装 1.打开nginx官网并点击右侧的download,Nginx官网下载地址 2.选择稳定版本 我放在/usr/local/nginx/下,新建文件夹 mkdir /usr/local/nginx/ 通过xftp传输到Linux的服务器上,这里方法不过多复述。 或者如果Linux联网…...

追求完美用户体验,从变量名设计的细节抓起
在一个安静的办公室里,卧龙和凤雏正坐在电脑前忙碌地工作着。阳光透过窗户洒在他们的脸上,映照出专注的神情。 “变量命名让人摸不着头脑,光看变量名很难搞清楚它的用途。”卧龙眉头紧皱,表情严肃地说道。 “哦?具体是…...

matlab实现K均值聚类
在MATLAB中实现聚类分析,可以使用MATLAB内置的聚类函数,如kmeans(用于K均值聚类),linkage和cluster(用于层次聚类),或者使用MATLAB的统计和机器学习工具箱中的其他函数。 以下是一个…...

详解BOM编程
华子目录 BOM编程window对象常见的window对象的属性常见的window对象的方法注意 history对象history对象的属性history对象的方法 screen 对象navigator 对象属性方法 location对象属性方法示例 BOM编程 JavaScript本质是在浏览器中运行,所以JavaScript提供了BOM&a…...