【C语言】Linux socket 编程

一、Socket 通信过程

在 Linux 系统中,socket 是一种特殊的文件描述符,用于在网络中的不同主机间或者同一台主机中的不同进程间进行双向通信。它是通信链路的端点,可以看作是网络通信的接口。Socket 通信过程主要分为以下几个步骤:

1. 创建 Socket(socket()):

   首先需要调用 socket() 函数来创建一个 socket。这个函数会返回一个 socket 文件描述符,该描述符将用于其他所有的网络通信函数。创建 socket 时需要指定通信的类型(通常是 TCP 或 UDP)、通信协议(IP)和其他相关参数。

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

2. 绑定地址到 Socket(bind()):

   接下来,如果是服务器端,需要将一个地址(IP 地址和端口号)绑定到 socket 上。这样客户端就知道应该连接到哪个地址。

   struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = INADDR_ANY;bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));

3. 监听连接(listen()):

   对于服务器端应用程序来说,下一步是监听网络上的连接请求。这可以通过 listen() 函数实现。

   listen(sockfd, backlog);

4. 接受连接(accept()):

   当客户端请求连接时,服务器需要接受连接。采用 accept() 函数可以接受来自客户端的连接请求,并返回一个新的 socket 文件描述符,用于后续的通信。

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

5. 发起连接(connect()):

   对于客户端应用程序,使用 connect() 函数发起对远程服务器的连接请求。

  connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));

6. 数据传输(send() / recv() 或 write() / read()):

   一旦连接建立,数据就可以在 socket 之间传输。可以使用 send() 和 recv() 函数,或者 write() 和 read() 函数来进行数据的发送和接收。

   send(sockfd, data, datalen, 0);recv(sockfd, buffer, buflen, 0);

7. 断开连接(close()):

   最后,当通信结束时,应当使用 close() 函数来关闭 socket,释放资源。

   close(sockfd);

这是一个高层次的概述,实际的实现可能会根据 TCP、UDP、IPv4、IPv6 等不同设置有所不同。此外,还有各种 flag 和选项可以设置,比如非阻塞模式、超时设置等,都会影响 socket 的行为。在多线程或多进程的服务器中,还会用到一些额外的技术来高效地处理多个连接。

二、详解创建 Socket(socket())

创建 socket 是网络编程中的第一步,它是网络通信过程的起点。在 Linux 中,我们使用 socket() 系统调用来创建一个 socket 描述符。创建 socket 实际上是在请求操作系统开启网络 I/O 端口。

socket() 函数的原型

定义在头文件 <sys/types.h> 和 <sys/socket.h> 中,函数定义如下:

#include <sys/types.h>
#include <sys/socket.h>int socket(int domain, int type, int protocol);

函数参数详解:

- domain: 指定 socket 所使用的协议族 (例如 AF_INET 用于 IPv4, AF_INET6 用于 IPv6, AF_UNIX 用于本地通信)。
- type: 指定 socket 类型,常见的有 SOCK_STREAM(流式socket,通常用于 TCP)和 SOCK_DGRAM(数据报式socket,通常用于 UDP)。

- protocol: 指定在 domain 和 type 的基础上的具体协议。通常设为 0 表示选择默认协议 (例如,在 AF_INET 下使用 SOCK_STREAM 则默认使用 TCP 协议)。

调用 socket() 函数后,它会返回一个整数值 —— socket 文件描述符。如果成功创建,返回的描述符将作为后续所有基于该 socket 的操作的句柄。如果创建失败,返回 -1,并能通过 errno 获取具体的失败原因。

创建 TCP socket 的简单例子(IPv4):

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h> // for closeint main() {int sockfd;// 创建 socketsockfd = socket(AF_INET, SOCK_STREAM, 0);// 检查是否成功创建if (sockfd == -1) {perror("Failed to create socket");exit(EXIT_FAILURE);}printf("Socket created successfully with fd: %d\n", sockfd);// 使用结束后关闭 socketclose(sockfd);return 0;
}

在这个例子中,我们创建了一个用于 IPv4 网络通信的流式 socket(即 TCP socket)。创建完后,我们检查了返回的文件描述符,看是否创建成功。如果创建不成功,使用 perror 打印出错误信息。创建成功后,可以使用返回的文件描述符来配置 socket,例如通过 bind、`listen`、`accept`、`connect` 等系统调用进行进一步的网络操作。使用完毕后,用 close() 系统调用关闭文件描述符,清理资源。

三、详解绑定地址到 Socket(bind())

函数 bind() 在 socket 编程中的作用是将一个本地地址绑定到指定的 socket 文件描述符上。对于 TCP 服务器来说,这一步骤是必需的,因为它定义了服务的地址和端口,客户端需要这些信息来能够连接到服务器。对于 UDP 协议,`bind()` 用于指定一个端口用于监听传入的数据报。

下面是 bind() 函数的一般用法和详细解释:

原型

在 <sys/socket.h> 头文件中,`bind()` 函数的原型如下:

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

sockfd:socket 文件描述符,由之前调用 socket() 函数返回。
addr:指向 sockaddr 结构体或其子类型(如 sockaddr_in 用于 IPv4 地址)的指针,持有你想要绑定给 socket 的地址信息。

addrlen:`addr` 指针指向的地址的字节长度。

参数

关于参数的详细解释如下:

1. sockfd:这是调用 socket() 函数时得到的 socket 描述符,它代表一个 socket 实例。

2. addr:这是一个指向 sockaddr 结构体的指针,它定义了要绑定的地址包括 IP 地址和端口号。在实际编程中,通常使用特定于协议的地址结构体(如 IPv4 的 sockaddr_in),并且在传递给 bind() 之前转换为 sockaddr 类型的指针。

3. addrlen:这表示 addr 结构体的大小,确保 bind() 函数可以正确地读取或解释地址结构体中的信息。

地址结构体(IPv4 为例)

对于 IPv4 地址,`sockaddr_in` 结构体的定义如下:

struct sockaddr_in {short            sin_family;   // 地址族(如 AF_INET)unsigned short   sin_port;     // 端口号(使用网络字节顺序)struct in_addr   sin_addr;     // IP 地址char             sin_zero[8];  // 填充 0 以保持结构体大小与 sockaddr 一致
};

在使用时,你通常会填充 sockaddr_in 结构体,然后将其转换为 sockaddr 结构类型的指针传递给 bind() 函数。

使用示例

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>int main() {// 创建 socketint sockfd = socket(AF_INET, SOCK_STREAM, 0);// 创建 sockaddr_in 结构体来指定地址和端口struct sockaddr_in addr;memset(&addr, 0, sizeof(addr));  // 初始化结构体addr.sin_family = AF_INET;       // 指定地址族addr.sin_port = htons(8080);     // 指定端口号addr.sin_addr.s_addr = INADDR_ANY; // 使用任意可用地址或指定具体 IP// 绑定 socket 到地址和端口if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {// 错误处理close(sockfd);return -1;}// 其他操作...// 关闭 socketclose(sockfd);return 0;
}

在上面的代码中,`sockfd` 是一个有效的 socket 描述符,`addr` 结构体填充了所需的 IP 地址和端口信息,然后通过 bind() 函数与 sockfd 绑定。常量 INADDR_ANY 用于绑定到所有可用的接口上,端口号由 htons() 函数转换为网络字节顺序。

如果 bind() 调用成功,则返回 0;如果失败,则返回 -1 并设置全局变量 errno 以指示错误。常见的错误代码包括 EACCES(表示没有权限绑定指定端口)、`EADDRINUSE`(指定的地址或端口已在使用中)、`EBADF`(非法的文件描述符)等。

四、详解监听连接(listen())

listen() 函数在 Socket 编程中用于将一个未连接的 socket 转换成一个被动的监听 socket,指示内核应当接受指向该 socket 的连接请求。这个函数特别适用于服务器端的 socket,在客户端尝试建立连接之前,服务器需要明确准备好接受连接。

 listen() 函数的原型:

int listen(int sockfd, int backlog);

参数说明:

sockfd:这是 socket() 函数调用成功后返回的文件描述符。它代表一个打开的 socket,服务器将通过该 socket 接收客户端的连接请求。

backlog:这个参数指定了内核中未处理连接请求的最大数量。具体来说,这个参数定义了内核应该为相应 socket 队列的最大长度。当队列满时,会拒绝新的连接请求。不同的系统对这个数值的限制不同,当你设置的值超过系统最大值时,系统会将其调整为最大值。

函数行为:

- 当 listen() 调用成功时,这个 socket 将可以接收连接请求,通常紧接着会使用 accept() 函数来等待和响应实际的连接请求。

- 当 listen() 调用失败时,它会返回 -1,并设置全局变量 errno 来指明错误原因。

在实际编程中,你通常需要先通过 socket() 创建一个 socket,再通过 bind() 函数将其绑定到本地地址和端口,最后使用 listen() 函数来开始监听连接请求。

下面是这个步骤的示例代码:

#include <sys/types.h>
#include <sys/socket.h>int main() {int sockfd;struct sockaddr_in addr;// 创建 socketsockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd == -1) {// 错误处理}// 初始化地址结构体addr.sin_family = AF_INET;         // 使用 IPv4addr.sin_port = htons(8080);       // 设置端口号 (e.g., 8080)addr.sin_addr.s_addr = INADDR_ANY; // 监听所有地址// 绑定 socket 到指定的地址和端口if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {// 错误处理}// 开始监听连接请求if (listen(sockfd, 10) == -1) { // 设置 backlog 为 10// 错误处理}// ... 接下来可以接受连接和处理数据return 0;
}

当服务器调用 listen() 之后,操作系统会处理所有到来的网络连接请求。如果接受请求的队列被填满,则新的连接请求可能会被忽略或者被拒绝,这取决于操作系统如何处理这种情况。通常,服务器会在多线程或者异步的环境下运行,来高效地处理多个客户端的连接。

五、详解接受连接(accept())

accept() 函数在服务器端用于接受客户机的连接请求。在调用 accept() 之前,服务器应该已经使用 socket() 创建好一个套接字(socket),然后使用 bind() 将其绑定到一个本地地址和端口上,最后用 listen() 设置此套接字为监听模式,准备接收客户端发起的连接请求。

函数原型如下:

#include <sys/types.h>
#include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

sockfd:这是调用 socket() 函数时返回的套接字文件描述符,并且已经通过 bind() 和 listen() 函数绑定地址并且监听。
addr:这是一个指向 sockaddr 结构的指针,该结构用于返回连接方的协议地址(即客户端的地址)。在实际应用中,这通常指向一个 sockaddr_in 或 sockaddr_in6 结构(根据是 IPv4 还是 IPv6)。

addrlen:这是一个指向 socklen_t 类型变量的指针,在调用 accept() 之前,`*addrlen` 应该被设置为 addr 指向结构的大小。函数返回后,`*addrlen` 被设置为实际的地址长度。

调用 accept() 函数时,如果存在待处理的连接请求,它会创建一个新的已连接套接字,并从队列中移除该请求。`accept()` 返回一个新的文件描述符来指代这个连接。这个新的文件描述符完全独立于原来监听的 sockfd,应该用于后续的数据发送和接收操作。原来的 sockfd 仍然保持打开着,可以继续用于接受其他连接请求。

如果 addr 和 addrlen 非空,`accept()` 会在 addr 指向的结构中填充连接客户端的地址和端口信息,而 addrlen 会被设置为该结构体的实际长度。如果你对客户端的地址不感兴趣,可以将这两个参数设置为 NULL

在默认情况下,`accept()` 是阻塞的,这意味着如果没有客户端连接请求,调用 accept() 的程序将会挂起,直到有一个连接请求到来。如果想要 accept() 在没有连接请求时不阻塞程序,可以将监听套接字设置为非阻塞模式。

还要注意的是,`accept()` 可以在多线程或者多进程的服务器中被调用,以便同时处理多个连接请求。此时,必须确保对于每个连接都会有一个独立的线程或进程负责处理。

当 accept() 成功时,会返回一个非负的文件描述符用于操作这个连接。如果出错,则返回 -1,并设置 errno 以指示错误类型。

六、详解发起连接(connect())

connect() 函数在客户端用于建立与服务器端的连接。连接建立后,客户端和服务器就可以开始通信。该函数定义在 <sys/types.h> 和 <sys/socket.h> 头文件中,通常用于 SOCK_STREAM 类型的 socket(如 TCP 连接)。

函数原型:

#include <sys/types.h>
#include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数说明:

- sockfd: 调用 socket() 函数时返回的文件描述符,代表了客户端用于尝试连接的本地 socket。

- addr: 指向 struct sockaddr 结构体的指针,该结构体包含了目标服务器的地址和端口信息。对于不同的地址类型(如 IPv4、IPv6),该结构体有不同的具体实现,分别为 struct sockaddr_in 和 struct sockaddr_in6

- addrlen: addr 结构体的长度,用字节为单位。

函数返回值:

- 返回 0,表示连接成功。

- 返回 -1,表示连接失败,并设置 errno 以指示错误类型。

实际使用中,需要先准备好服务器的地址信息:

struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;           // AF_INET 表示 IPv4
server_addr.sin_port = htons(port);         // 设置服务器端口,并转化为网络字节序
server_addr.sin_addr.s_addr = inet_addr("服务器 IP 地址"); // 将 IP 地址转化为网络字节序

之后,可以调用 connect() 函数尝试建立连接:

int status = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (status == -1) {perror("connect failed");// 错误处理// 根据 errno 的不同值处理不同的错误情况
}

当 connect() 函数调用成功端,此时 socket 已经与服务器建立了 TCP 连接,可以通过 read/write 或 send/recv 函数进行数据传输。

在阻塞模式下(默认情况),如果连接无法立即建立,`connect()` 会阻塞当前线程直到连接完成,或者发生错误(如超时)。可以通过将 socket 设置为非阻塞来改变这个行为。

如果是非阻塞 socket 的话,`connect()` 函数调用可能立即返回 -1,并且 errno 设置为 EINPROGRESS,表示连接正在进行中。此时,你可以使用 select()、`poll()` 或 epoll() 等函数来检测连接是否成功,或者使用 getsockopt() 来检查 socket 的 SO_ERROR 选项来获取实际的错误代码。

七、详解数据传输(send() / recv() 或 write() / read())

数据传输是 Socket 编程中数据在建立的连接上进行读取和发送的过程。在 TCP 套接字中,这个过程是可靠的,保证数据按序到达;在 UDP 套接字中,则是不可靠的,数据可能会丢失或者顺序被改变。下面详细介绍 TCP 套接字的数据传输函数。

TCP 数据传输

对于 TCP 协议(`SOCK_STREAM`)的套接字,`send()` 和 recv() 函数经常被用来发送和接收数据。

1. send() 函数:

   send() 函数用于向 TCP 连接的另一端发送数据。

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

   - sockfd 是要发送数据的套接字文件描述符。
   - buf 是指向要发送数据的缓冲区的指针。
   - len 是要发送数据的字节数。

   - flags 通常设置为0;其它可选值可以提供额外的控制,如 MSG_DONTROUTE

   这个函数返回值是实际发送的字节数,或者在出错时返回-1。

2. recv() 函数:

   recv() 函数用于从 TCP 连接的另一端接收数据。

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

   - sockfd 是要接收数据的套接字文件描述符。
   - buf 是指向用于接收数据的缓冲区的指针。
   - len 是缓冲区的大小,即最大可接收的字节数。

   - flags 同样通常设置为0,提供特定控制如 MSG_PEEK(预览数据而不移除队列中的数据)等。

   recv() 返回实际接收到的字节数,如果连接已经正常关闭,则返回0;出错时返回-1。

UDP 数据传输

虽然 send() 和 recv() 函数同样可以用于 UDP 协议(`SOCK_DGRAM`)的套接字,但是对于 UDP 更常用的是 sendto() 和 recvfrom() 函数,因为 UDP 具有连接无关的特征,没有固定的连接状态。

1. sendto() 函数:用于发送数据到特定的地址。
   ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

2. recvfrom() 函数:用于从特定地址接收数据。
   ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

write() 和 read() 函数

write() 和 read() 是更一般的 POSIX 文件操作函数,也可以用于 socket 数据传输,这是因为在Unix和类Unix系统中,一切皆文件。

1. write() 函数:

用于向文件描述符写入数据,它可以用于 TCP socket 和文件的写操作。

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

2. read() 函数:

用于从文件描述符读取数据,同样适用于 TCP socket 和读取普通文件。

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

在处理socket传输时,一个重要的实践是适当的错误处理和重试机制。网络传输可能会因为各种原因导致发送和接收不完全,因此编写健壮的代码需要处理这些问题,如使用循环确保所有数据都发送/接收成功,或处理丢包和超时情况。

八、详解断开连接(close())

在 Linux socket 编程中,`close()` 函数用于关闭一个已经打开的 socket 连接。当你完成了数据的发送和接收,不再需要这个 socket 时,应当关闭它以释放系统资源。

在 TCP 连接中,`close()` 函数的执行将启动 TCP 连接的终止过程,通常称为四次挥手(four-way handshake)。详细来说,`close()` 会使得执行了关闭操作的一方(通常是客户端或服务器端的应用程序)发送一个 FIN(结束)信号,以通知另一方它已经完成了发送数据。

四次挥手的过程如下:

1. 第一次挥手:关闭操作的一方发送一个 FIN 包给对方,表明它已经没有数据要发送了。

2. 第二次挥手:接收到 FIN 包的一方会发送一个 ACK (确认) 包作为响应。

3. 第三次挥手:接收 FIN 的一方在完成它自己的数据发送后,会发送一个自己的 FIN 包。

4. 第四次挥手:最初发送 FIN 的原始关闭方收到这个 FIN 包后,发送一个 ACK 包作为最终确认,然后关闭这个连接。

一旦这个过程完成,连接就被完全关闭了。然而,对于原始调用 close() 函数的一方来说,这个函数通常会立即返回,不会等待整个四次挥手过程结束。

另外,需要注意以下几点:

- 半关闭(Half-close):TCP 提供的是全双工的服务,意味着数据可以在两个方向上同时传输。调用 close() 表示应用层不再发送数据,但是依然可以接收数据直到收到对方的 FIN 包。

- TIME_WAIT 状态:在 TCP 连接完全关闭后,关闭端口的一方(通常是发起连接的客户端)会进入 TIME_WAIT 状态。在这个状态下,端口需要等待一段时间(通常是 2MSL,其中 MSL 是最大报文生存时间)以确保对方收到了最后一个 ACK 包。这个机制确保了在相同的端口上快速重用连接是安全的,并且处理了可能在网络中延迟的旧数据包。

- 引用计数:在某些系统中,当多个进程或线程共享同一个 socket 文件描述符时,这个描述符会有一个引用计数。在所有拥有这个文件描述符的进程或线程中,`close()` 需要被调用相应次数才能真正关闭连接。

- shutdown() 函数:此外,还有一个 shutdown() 函数可以用来关闭连接。与 close() 不同的是,`shutdown()` 允许你执行部分关闭,即关闭读和/或写的一部分。这对于实现半关闭非常有用。

关闭 socket 连接时还需要处理可能产生的异常和错误,比如网络中断或对端突然关闭连接等情况。正确地管理 socket 的关闭操作对于保持应用程序和系统稳定性是非常重要的。


 

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

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

相关文章

RocketMQ5.0顺序消息设计实现

前言 顺序消息是 RocketMQ 提供的一种高级消息类型&#xff0c;支持消费者按照发送消息的先后顺序获取消息&#xff0c;从而实现业务场景中的顺序处理。 顺序消息的顺序关系通过消息组&#xff08;MessageGroup&#xff09;判定和识别&#xff0c;发送顺序消息时需要为每条消息…

RocketMQ5.0Pop消费模式

前言 RocketMQ 5.0 消费者引入了一种新的消费模式&#xff1a;Pop 消费模式&#xff0c;目的是解决 Push 消费模式的一些痛点。 RocketMQ 4.x 之前&#xff0c;消费模式分为两种&#xff1a; Pull&#xff1a;拉模式&#xff0c;消费者自行拉取消息、上报消费结果Push&#x…

分布式(8)

目录 36.什么是TCC&#xff1f; 37.分布式系统中常用的缓存方案有哪些&#xff1f; 38.分布式系统缓存的更新模式&#xff1f; 39.分布式缓存的淘汰策略&#xff1f; 40.Java中定时任务有哪些&#xff1f;如何演化的&#xff1f; 36.什么是TCC&#xff1f; TCC&#xff08…

【算法挨揍日记】day41——【模板】01背包、416. 分割等和子集

【模板】01背包_牛客题霸_牛客网你有一个背包&#xff0c;最多能容纳的体积是V。 现在有n个物品&#xff0c;第i个物品的体积为 ,。题目来自【牛客题霸】https://www.nowcoder.com/practice/fd55637d3f24484e96dad9e992d3f62e?tpId230&tqId2032484&ru/exam/oj&qru…

HarmonyOS-ArkTS基本语法及声明式UI描述

初识ArkTS语言 ArkTS是HarmonyOS优选的主力应用开发语言。ArkTS围绕应用开发在TypeScript&#xff08;简称TS&#xff09;生态基础上做了进一步扩展&#xff0c;继承了TS的所有特性&#xff0c;是TS的超集。因此&#xff0c;在学习ArkTS语言之前&#xff0c;建议开发者具备TS语…

机器学习常用算法模型总结

文章目录 1.基础篇&#xff1a;了解机器学习1.1 什么是机器学习1.2 机器学习的场景1.2.1 模式识别1.2.2 数据挖掘1.2.3 统计学习1.2.4 自然语言处理1.2.5 计算机视觉1.2.6 语音识别 1.3 机器学习与深度学习1.4 机器学习和人工智能1.5 机器学习的数学基础特征值和特征向量的定义…

软件测试/测试开发丨Python 模块与包

python 模块与包 python 模块 项目目录结构 组成 package包module模块function方法 模块定义 定义 包含python定义和语句的文件.py文件作为脚本运行 导入模块 import 模块名from <模块名> import <方法 | 变量 | 类>from <模块名> import * 注意&a…

小红书如何高效引流?

近年来&#xff0c;公域流量价格不断上涨&#xff0c;私域流量的优势逐渐凸显。企业正花费大量资源和成本来获取新流量&#xff0c;但与其如此&#xff0c;不如将精力放在留存和复购上&#xff0c;从而实现业绩的新增长。其中关键在于如何有效地将公域流量转化为私域流量。 然而…

境内深度合成服务算法备案清单(2023年12月)

截止2024年1月3日&#xff0c;第三批深度合成服务算法备案信息的公告尚未发布&#xff0c;预计将会在2024-1-10左右发布&#xff0c;我公司已知晓部分公示名单&#xff0c;如中国电信数字人生成算法&#xff0c;详情联系WX号&#xff1a;SuanfabeiandayuAI生成合成类算法应办理…

ArkTS - @Prop、@Link

一、作用 Prop 装饰器 和Link装饰器都是父组件向子组件传递参数&#xff0c;子组件接收父组件参数的时候用的&#xff0c;变量前边需要加上Prop或者Link装饰器即可。&#xff08;跟前端vue中父组件向子组件传递参数类似&#xff09; // 子组件 Component struct SonCom {Prop…

管程-第三十三天

目录 为什么要引入管程 管程的定义和基本特征 用管程解决生产者消费者问题 结论 本节思维导图 为什么要引入管程 原因&#xff1a;在解决进程的同步与互斥问题时&#xff0c;信号量机制存在编写困难和易出错的问题 能不能设计一种机制&#xff0c;让程序员写程序时不再需…

openGauss学习笔记-184 openGauss 数据库运维-升级-升级验证

文章目录 openGauss学习笔记-184 openGauss 数据库运维-升级-升级验证184.1 验证项目的检查表184.2 升级版本查询184.2.1 验证步骤 184.3 检查升级数据库状态184.3.1 验证步骤 openGauss学习笔记-184 openGauss 数据库运维-升级-升级验证 本章介绍升级完成后的验证操作。给出验…

VINS-MONO拓展1----手写后端求解器,LM3种阻尼因子策略,DogLeg,构建Hessian矩阵

文章目录 0. 目标及思路1. 非线性优化求解器2. 基于VINS-MONO的Marginalization框架构建Hessian矩阵2.1 estimator.cpp移植2.2 solve.cpp/preMakeHessian()2.3 solve.cpp/makeHessian() 3. solve.cpp/solveLinearSystem()求解正规方程4. 更新状态5. 迭代求解6. EVO评估结果7. 待…

虹科方案丨从困境到突破:TigoLeap方案引领数据采集与优化变革

来源&#xff1a;虹科工业智能互联 虹科方案丨从困境到突破&#xff1a;TigoLeap方案引领数据采集与优化变革 原文链接&#xff1a;https://mp.weixin.qq.com/s/H3pd5G8coBvyTwASNS_CFA 欢迎关注虹科&#xff0c;为您提供最新资讯&#xff01; 导读 在数字化工厂和智能制造时…

connection refused

nohup /home/bavon/miniconda3/envs/SLFCD/bin/python -m visdom.server -port 8098 >/home/bavon/logs/visdom.log 2>&1 &

8086CPU的寻址方式(7种)

基本概念 立即操作数&#xff1a;操作数包含在指令中寄存器操作数&#xff1a;操作数包含在CPU的某个内部寄存器中存储器操作数&#xff1a;约定操作数事先存放在存储器中存放数据的某个单元基本格式 MOV xx,yy xx&#xff1a;目的操作数字段 yy&#xff1a;源操作数字段 EA&a…

whl is not a supported wheel on this platform.解决办法

1.问题&#xff1a; 安装torch产生 2.解决办法&#xff1a; 使用pip debug --verbose查看 对应的torch版本号 Compatible tags字样&#xff0c;这些就是当前Python版本可以适配的标签。例如&#xff0c;我的Python版本是3.11&#xff0c;可以匹配下面这些文件名&#xff1a;…

Nginx多域名部署多站点

目录 1.修改配置文件nginx.conf 2. 修改hosts文件 1.修改配置文件nginx.conf 在配置文件的 server_name 处修改成自己需要的域名&#xff0c;然后保存退出 j 查看语法是否错误&#xff0c;然后重启nginx nginx -t # 查看语法是否正确 systemctl restart nginx # 重启nginx …

【面试】面向对象编程的三大概念(实例辅助记忆)

【面试】面向对象编程的三大概念&#xff08;实例辅助记忆&#xff09; 虑面向对象编程的三大特性&#xff0c;它们是&#xff1a; 封装&#xff08;Encapsulation&#xff09;&#xff1a; 将对象的状态和行为封装在一起&#xff0c;对外部隐藏对象的内部实现细节。这样可以防…

Power Automate删除SharePoint Online或OneDrive for Business文件版本历史

SharePoint Online和OneDrive for Business支持版本控制&#xff0c;可以保留文件的版本历史&#xff0c;方便用户随时查看和恢复以前的版本。但该功能也会占用大量SharePoint Online或OneDrive for Business存储空间。官方删除版本历史的方法无法批量操作&#xff0c;故今天提…