Linux网络:TCP UDP socket

news2024/11/14 13:36:27

Linux网络:TCP & UDP socket

    • socket 套接字
      • sockaddr
      • 网络字节序
      • IP地址转换
      • bzero
    • UDP socket
      • socket
      • bind
      • recvfrom
      • sendto
    • TCP socket
      • socket
      • bind
      • listen
      • connect
      • accept
      • send
      • recv


本博客讲解 Linux 下的 TCP 和 UDP 套接字编程。无论是创建套接字、绑定地址,还是发送和接收数据,都会详细讲解。希望这篇博客能帮你更轻松地理解这些概念,并在实践中得心应手。

socket 套接字

套接字是网络通信的基础,它提供了一种标准化的方式,使得程序能够通过网络发送和接收数据。

套接字的种类非常多样,比如:

  • unix socket域套接字:用于本地通信
  • inet socket网络套接字:用于网络通信
  • raw socket原始套接字:用于网络管理

sockaddr

sockaddr 是一个在网络编程中用于表示“套接字地址”的通用结构体。它的作用是存储网络地址信息,供套接字函数使用,此时套接字函数就知道要对哪一台主机进行网络操作。sockaddr包含在头文件<arpa/inet.h>中。

sockaddr 结构体不能直接存储 IPv4 或 IPv6 的地址信息,在实际使用中,通常会用到它的具体子类型,如 sockaddr_in(用于 IPv4)和 sockaddr_in6(用于 IPv6),sockaddr_un(用于域套接)。

如图:

在这里插入图片描述

为了管理多种套接字,所有套接字的头部都是一个16位的地址类型,用于辨别这个结构体表示哪一个套接字。当操作sockaddr的时候,读取前16位就知道这个sockaddr具体是哪一种套接字。随后再进行类型转化,变成对应套接字类型的结构体,此时就能对具体的套接字做操作了。

sockaddr的定义如下:

struct sockaddr {
    unsigned short sa_family;   
    char sa_data[14];           
};
  • sa_family:表示协议族,例如 IPv4 使用 AF_INET,IPv6 使用 AF_INET6,域套接使用AF_UNIX

其中最常用的就是AF_INET进行IPv4通信。其对应的具体结构体为struct sockaddr_in,定义如下:

struct sockaddr_in {
  sa_family_t		sin_family;
  __be16		sin_port;
  struct in_addr	sin_addr;

  unsigned char		__pad[__SOCK_SIZE__ - sizeof(short int) -
			sizeof(unsigned short int) - sizeof(struct in_addr)];
};
  • sin_family:前16位,表示套接字类型
  • sin_port:表示端口号
  • sin_addr:表示IPv4地址

此处有一个小细节,sin_addr的类型是struct in_addr,按理来说IPv4的地址占32位,用一个int类型即可存储,这里的结构体又是啥?

其实是Linux对其进行了额外的一层封装:

struct in_addr {
	__be32	s_addr;
};

此处的__be32就是一个32位的整型,也就是说存储地址的时候,要用sockaddr_in.sin_addr.s_addr,此处嵌套了两层结构体

基于IP地址和端口号,此时就可以定位到全世界的一个主机上的一个具体进程,此时就可以进行后续的网络通信了!


网络字节序

在不同主机内存中,字节数据分为大端字节序和小端字节序,假设一个大端主机和一个小端主机进行通信,此时就会发生错误,因为两别解析数据的方式不同,于是网络字节序出现了。

TCP/IP 规定,大端字节序为网络字节序,在网络中通信必须使用网络字节序

也就是说,如果当前主机是大端主机,那么收发数据时不做处理。如果是小端主机,那么收发数据时要把数据转化为小端字节序。

在此处讲解这个问题,就是因为在填写sockaddr_in内部的IP和端口号时,内部数据的字节序要使用网络字节序

假设我们现在要往sockaddr_in 内部填入端口号22:

struct sockaddr_in sock;
sock.sin_port = 22;

以上就是一个错误示例,因为不清楚代码的运行环境是大端还是小端,此时存入的数据22就有可能不是网络字节序,所以要先将22转为网络字节序,Linux为此提供了专门的接口。

以下接口用于序列转化,需要头文件<arpa/inet.,h>

uint32_t htonl(uint32_t hostlong);

uint16_t htons(uint16_t hostshort);

uint32_t ntohl(uint32_t netlong);

uint16_t ntohs(uint16_t netshort);

上面接口的用法非常好记,比如htonl拆解为host to net long,也就是将long类型从主机的字节序转化为网络字节序。同理htns就是将short类型从主机的字节序转化为网络字节序。

反过来,ntohl就是net to host long,将long类型从网络字节序转化为主机的字节序。

如果主机是大端,那么这些函数对数据不做任何处理。如果主机是小端,那么就会进行大小端的转化。

由于sin_port 的类型是int16,所以在写入端口前要用htons进行转化:

struct sockaddr_in sock;
sock.sin_port = htons(22);

IP地址转换

如果想要给一个sockaddr_in结构体填入数据,那么第一个问题就是IP地址的格式问题。

IP地址有两种基本格式,4字节序列,以及点分十进制,如果拿到的IP地址格式与自己所需的类型不符,此时就要考虑两种格式之间转化的问题了。但是不必担心,这个问题也有对应的接口解决。

以下函数需要头文件:<sys/socket.h><netinet/in.h><arpa/inet.h>

inet_addr用于将点分十进制转化为四字节序列:

in_addr_t inet_addr(const char *cp);
  • cp:指向点分十进制IP地址字符串的指针

如果转化错误,返回INADDR_NONE,本质上是数字-1

示例:

struct sockaddr_in sock;
sock.sin_addr.s_addr = inet_addr("127.0.0.1");

此处要注意,实际存储IP序列的是.sin_addr.s_addr,这里有两层结构体嵌套。

那么有人就有疑问了,都说了存入sockaddr_in 中的数据必须是网络字节序,此处将点分十进制转化为四字节序列后,是不是要再转化为网络字节序?

比如这样:

struct sockaddr_in sock;
sock.sin_addr.s_addr = htons(inet_addr("127.0.0.1"));

其实不用,inet_addr会完成两个任务:

  1. 将点分十进制转化为四字节序列
  2. 将四字节序列转化为网络字节序

也就是说inet_addr内部已经顺带完成了转化网络字节序的工作

inet_ntoa用于将四字节序列转化为点分十进制:

char *inet_ntoa(struct in_addr in);

bzero

目前初始化一个sockaddr_in的代码如下:

struct sockaddr_in sock;
sock.sin_addr.s_addr = inet_addr("127.0.0.1");
sock.sin_port = htons(22);

一个要点是IP地址格式的转化,另一个要点是网络字节序。

但是这样还不完整,还有一个成员sin_family没有初始化,对于IPv4通信,此处填入AF_INET,这个在之前已经说过。

struct sockaddr_in sock;
sock.sin_family = AF_INET;
sock.sin_addr.s_addr = inet_addr("127.0.0.1");
sock.sin_port = htons(22);

目前为止已经初始化了一个比较完整的sockaddr_in了。但是创建结构体时,分配到的内存原先有可能存储了其他数据,因为有填充部分我们不初始化,为了保证之前的数据不会影响,还要把整个结构体的内存全部置为0

bzero用于初始化一段内存为0,需要头文件<strings.h>,定义如下:

void bzero(void* s, size_t n);
  • s:要初始化内存的地址
  • n:要初始化的字节数

在初始化整个sockaddr_in 之前,先用bzero将内存清零:

struct sockaddr_in sock;
bzero(&sock, sizeof(sock));

现在我们就有一个比较完整的sockaddr_in 初始化过程了:

struct sockaddr_in sock;
bzero(&sock, sizeof(sock));
sock.sin_family = AF_INET;
sock.sin_addr.s_addr = inet_addr("127.0.0.1");
sock.sin_port = htons(22);

上述所有内容,都只是在初始化一个套接字所需的地址,还没有真正的创建套接字,接下来就了解操作真正的套接字的接口。


UDP socket

socket

socket函数用于创建一个新的套接字,需要头文件<sys/types.h><sys/socket.h>,函数原型如下:

int socket(int domain, int type, int protocol);

参数:

  • domain:指定协议族
  • type:指定套接字类型
  • protocol:通常设为 0,表示使用默认协议

返回值:

  • 成功:返回新创建的套接字的文件描述符
  • 失败:返回 -1

如果要创建UDP套接字,那么参数应该填为:

int fd = socket(AF_INET, SOCK_DGRAM, 0);
  • AF_INET:表示IPv4
  • SOCK_DGRAM:表示UDP套接字,DGRAMdatagram缩写,即数据报

socket的返回值是一个整型,本质是一个文件描述符,Linux一切皆文件,后续对网络的操作就是对这个文件的操作。比如向网络中发送消息,其实就是向文件中写入数据


bind

当创建完套接字后,这个套接字还没有指定和哪一个主机通信,此时就需要IP地址和端口号,之前讲的sockaddr_in就派上用场了!

bind函数用于给套接字绑定IP地址和端口号,指定和哪一台主机通信,需要头文件<sys/types.h><sys/socket.h>,函数原型如下:

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数:

  • sockfd:套接字对应的文件描述符
  • addr:指向sockaddr的指针
  • addrlensockaddr的真实类型的长度

返回值:

  • 成功:返回0
  • 失败:返回-1,并设置错误码

之前讲解的sockaddr_insockaddr的一种,此处注意传入的是struct sockaddr *,也就是说sockaddr_in类型的变量传入的时候要进行类型转化。

由于不知道struct sockaddr *具体指向哪一种套接字地址,所以第三个参数要传入真实类型结构体大小,防止越界访问。

示例:

// 1.创建UDP套接字
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);

// 2.初始化套接字要通信的目标主机地址
struct sockaddr_in sock;
bzero(&sock, sizeof(sock));
sock.sin_family = AF_INET;
sock.sin_addr.s_addr = inet_addr("1.2.3.4");
sock.sin_port = htons(8888);

// 3.绑定地址到套接字
bind(sockfd, (struct sockaddr*)&sock, sizeof(sock));

比如以上示例中,绑定的地址为1.2.3.4,端口为8888,这表明只有1.2.3.4可以通过端口8888和这个套接字与本主机进行通信

此处不代表1.2.3.4以外的地址不能与当前主机通信了,只是对于被绑定套接字而言。就好像一个学校有好几个大门,其中某个门叫做“校长专用通道”,那么只有校长可以通过这个门进入校园,其他学生不能通过这个门进入校园,但是其他学生也可以通过其它的门进入。


recvfrom

绑定好主机后,就可以开始进行网络通信了,UDP基于报文通信,所以发送的数据也是报文形式。

recvfrom函数用于接收数据报,需要头文件<sys.types.h><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:输出型参数,得到消息发送方的sockaddr,即IP地址和端口号
  • addrlen:输出型参数,得到src_addr 真实结构的大小

返回值:

  • 成功时返回实际读取的字节数
  • 失败时返回 -1,并设置错误码

示例:

int sockfd = socket(AF_INET, SOCK_DGRAM, 0);

struct sockaddr_in src_addr;
bzero(&src_addr, sizeof(src_addr));
src_addr.sin_family = AF_INET;
src_addr.sin_addr.s_addr = inet_addr("0.0.0.0");
src_addr.sin_port = htons(8888);

bind(sockfd, (struct sockaddr*)&src_addr, sizeof(src_addr));

// 输出形参数
struct sockaddr_in peer;
socklen_t peer_len;

// 接收消息
char* buf[1024];
recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&peer, &peer_len);

此处的peer用于接收发送方的sockaddr,也就是IP地址和端口号。发送方发送的报文被存储在buf中。


sendto

sendto函数用于发送数据报,需要头文件<sys.types.h><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
  • dest_addr:指向消息接收方目标地址sockaddr的指针
  • addrlendest_addr 真实结构的大小。

返回值:

  • 成功时返回实际发送的字节数
  • 失败时返回 -1,并设置错误码
  • 返回0表示没有读取到数据

示例:

int sockfd = socket(AF_INET, SOCK_DGRAM, 0);

struct sockaddr_in dest_addr;
bzero(&dest_addr, sizeof(dest_addr));
dest_addr.sin_family = AF_INET;
dest_addr.sin_addr.s_addr = inet_addr("192.168.1.100");
dest_addr.sin_port = htons(8888);

const char *message = "Hello, UDP!";
sendto(sockfd, message, strlen(message), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));

此处给192.168.1.100地址,通过端口8888发送了一个报文,内容是”Hello, UDP!“

不知你有没有发现这一份代码有一点蹊跷,这个套接字没有bind。此处程序作为客户端向主机主动发起报文,那么操作系统会给这个进程分配一个端口。在还没有发送数据前,我们并不知道这个随机分配的端口是多少,无法进行bind,这该咋办?

在发送数据时,有两种情况:

  1. 如果套接字没有绑定端口号,Linux会自动为其分配端口号,并完成绑定,随后通过随机分配的端口发送数据,这种行为称为隐式绑定
  2. 如果套接字已经绑定了端口号,Linux则直接通过指定端口发送数据

因此以上代码中没有bind这个过程。


TCP socket

socket

与UDP一样,TCP也要通过socket函数创建套接字,只是参数略有不同。

int socket(int domain, int type, int protocol);
  • domain:对于IPv4,使用AF_INET
  • type:填入SOCK_STREAM,即面向字节流的TCP服务
  • protocol:填0即可

创建TCP套接字代码如下:

int sockfd = socket(AF_INET, SOCK_STREAM, 0);

后续就可以通过文件描述符sockfd操作TCP连接了。


bind

同样的,TCP服务端要通过bind进行绑定,表明自己连接的主机以及端口号。而客户端不用,因为发送数据时操作系统会隐式绑定。


listen

当TCP服务端启动后,此时就要等待别人来连接自己,此时就处于listen状态。而进入该状态,需要调用函数listen,包含在头文件<sys/types.h><sys/socket.h>,函数原型如下:

int listen(int sockfd, int backlog);

参数:

  • sockfd:进行监听的套接字
  • backlog:TCP全连接队列的大小,此处暂时不管,设为一个适中的值,比如10

返回值:

  • 成功:返回0
  • 失败:返回-1

示例:

struct sockaddr_in sock;
bzero(&sock, sizeof(sock));
sock.sin_family = AF_INET;
sock.sin_addr.s_addr = inet_addr("0.0.0.0");
sock.sin_port = htons(8888);

int sockfd = socket(AF_INET, SOCK_STREAM, 0);

bind(sockfd, (struct sockaddr*)&sock, sizeof(sock));
listen(sockfd, 10);

while (true);

假设该进程为test.exe,执行该程序后,通过netstat指令查看:

在这里插入图片描述

此时进程就处于LISTEN状态,即等待连接。


connect

对于客户端来说,需要向服务端发起连接请求,此时需要函数connect,包含在头文件<sys/types.h><sys/socket.h>中,函数原型如下:

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数:

  • sockfd:发起连接的套接字
  • addr:发起连接对象的信息,即IP地址何端口号
  • addrlenaddr的真实类型的大小

返回值:

  • 成功:返回0
  • 失败:返回-1并设置错误码

以下是一个客户端的示例:

// 构建目标主机信息
struct sockaddr_in sock;
bzero(&sock, sizeof(sock));
sock.sin_family = AF_INET;
sock.sin_addr.s_addr = inet_addr("127.0.0.1");
sock.sin_port = htons(8888);

// 发起TCP连接
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
connect(sockfd, (struct sockaddr*)&sock, sizeof(sock));

accept

当服务端在listen状态下,接收到来自客户端的连接,就可以选择同意这个连接,此时需要函数accept,包含在头文件<sys/types.h><sys/socket.h>中,函数原型如下:

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数:

  • sockfd:之前进行listen的套接字
  • sockaddr:输出客户端的信息,即IP地址和端口号
  • addrlensockaddr的真实大小

返回值:

  • 成功:返回一个文件描述符
  • 失败:返回-1并设置错误码

奇怪的事情来了,为什么accept会返回一个文件描述符?

此处的文件描述符,其实就是一个套接字socket,先前说过套接字是通过文件描述符来操作的。此处返回的套接字,是专门用于和客户端通信的套接字。也就是说后续与客户端通信,使用这个新的套接字完成。

对于TCP服务端来说,有两种套接字,一种是用于listen的套接字,其负责监听指定端口,查看有没有到来的连接。一旦连接建立成功,此时与客户端的通信过程由新的套接字完成。

示例:

struct sockaddr_in sock;
bzero(&sock, sizeof(sock));
sock.sin_family = AF_INET;
sock.sin_addr.s_addr = inet_addr("0.0.0.0");
sock.sin_port = htons(8888);

int listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);

bind(listen_sockfd, (struct sockaddr*)&sock, sizeof(sock));
listen(listen_sockfd, 10);

// peer用于接收客户端信息
struct sockaddr_in peer;
socklen_t peer_len;

// 接收来自客户端的连接
int sockfd = accept(listen_sockfd, (struct sockaddr*)&peer, &peer_len);

以上是一个完整的TCP服务端启动过程,一般而言accpet之后,会使用多进程/多线程完成后续的通信,而主进程继续listen其它的连接,本博客不展示该过程了。

此处的listen_sockfd就是专门用于连接的套接字,而最后的sockfd 是与客户端通信的套接字。


send

当连接建立成功后,就可以开始收发消息了,TCP是面向字节流的,与UDP不同,TCP可以把sockfd文件描述符完全当作一个文件,完成消息的读写。

发送消息使用send函数,包含在头文件<sys/types.h><sys/socket.h>中,函数原型如下:

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

参数:

  • sockfd:用于通信的套接字
  • buf:要发送的数据缓冲区
  • len:要发送的数据的长度
  • flags:设为0即可

不知道你有没有发现,它和文件写入函数write几乎没有差别:

ssize_t write(int fd, const void *buf, size_t count);

前三个参数没有区别,而send的第四个参数固定为0。其实sendwrite一样,都是直接向文件中写入字符串的,这符合TCP面向字节流的特性,在发送数据时,也可以用write代替send


recv

接收消息使用recv函数,包含在头文件<sys/types.h><sys/socket.h>中,函数原型如下:

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

参数:

  • sockfd:用于通信的套接字
  • buf:数据的接收缓冲区
  • len:最大接收数据的长度
  • flags:设为0即可

同样,其实recvread函数差不多:

ssize_t read(int fd, void *buf, size_t count);

在读取TCP连接的数据的时可以使用两者的任何一个。


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

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

相关文章

软件设计师全套备考系列文章16 -- 程序设计语言基础知识

软考-- 软件设计师&#xff08;16&#xff09;-- 程序设计语言基础知识 文章目录 软考-- 软件设计师&#xff08;16&#xff09;-- 程序设计语言基础知识前言一、章节考点二、基本概念三、文法四、有限自动机五、前缀、中缀、后缀表达式六、传值和引用(传址)七、各个程序语言的…

链表OJ题——使用栈实现单链表的逆序打印

文章目录 一、题目链接二、解题思路三、解题代码 一、题目链接 题目描述&#xff1a;使用栈&#xff0c;实现单链表的逆序打印 二、解题思路 三、解题代码 /*** 非递归实现单链表的顶逆序打印——>通过栈来实现* param*/public void printReverseListFromStack(){Stack<…

HAL库:GPIO唤醒模式 唤醒睡眠模式下的单片机

目录 HAL库&#xff1a;GPIO唤醒模式 唤醒睡眠模式下的单片机 注意事项&#xff1a; 初始化部分&#xff1a; 主函数测试部分 结果如图 HAL库&#xff1a;GPIO唤醒模式 唤醒睡眠模式下的单片机 注意事项&#xff1a; HAL库滴答定时器默认为打开状态&#xff0c;需要关闭…

AI学习指南深度学习篇:循环神经网络(RNN)Python实践

引言 在人工智能的广袤领域中,循环神经网络(Recurrent Neural Networks, RNNs)因其在处理序列数据中的卓越表现而广受关注。RNN的独特之处在于它能够保留输入数据的历史信息,并利用这些信息来预测后续的输出,这使得它在自然语言处理、时间序列预测等领域中拥有广泛的应用…

新审视零阶优化在内存高效大模型微调中的应用

人工智能咨询培训老师叶梓 转载标明出处 随着大模型模型规模的增大&#xff0c;反向传播&#xff08;BP&#xff09;所需的内存开销也日益增加&#xff0c;这对内存效率提出了挑战。尤其是在设备上训练等内存效率至关重要的应用场景中&#xff0c;解决这一问题变得尤为迫切。 …

Sora 代码规范之Refactor this method to not always return the same value.(目的性问题)

Sora描述 Refactor this method to not always return the same value.&#xff08;目的性问题&#xff09; 上述代码&#xff0c;可能出现 总是返回 null 的情况 解决一下 原因&#xff1a;为什么要这么写呢&#xff1f;因为 下面的代码会对 materialInfos 这个集合处理&#…

房子公摊要消失了?

文&#xff5c;琥珀食酒社 作者 | 璇子 你敢信 才短短三个月 江苏、浙江、广东 这三房价高昂的城市 陆续宣布房市重大改革信号 比如将空中花园绿化阳台、 小区景观绿化亭廊 、 开放式风雨连廊等不计容积率 挑高客厅只算单层面积 甚至部分区域买房面积 直接按照套内面…

足球联赛|基于SprinBoot+vue的足球联赛管理系统(源码+数据库+文档)

足球联赛管理系统 目录 基于SprinBootvue的足球联赛管理系统 一、前言 二、系统设计 三、系统功能设计 5.1 系统前台功能实现 5.2 后台功能模块实现 5.2.1 管理员模块实现 5.2.2 用户后台模块实现 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选…

20+岁老牌定制家居品牌,如何靠呼叫中心捕获年轻消费者?

Home Tour类一镜到底的短视频&#xff0c;在社交平台一直备受年轻人喜爱。金牌厨柜这个20多岁的老牌定制家居品牌&#xff0c;不仅在高端厨柜和定制家居领域有着深厚的积累&#xff0c;而且一直也在探索数字化转型的新路径&#xff1a;用全新的数字化体系&#xff0c;迎合年轻一…

使用VScode的Git版本控制功能(图文版)

☁️ 前言 今天让我来手把手教你简单入门VScode自带的Git版本控制。 &#x1f389; 初始化仓库 初始化仓库之后&#xff0c;仓库里的文件发生了任何改动都会有相应的提示&#xff0c;这对于我们开发和维护项目非常有帮助。 &#x1f389;提交更改 初始化仓库之后&#xff…

机器学习:K-means算法及代码实现

1、K-means算法原理 K-means算法是一种常用的聚类算法&#xff0c;其目的是将数据集划分为K个簇&#xff08;clusters&#xff09;&#xff0c;使得每个簇内部的数据点尽可能相似&#xff0c;而簇与簇之间的数据点尽可能不同。以下是K-means算法的基本原理和步骤&#xff1a; 初…

【C++】C++中的字符串

提示并输入一个字符串&#xff0c;统计该字符串中字母个数、数字个数、空格个数、其他字符的个数 #include <iostream>using namespace std; void total(string str); int main() {string str;cout << "请输入一个字符串:" ;//cin >>str;getline(…

SAP怎么查找系统全部的增强点呢?

1.在已有的BADI查找程序里面有点手无足措的样子&#xff0c;不知道该如何去找增强&#xff01; 2.这个时候刚刚接触系统还不熟悉&#xff0c;系统里面存在了什么增强&#xff0c;这个时候咋办捏&#xff1f;SE38 -SNIF 此时全部的增强点都在这里面啦&#xff01;&#xff01;&…

使用LinkedHashMap实现固定大小的LRU缓存

使用LinkedHashMap实现固定大小的LRU缓存 1. 什么是LRU&#xff1f; LRU是"Least Recently Used"的缩写&#xff0c;意为"最近最少使用"。LRU缓存是一种常用的缓存淘汰算法&#xff0c;它的核心思想是&#xff1a;当缓存满时&#xff0c;优先淘汰最近最少…

二、设置地图配置表

一、导入一个背景图 由于背景图比较大&#xff0c;需要缩小至0.73 二、写配置文件&#xff08;SO&#xff09; 使用List需要一个命名空间 写一个类&#xff0c;声明房间的出现数量和种类&#xff1b;将它实例化出来 三、枚举变量的多选 在枚举变量中标记命名空间&#xff…

C++:list篇

前言: 观看C的list前需要对链表有一些了解&#xff0c;如C语言的链表结构。本片仅介绍list容器中常用的接口函数概念以及使用。 list的概念&#xff1a; 简而言之&#xff0c;C的list是一个双向带哨兵位的链表容器模板 list的构造&#xff1a; 1.list():默认构造 2.li…

2024最新版Python+Pycharm安装教程,安装、环境配置、汉化全搞定,保姆级教学!

一、Python下载 为了节约时间&#xff0c;我将PythonPycharm安装包、集火码全部打包上传至CSDN官方&#xff0c;可放心下载&#xff0c;完全免费&#xff01;&#xff08;安装包均为最新版本&#xff09; 二、Python安装 1.双击运行本地文件夹下的python安装包&#xff08;以…

c++关于字符串的联系

提示并输入一个字符串&#xff0c;统计该字符串中字母个数、数字个数、空格个数、其他字符的个数 #include <iostream> #include<string> using namespace std;int main() {string s1;int letter0,digit0,space0,other0;cout<<"请输入一个字符串:"…

Visual Studio 2022 自定义字体大小

常用编程软件自定义字体大全首页 文章目录 前言具体操作1. 打开字体和颜色对话框2. 设置【文本编辑器】字体大小3. 设置【编辑器智能提示框】字体大小4. 设置【编辑器方法提示框】字体大小5. 设置【输出框】、【错误列表框】字体大小6. 设置【终端】字体大小7. 设置【程序包管理…

线程基础和线程间通信

作业一:创建3个线程&#xff0c;一个子线程拷贝文件的前一半&#xff0c;一个子线程拷贝后一半文件&#xff0c;主线程回收子线程资源。 #include <myhead.h> //定义结构体变量 typedef struct {const char *src_file;const char *dest_file;int start;int end; } Copy;…