【C语言】Linux socket 编程

news2024/9/21 14:36:00

一、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 close

int main() {
    int sockfd;

    // 创建 socket
    sockfd = 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);

    // 使用结束后关闭 socket
    close(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() {
    // 创建 socket
    int 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;
    }

    // 其他操作...

    // 关闭 socket
    close(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;

    // 创建 socket
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {
        // 错误处理
    }

    // 初始化地址结构体
    addr.sin_family = AF_INET;         // 使用 IPv4
    addr.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.coloradmin.cn/o/1354266.html

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

相关文章

canvas绘制直角梯形(向右)

查看专栏目录 canvas示例教程100专栏&#xff0c;提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重…

PTA QQ帐户的申请与登陆

QQ帐户的申请与登陆 分数 25 全屏浏览题目 作者 DS课程组 单位 浙江大学 实现QQ新帐户申请和老帐户登陆的简化版功能。最大挑战是&#xff1a;据说现在的QQ号码已经有10位数了。 输入格式: 输入首先给出一个正整数N&#xff08;≤105&#xff09;&#xff0c;随后给出N行…

微服务-Gateway

案例搭建 官网地址 父Pom <com.alibaba.cloud.version>2.2.8.RELEASE</com.alibaba.cloud.version> <com.cloud.version>Hoxton.SR12</com.cloud.version> <com.dubbo.version>2.2.7.RELEASE</com.dubbo.version> <dependencyManagem…

Python基础-05(输出输入、if、if else和elif)

文章目录 前言一、输出&#xff08;print()&#xff09;和输入&#xff08;input()&#xff09;二、if、if else、elif1.if2.if else3.关于输入input的默认值4.elif 前言 今天复习一些非常基础的内容&#xff0c;以及if、if else和elif语句 一、输出&#xff08;print()&…

【vue/uniapp】pdf.js 在一些型号的手机上不显示

引入&#xff1a; uniapp 项目通过 pdf.js 来在线浏览 pdf 链接&#xff0c;在微信小程序中都显示正常&#xff0c;但是通过 app 跳转小程序&#xff0c;在苹果、小米显示正常&#xff0c;但是华为和 oppo 就不显示&#xff0c;可以通过降 pdf.js 的版本来解决这个问题。 解决&…

1.3 力扣二叉树中等题

题目一&#xff1a; 669. 修剪二叉搜索树 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树&#xff0c;使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即&#xff0c;如果没有被移除&…

python flask图书管理系统带文档

python flask图书管理系统带文档。功能&#xff1a;登录&#xff0c;图书的增删改查&#xff0c;读者管理&#xff0c;借阅记录&#xff0c;有文档。 技术&#xff1a;python3,flask,mysql,html。 包含源码数据库文件文档。 源码下载地址&#xff1a; https://download.csd…

Calibre PEX Hspice Netlist提取步骤(数模芯片提取spice netlist流程)

在数模混合芯片中&#xff0c;通常模拟需要数字模块通过calibre工具来提取Hspice netlist用于功耗仿真。注意这里的spice netlist和做Calibre的spice netlist是不太一样的。 另外在做calibre pex时需要确保当前的design LVS已经pass。否则功耗仿真可能会不准。 Calibre LVS常…

Z-score 因子的深入思考

最新&#xff08;2024 年 1 月&#xff09;出版的 SC 技术分析&#xff08;Techical Analysis of Stock & Commodities&#xff09;的第 4 条文章给到了 Z-score&#xff0c;原文标题为《Z-score: How to use it in Trading》。今天的笔记&#xff0c;就借此机会&#xff0…

在日常工作中如何保障服务器的安全?

服务器在日常工作中具有重要的作用。它是网络的核心组成部分&#xff0c;承担着提供信息和服务的任务。 1.服务器为各种应用提供数据存储和处理服务&#xff0c;支持电子邮件、网页浏览、文件下载等服务&#xff0c;为用户提供高效、安全、可靠的网络访问。 2.服务器承载着各…

uni-app中实现元素拖动

uni-app中实现元素拖动 1、代码示例 <template><movable-area class"music-layout"><movable-view class"img-layout" :x"x" :y"y" direction"all"><img :src"musicDetail.bgUrl" :class&…

Linux mcd命令教程:如何在MS-DOS文件系统中切换工作目录(附实例教程和注意事项)

Linux mcd命令介绍 mcd是mtools工具的指令&#xff0c;它用于在MS-DOS文件系统中切换工作目录。如果不加任何参数&#xff0c;它将显示当前所在的磁盘和工作目录。 Linux mcd命令适用的Linux版本 mcd命令在所有主流的Linux发行版中都可以使用&#xff0c;包括但不限于Ubuntu…

Canvas保姆级教程----深入解析HTML5 Canvas工作原理和常用方法

&#x1f4e2; 鸿蒙专栏&#xff1a;想学鸿蒙的&#xff0c;冲 &#x1f4e2; C语言专栏&#xff1a;想学C语言的&#xff0c;冲 &#x1f4e2; VUE专栏&#xff1a;想学VUE的&#xff0c;冲这里 &#x1f4e2; CSS专栏&#xff1a;想学CSS的&#xff0c;冲这里 &#x1f4…

01、Kafka ------ 下载、安装 ZooKeeper 和 Kafka

目录 Kafka是什么&#xff1f;安装 ZooKeeper下载安装启动 zookeeper 服务器端启动 zookeeper 的命令行客户端工具 安装 Kafka下载安装启动 Kafka 服务器 Kafka是什么&#xff1f; RabbitMQ的性能比ActiveMQ的性能有显著提升。 Kafka的性能比RabbitMQ的性能又有显著提升。 K…

ngrok-内网穿透

一、访问官网下载相关的内容 Download (ngrok.com) linux或者windows可供选择&#xff0c;主要在于你的项目跑在什么地方 选择下载 二、获取Authtoken 点击右上角登录&#xff0c;没有号用谷歌账号登录即可跳转 三、在Windows进行内网穿透 下载打开 打开 运行在命令行中&am…

【42页动态规划学习笔记分享】动态规划核心原理详解及27道LeetCode相关经典题目汇总

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能AI、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推荐--…

【Spring】19 AOP介绍及实例详解

文章目录 1. 定义1&#xff09;什么意思呢&#xff1f;2&#xff09;如何解决呢&#xff1f; 2. 基本概念1&#xff09;切面&#xff08;Aspect&#xff09;2&#xff09;切点&#xff08;Pointcut&#xff09;3&#xff09;通知&#xff08;Advice&#xff09;4&#xff09;连…

【python入门】day17:模块化编程、math库常见函数

什么叫模块 模块的导入 导入所有&#xff1a;import 模块名称 导入指定&#xff1a;from 模块名称 import 函数/变量/类 python的math库 什么是math库 Python的math库是Python的内建库之一&#xff0c;它提供了许多数学函数&#xff0c;包括三角函数、对数函数、幂函数等&a…

迅为RK3588开发板使用 FFMpeg 进行推流

Debian/Ubuntu 系统使用以下命令安装 FFMpeg &#xff0c;如下图所示&#xff1a; apt-get install ffmpeg 使用 ifconfig 查看开发板 ip 为 192.168.1.245 如下图所示&#xff1a; 使用 FFMpeg 推流一个 mp4 视频进行测试&#xff0c;作者将测试视频 test.mp4 放在了根目录下…

linuxnodejs 20.* 安装问题,version `GLIBCXX_3.4.26‘

背景 今天服务器被重置拉&#xff0c;nodejs 环境不存在&#xff0c;特意安装下nodejs&#xff0c;一访问官网&#xff0c;妈呀&#xff0c;居然到20版本拉&#xff01;就尝试安装下最新版本&#xff01; 过程 $ cd /opt $ curl -OL https://nodejs.org/dist/v20.10.0/node-v2…