【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 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 的关闭操作对于保持应用程序和系统稳定性是非常重要的。


 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【算法】利用分治思想解算法题:快排、归并、快速选择实战(C++)

1. 分治思想 介绍 分治法将问题划分成多个相互独立且相同或类似的子问题&#xff0c;然后递归地解决每个子问题&#xff0c;并将结果合并以得到原始问题的解。 分治思想通常包含以下三个步骤&#xff1a; 分解&#xff1a;将原始问题划分成多个规模较小、相互独立且类似的子…

企业如何利用好数据,让数据真正成为数据资产?数据资产管理应该怎样建设?

数字化时代&#xff0c;数据已经成为了个人、机构、企业乃至国家的重要战略资产。 近日&#xff0c;财政部正式对外发布《企业数据资源相关会计处理暂行规定》&#xff0c;并自 2024 年 1 月 1 日开始施行。数据资产入表政策落地节奏超预期&#xff0c;标志着国家把数据作为生…

如何用python实时监控股票,并且持续扫描大盘?

用 Python 抓取分析股市数据很简单&#xff01;只用短短几行代码&#xff0c;就能实现策略制定到交易信号生成。 一、数据准备 在分析的最开始&#xff0c;需要获取数据。本文中将以沪深 300 指数为标的进行分析&#xff08;包含日期、开高低收价、成交量、成交额字段&#xf…

MySQL之四大引擎、账号管理以及建库认识

目录 一、数据库存储引擎&#xff08;发动机&#xff09; 1.1、认识引擎 1.2、查看存储引擎 1.3、引擎常识 1.4、support字段说明 1.5、四大引擎 二、数据库管理 2.1、元数据库介绍&#xff1a; 2.2、分类&#xff1a; 2.3、增删改查以及使用操作 2.4、权限 三、数…

【面试高频算法解析】算法练习2 回溯

目录 前言算法解析练习题组合总和全排列II单词搜索 前言 本篇章开放目的是按算法类型学习算法&#xff0c;学习对应算法理论&#xff0c;并通过练习一些经典算法题深入理解这类算法&#xff0c;避免出现刷了很多算法题&#xff0c;还是一知半解的状态 算法解析 回溯&#xff…

UDP通信(服务器-客户端)

一、 UDP服务器-客户端通信 UDP&#xff08;User Datagram Protocol&#xff09;是一种面向无连接的传输层协议&#xff0c;它提供了一种简单的、不可靠的数据传输服务。与TCP&#xff08;Transmission Control Protocol&#xff09;不同&#xff0c;UDP不建立连接&#xff0c;…

FusionAccess配置Lite AD

1、Lite AD的安装及配置 Lite AD流程&#xff1a; &#xff08;1&#xff09;创建一个新的Windows 10&#xff0c;安装tools&#xff0c;再安装ITA组件&#xff08;安装Lite AD会自动安装VAG/VLB&#xff09; &#xff08;2&#xff09;创建一个新的Windows 10&#xff0c;安…

线性规划中解的关系

写于&#xff1a;2024年1月2日星期二 修改于&#xff1a; 本文从两个角度对线性规划中的解做划分&#xff0c;角度一是将解划为基解、基可行解、可行解&#xff1b;角度二是将解划分为无可行解、无界解、最优解&#xff08;唯一和无穷多&#xff09;。同时&#xff0c;详细描述…

【计算机视觉网络训练技巧】你知道你拿什么图片在训练吗?训练图片可视化简易版

以下是一张图片&#xff0c;数据增广之后的示意图&#xff1a; 问题是这样的&#xff0c;当数据增广后&#xff0c;我们怎么知道图片变成什么样了呢&#xff0c;或者说我们输入到网络中的图片长什么样&#xff1f;对&#xff0c;解法很简单&#xff0c;就是在图片输入到网络时…

C++的基础语句

C前奏 1.变量的定义2.键入和输出3.运算符4.sizeof()函数5.判断6.goto语句7.总结 这个专题&#xff0c;我会用简单的语言介绍C的语法&#xff0c;并会适当的对比实现相同或相似功能的C与python代码写法上的不同。 1.变量的定义 对于python来说&#xff0c;我们可以跳过定义直接…

Efficient Classification of Very Large Images with Tiny Objects(CVPR2022补1)

文章目录 Two-stage Hierarchical Attention SamplingsummaryOne-stageTwo-Stage内存需求 Efficient Contrastive Learning with Attention Sampling Two-stage Hierarchical Attention Sampling summary 从一个大图像中按照指定的低分辨率比例和位置提取出一个小图块 一阶段…

web前端——clear可以清除浮动产生的影响

clear可以解决高度塌陷的问题&#xff0c;产生的副作用要小 未使用clear之前 <!DOCTYPE html> <head><meta charset"UTF-8"><title>高度塌陷相关学习</title><style>div{font-size:50px;}.box1{width:200px;height:200px;backg…

阿里云盘在线自动签到-无需部署

声明&#xff1a;本文的代码内容来源于知乎用户小猪猪和艾欧娜传播此内容是基于学术研究和学习目的&#xff0c;遵循了适用的版权规定和学术研究的合理使用原则。 作者只对源代码进行了一点点改动&#xff0c;本文主要演示如何使用金山文档的每日定时任务&#xff0c;执行阿里云…

nccl 源码安装与应用示例 附源码

1&#xff0c; 官方下载网址 注意&#xff0c;本文并不使用nv预编译的包来安装&#xff0c;仅供参考&#xff1a; NVIDIA Collective Communications Library (NCCL) | NVIDIA Developer 2&#xff0c;github网址 这里是nv开源的nccl源代码&#xff0c;功能完整&#xff0c;不…

Adobe Experience Design安装指南

XD&#xff08;Adobe Experience Design&#xff09;下载链接 https://pan.baidu.com/s/1MVcaE2GB1Q9YpgmgDxUGJw?pwd0531 1.鼠标右击【Adobe XD 55.1(64bit)】压缩包选择&#xff08;win11以上系统需先点击“显示更多选项”&#xff09;【解压到 Adobe XD 55.1(64bit)】。 …

《JVM由浅入深学习【四】 2023-12-24》JVM由简入深学习提升分享

JVM由简入深学习提升分享四 1.JVM中java堆的特点及作用2. JVM中对象如何在堆内存中分配3. JVM堆内存中的对象布局 1.JVM中java堆的特点及作用 是线程共享的一块区域虚拟机启动时就创建了是虚拟机中内存占用很大的一块存放所有的实例对象和数组GC主要的作用区域可分为新生代&am…

关于“Python”的核心知识点整理大全50

目录 python_repos.py 17.1.6 概述最受欢迎的仓库 python_repos.py 17.1.7 监视 API 的速率限制 注意 17.2 使用 Pygal 可视化仓库 python_repos.py 17.2.1 改进 Pygal 图表 python_repos.py 往期快速传送门&#x1f446;&#xff08;在文章最后&#xff09;&#xf…

09、docker 安装nacos并配置mysql存储配置信息

docker 安装nacos并配置mysql存储配置信息 1、docker启动nacos的各种方式2、Docker安装nacos3、MySQL中新建nacos的数据库4、挂载数据or配置目录5、运行 1、docker启动nacos的各种方式 内嵌derby数据源 docker run -d \ -e PREFER_HOST_MODEhostname \ -e SPRING_DATASOURCE_…

python旅游大数据分析可视化大屏 游客分析+商家分析+舆情分析 计算机毕业设计(附源码)Flask框架✅

毕业设计&#xff1a;2023-2024年计算机专业毕业设计选题汇总&#xff08;建议收藏&#xff09; 毕业设计&#xff1a;2023-2024年最新最全计算机专业毕设选题推荐汇总 &#x1f345;感兴趣的可以先收藏起来&#xff0c;点赞、关注不迷路&#xff0c;大家在毕设选题&#xff…

详解静态网页数据获取以及浏览器数据和网络数据交互流程-Python

目录 前言 一、静态网页数据 二、网址通讯流程 1.DNS查询 2.建立连接 3.发送HTTP请求 4.服务器处理请求 5.服务器响应 6.渲染页面 7.页面交互 三、URL/POST/GET 1.URL 2.GET 形式 3.POST 形式 四.获取静态网页数据 1.requests库 点关注&#xff0c;防走丢&am…