网络编程 | TCP套接字通信及编程实现经验教程

1、TCP基础铺垫

        TCP/IP协议簇中包含了如TCP、UDP、IP、ICMP、ARP、HTTP等通信协议。TCP协议是TCP/IP协议簇中最为常见且重要的通信方式之一,它为互联网上的数据传输提供了可靠性和连接管理。

        TCP(Transmission Control Protocol,传输控制协议)是面向连接的、可靠的、基于字节流的传输层通信协议。它主要用于在不可靠的互联网上提供可靠的数据传输。TCP被广泛应用于各种网络应用中,如Web浏览(HTTP/HTTPS)、电子邮件(SMTP、POP3、IMAP)、文件传输(FTP)等。

        TCP通信时,是一发一收的,即TCP的数据发出时,会进行一个等待确认操作,确认数据是否正常发出并被接收方接收到数据信息。

        TCP的关键特性:

(1)、面向连接:在数据传输之前,TCP需要通信双方建立一个连接。

(2)、可靠性:TCP采用了多种机制来确保数据的可靠传输,包括: 校验和 、序列号 、确认应答 、超时重传 、流量控制 、拥塞控制等技术。

(3)、全双工通信:TCP允许通信双方同时发送和接收数据,即双向通信可以在同一时间进行。

(4)、有序性:即使数据分段到达的顺序不同,TCP也能按照正确的顺序组装数据。

(5)、错误恢复:当发生错误或丢失数据时,TCP能够自动检测并尝试恢复丢失的数据。

(6)、连接终止:在数据传输完成后,TCP需要关闭连接。

(7)、多路复用:TCP支持在一个IP地址上通过不同的端口号区分多个应用程序或服务。

2、TCP关键技术梳理

(1)、TCP头部结构

(2)、三次握手

        在TCP连接建立之前,客户端和服务器之间需要进行三次握手来同步双方的序列号,并确认双方都准备好进行数据传输。

        第一次握手:客户端向服务器发送一个 SYN(同步序列编号)报文段,表示请求建立连接。客户端进入 SYN_SENT 状态。

        第二次握手:服务器收到 SYN 报文段后,回复一个 SYN-ACK(同步序列编号 + 确认)报文段,表示同意建立连接。服务器进入 SYN_RCVD 状态。

        第三次握手:客户端收到 SYN-ACK 报文段后,回复一个 ACK(确认)报文段,表示确认收到服务器的响应。客户端和服务器都进入 ESTABLISHED 状态,连接正式建立。

Client                                 Server|                                       ||  SYN (seq=0)                          ||-------------------------------------->||                                       | SYN_RCVD|  <------------------------------------||  SYN-ACK (ack=1, seq=0)               ||  ACK (seq=1, ack=1)                   ||-------------------------------------->||                                       | ESTABLISHED|  ESTABLISHED                          ||                                       |

(3)、四次握手

        当通信结束时,客户端或服务器可以发起断开连接的请求。断开连接的过程称为四次挥手,以确保双方都能正确关闭连接并释放资源。

        第一次挥手:主动关闭方(通常是客户端)发送一个 FIN(终止)报文段,表示不再发送数据。主动关闭方进入 FIN_WAIT_1 状态。

        第二次挥手:被动关闭方(通常是服务器)收到 FIN 报文段后,回复一个 ACK 报文段,表示确认收到 FIN。被动关闭方进入 CLOSE_WAIT 状态,而主动关闭方进入 FIN_WAIT_2 状态。

        第三次挥手:被动关闭方在处理完所有未完成的数据后,发送一个 FIN 报文段,表示自己也不再发送数据。被动关闭方进入 LAST_ACK 状态。

        第四次挥手:主动关闭方收到 FIN 报文段后,回复一个 ACK 报文段,表示确认收到 FIN。主动关闭方进入 TIME_WAIT 状态,等待一段时间(通常为2倍的最大报文段生命周期,即2MSL),以确保被动关闭方收到了最后的 ACK。之后,主动关闭方进入 CLOSED 状态,连接完全关闭。

Client                                 Server|                                       ||  FIN (seq=1)                          ||-------------------------------------->||  <------------------------------------||  ACK (ack=2)                          ||  FIN (seq=1)                          ||  <------------------------------------||  ACK (seq=2, ack=2)                   ||-------------------------------------->||                                       |

(4)、可靠传输

        TCP通过以下机制确保数据的可靠传输:

        序列号(Sequence Number):每个TCP报文段都有一个序列号,表示该报文段中的第一个字节在整个数据流中的位置。接收方可以根据序列号重新排序接收到的报文段,确保数据按顺序传递。

        确认应答(Acknowledgment, ACK):接收方在收到报文段后,会发送一个确认应答,告诉发送方哪些数据已经成功接收。发送方根据确认应答判断是否需要重传丢失或损坏的报文段。

        超时重传(Timeout and Retransmission):如果发送方在一定时间内没有收到确认应答,它会认为报文段可能丢失或延迟,并重新发送该报文段。TCP使用动态调整的超时机制来优化重传策略。

        流量控制(Flow Control):TCP使用滑动窗口机制来控制发送方的发送速率,确保接收方不会被过多的数据淹没。接收方会在确认应答中告知发送方当前可用的接收窗口大小,发送方根据这个信息调整自己的发送速率。

        拥塞控制(Congestion Control):TCP通过多种算法(如慢启动、拥塞避免、快速重传和快速恢复)来动态调整发送方的发送速率,避免网络拥塞。这些算法旨在在网络负载较高时减小发送速率,在网络条件改善时逐渐增加发送速率。

(5)、数据分段与重组

        TCP将应用层的数据分成多个较小的报文段(Segment),并通过IP层进行传输。每个报文段包含一个TCP头部和一部分应用层数据。接收方会根据报文段的序列号将它们重新组合成完整的数据流。

        最大报文段长度(MSS, Maximum Segment Size):为了提高传输效率并避免IP层的分片,TCP在连接建立时会协商一个合适的最大报文段长度。MSS通常由路径MTU(Maximum Transmission Unit)决定。

        分段与重组:如果应用层数据较大,TCP会将其分成多个报文段进行传输。接收方会根据序列号将这些报文段重新组合成原始的应用层数据。

(6)、连接管理

        TCP是一个面向连接的协议,这意味着在数据传输之前,必须先建立连接,传输结束后再关闭连接。TCP通过以下状态机来管理连接的生命周期:

  • LISTEN:服务器处于监听状态,等待客户端的连接请求。
  • SYN_SENT:客户端已发送 SYN 报文段,等待服务器的 SYN-ACK 响应。
  • SYN_RCVD:服务器已收到 SYN 报文段,等待客户端的 ACK 确认。
  • ESTABLISHED:连接已建立,双方可以开始传输数据。
  • FIN_WAIT_1:主动关闭方已发送 FIN 报文段,等待对方的 ACK 确认。
  • FIN_WAIT_2:主动关闭方已收到对方的 ACK,等待对方的 FIN。
  • CLOSE_WAIT:被动关闭方已收到对方的 FIN,等待应用程序关闭连接。
  • CLOSING:双方同时发送 FIN,等待对方的 ACK。
  • LAST_ACK:被动关闭方已发送 FIN,等待对方的 ACK。
  • TIME_WAIT:主动关闭方已收到对方的 FIN 和 ACK,等待2MSL后进入 CLOSED 状态。
  • CLOSED:连接已完全关闭,资源已释放。

(7)、带外数据(OOB)

        TCP支持带外数据传输,允许发送方发送紧急数据,而不必等待正常的TCP流排队。带外数据通常用于通知接收方有紧急事件发生,例如终止连接或重启服务。接收方可以通过SO_OOBINLINE选项将带外数据作为普通数据处理,或者通过 recv() 函数的 MSG_OOB 标志单独接收带外数据。

(8)、半关闭(Half-Close)

        TCP允许一方关闭连接的发送方向,而保持接收方向仍然打开。这种操作称为半关闭(Half-Close)。例如,客户端可以发送 FIN 报文段,表示不再发送数据,但仍然可以接收来自服务器的数据。服务器在收到 FIN 后,可以继续发送数据,直到它也发送 FIN 报文段,最终关闭整个连接。

3、TCP通信编程实现

3.1、TCP通信常用接口

        TCP实现网络通信的接口操作与文件操作惊人的相似,在对文件操作中,一般是先打开操作的文件描述符,然后对文件描述符配置相应的属性(如错误处理和多路复用等),设置完文件描述符的配置后,再进行对文件的读写操作,最后关闭文件描述符。而TCP网络通信时,也是先创建一个通信套接字,然后配置套接字属性及绑定、连接等操作,再进行数据的发送接收,最后关闭套接字。接下来这部分将重点介绍TCP网络通信的常用API。

        提醒:TCP通信接口API中,Windows和Linux的在使用上面存在一些区别,在下面主要介绍的是Linux平台下的API及使用。

(1)、socket
函数原型:#include <sys/socket.h>int socket(int domain, int type, int protocol);
函数功能:用于创建一个套接字的系统调用,它返回一个新的套接字描述符(一个非负整数),这个描述符可以用来进行后续的网络通信操作。
参数:domain:指定协议族(也称为地址族)AF_INET 或 PF_INET:IPv4 互联网协议AF_INET6 或 PF_INET6:IPv6 互联网协议AF_UNIX 或 PF_UNIX:本地通信(Unix域套接字)AF_ROUTE:路由套接字,用于与内核路由表交互...type:指定套接字类型SOCK_STREAM:提供面向连接、可靠的数据传输服务,使用TCP协议SOCK_DGRAM:提供无连接、不可靠的数据报服务,使用UDP协议SOCK_RAW:原始套接字,允许直接访问低层协议,如IP或ICMPSOCK_SEQPACKET:有序的、可靠的、双向传输的数据包服务,类似于 SOCK_STREAM,但以消息为单位SOCK_RDM:可靠的无连接数据报服务,保证消息按顺序到达,但不保证无重复...protocol:指定具体的协议设置为0,表示使用默认协议(根据domain和type自动选择)AF_INET 和 SOCK_STREAM,可以指定 IPPROTO_TCPAF_INET 和SOCK_DGRAM,可以指定IPPROTO_UDPAF_PACKET 和SOCK_RAW,可以指定具体的以太网协议,如ETH_P_IP ...
返回值:成功返回套接字的文件描述符(非0),失败返回-1,并设置 errno 变量来指示错误原因。
(2)、bind
函数原型:#include <sys/socket.h>int bind(int socket, const struct sockaddr *address,
socklen_t address_len);
函数功能:用于将一个本地地址(IP地址和端口号)绑定到一个已创建的套接字上。
参数:socket:要绑定地址的套接字描述符address:指向包含地址信息的sockaddr 结构体的指针对于 IPv4 (AF_INET),使用 struct sockaddr_in对于 IPv6 (AF_INET6),使用 struct sockaddr_in6对于 Unix 域套接字 (AF_UNIX),使用 struct sockaddr_unaddress_len:指定address指向的结构体的大小,以字节为单位对于 sockaddr_in 通常是sizeof(struct sockaddr_in)对于 sockaddr_in6 通常是sizeof(struct sockaddr_in6)
返回值:成功返回0,失败返回 -1,并设置 errno 变量来指示错误原因。
(3)、listen
函数原型:#include <sys/socket.h>int listen(int socket, int backlog);
函数功能:用于将一个未连接的套接字转换为监听套接字,使其能够接收传入的连接请求。
参数:socket:已经通过 bind() 绑定到一个本地地址的套接字backlog:指定监听队列的最大长度,即操作系统可以为该套接字排队的最大未接受连接数
返回值:成功返回0,失败返回 -1,并设置 errno 变量来指示错误原因。
(4)、accept
函数原型: #include <sys/socket.h>int accept(int socket, struct sockaddr *restrict address,
socklen_t *restrict address_len);
函数功能:用于服务器端接受一个传入的连接请求。当有客户端尝试连接到一个监听中的套接字时,accept() 会创建一个新的套接字来处理这个连接,而原来的监听套接字继续等待其他连接请求。
参数:socket:设置为监听状态的套接字描述符。该套接字应该已经绑定到一个本地地址,并且正在监听传入的连接请求address:指向 struct sockaddr 结构体的指针,用于接收客户端的地址信息。如果不需要获取客户端地址,可以传递 NULLaddress_len:指向一个 socklen_t 类型变量的指针,该变量在调用时应包含 address 指向的结构体的大小。函数返回时,address_len 将被更新为实际存储在 address 中的地址长度。如果 address 是 NULL,则 address_len 也应该是 NULL。
返回值:成功时,返回一个新的文件描述符,表示与客户端的连接。这个新的套接字专门用于与特定客户端通信。失败时,返回 -1,并且会设置 errno 变量来指示具体的错误原因。
(5)、connect
函数原型: #include <sys/socket.h>int connect(int socket, const struct sockaddr *address,
socklen_t address_len);
函数功能:用于主动发起一个到指定服务器的连接请求。将客户端套接字与远程服务器的地址和端口关联起来,从而建立一个通信通道。
参数:socket:已经配置好的套接字(例如,设置了协议族、类型和协议),但尚未绑定到本地地址或连接到远程地址address:指向 struct sockaddr 结构体的指针,该结构体包含要连接的远程服务器的地址信息对于 IPv4 (AF_INET),通常使用 struct sockaddr_in对于 IPv6 (AF_INET6),通常使用 struct sockaddr_in6对于 Unix 域套接字 (AF_UNIX),通常使用 struct sockaddr_unaddress_len:指定address指向的结构体的大小,以字节为单位对于 sockaddr_in 通常是sizeof(struct sockaddr_in)对于 sockaddr_in6 通常是sizeof(struct sockaddr_in6)
返回值:成功返回0,失败返回 -1,并设置 errno 变量来指示错误原因。
(6)、send
函数原型: #include <sys/socket.h>ssize_t send(int socket, const void *buffer, size_t length, int flags);
函数功能:用于通过已连接的套接字发送数据。它通常用于面向连接的协议(如TCP),但也可以用于无连接的协议(如UDP)。
参数:socket:已经连接的套接字描述符buffer:指向要发送的数据缓冲区的指针。该缓冲区包含要传输的字节数据length:要发送的数据长度,以字节为单位。如果缓冲区中的数据长度小于 len,则只发送缓冲区中实际存在的数据    flags:控制 send() 行为的标志0:默认行为,没有特殊选项MSG_OOB:发送带外数据(out-of-band data),适用于支持带外数据的协议(如TCP)MSG_DONTROUTE:跳过路由表查找,直接将数据发送到目标地址。通常用于诊断工具或特定网络配置MSG_DONTWAIT:使 send() 调用非阻塞。如果套接字是阻塞模式,此标志会使 send() 在无法立即发送所有数据时返回,而不是等待。如果套接字已经是非阻塞模式,此标志通常不会产生额外效果MSG_NOSIGNAL:防止 SIGPIPE 信号在尝试向已关闭的连接写入时生成。这对于避免程序意外终止非常有用
返回值:成功时,返回实际发送的字节数,可能小于length。失败时,返回 -1,并且会设置 errno 变量来指示具体的错误原因
(7)、recv
函数原型: #include <sys/socket.h>ssize_t recv(int socket, void *buffer, size_t length, int flags);
函数功能:用于从已连接的套接字接收数据。它通常用于面向连接的协议(如TCP),但也可以用于无连接的协议(如UDP)。
参数:socket:已经连接的套接字描述符。buffer:指向接收数据缓冲区的指针。该缓冲区将存储从套接字接收到的数据。length:指定缓冲区的最大长度,以字节为单位。flags:控制 recv() 行为的标志0:默认行为,没有特殊选项MSG_OOB:接收带外数据(out-of-band data),适用于支持带外数据的协议(如TCP)MSG_PEEK:窥视模式,数据被读取到缓冲区但不从输入队列中移除。下次调用 recv() 时仍然可以读取这些数据MSG_WAITALL:等待直到接收到请求的所有数据(len 个字节)。如果设置了这个标志,recv() 可能在接收到部分数据后仍然阻塞,直到接收到所有数据或发生错误MSG_DONTWAIT:使 recv() 调用非阻塞。如果套接字是阻塞模式,此标志会使 recv() 在无法立即读取数据时返回,而不是等待。如果套接字已经是非阻塞模式,此标志通常不会产生额外效果
返回值:成功时,返回实际接收到的字节数。如果返回值为 0,表示对端已经关闭了连接。失败时,返回 -1,并且会设置 errno 变量来指示具体的错误原因。
(8)、close
函数原型: #include <unistd.h>int close(int fildes);
函数功能:用于关闭一个打开的文件描述符,包括普通文件、设备、管道以及套接字等。关闭文件描述符后,操作系统将释放与该描述符相关的资源,并使其可以被重新分配给其他文件或套接字。
参数:fildes:要关闭的文件描述符。这个描述符可以是通过 open()、socket()、pipe() 等函数创建的。
返回值:成功返回0,失败返回 -1,并设置 errno 变量来指示错误原因。

3.2、TCP通信代码

(1)、客户端代码
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>#define SERVER_IP "127.0.0.1"  // 服务器IP地址
#define SERVER_PORT 8080       // 服务器端口
#define LOCAL_IP "0.0.0.0"     // 本地IP地址 (INADDR_ANY 表示任意可用接口)
#define LOCAL_PORT 6666       // 本地端口 (0 表示由操作系统选择)
#define BUFFER_SIZE 1024       // 缓冲区大小void error_exit(const char *msg) 
{perror(msg);exit(EXIT_FAILURE);
}void handle_receive(int client_sockfd) 
{char buffer[BUFFER_SIZE];ssize_t bytes_received;while (1) {bytes_received = recv(client_sockfd, buffer, BUFFER_SIZE - 1, 0);if (bytes_received < 0) {perror("Receive failed");break;} else if (bytes_received == 0) {printf("Server closed the connection\n");break;} else {buffer[bytes_received] = '\0'; // 确保字符串以null结尾printf("Received %zd bytes: %s\n", bytes_received, buffer);}}close(client_sockfd); // 关闭客户端套接字printf("Client socket closed in receive process\n");exit(EXIT_SUCCESS);   // 子进程退出
}int main(int argc, const char **argv) 
{int client_sockfd;struct sockaddr_in server_addr, local_addr;pid_t child_pid;// 1、创建一个TCP套接字client_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (client_sockfd < 0) {error_exit("Socket creation failed");}printf("Socket created successfully\n");// 配置本地地址结构memset(&local_addr, 0, sizeof(local_addr));local_addr.sin_family = AF_INET;local_addr.sin_port = htons(LOCAL_PORT); // 设置本地端口if (inet_pton(AF_INET, LOCAL_IP, &local_addr.sin_addr) <= 0) {error_exit("Invalid local address/ Address not supported");}// 2、绑定本地地址if (bind(client_sockfd, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {error_exit("Bind failed");}printf("Bound to local address %s:%d\n", LOCAL_IP, LOCAL_PORT);// 配置服务器地址结构memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT); // 设置服务器端口if (inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr) <= 0) {error_exit("Invalid server address/ Address not supported");}// 3、连接到服务器if (connect(client_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {error_exit("Connection failed");}printf("Connected to server at %s:%d\n", SERVER_IP, SERVER_PORT);// 4、创建子进程处理接收数据child_pid = fork();if (child_pid < 0) {error_exit("Fork failed");} else if (child_pid == 0) {// 子进程:关闭监听套接字,处理接收数据handle_receive(client_sockfd);} else {// 父进程:继续发送数据// 5、发送数据const char *message = "Hello, Server!";ssize_t bytes_sent = send(client_sockfd, message, strlen(message), 0);if (bytes_sent < 0) {error_exit("Send failed");}printf("Sent %zd bytes: %s\n", bytes_sent, message);printf("Enter message to send (or type 'exit' to quit): \n");// 父进程可以继续发送更多数据while (1) {char buffer[BUFFER_SIZE] = {0};fgets(buffer, BUFFER_SIZE, stdin);buffer[strcspn(buffer, "\n")] = '\0'; // 去掉换行符if (strcmp(buffer, "exit") == 0) {break;}bytes_sent = send(client_sockfd, buffer, strlen(buffer), 0);if (bytes_sent < 0) {error_exit("Send failed");}printf("Sent %zd bytes: %s\n", bytes_sent, buffer);}// 6、关闭套接字close(client_sockfd);printf("Socket closed\n");// 等待子进程结束,避免僵尸进程waitpid(child_pid, NULL, 0);}return 0;
}
(2)、服务器代码
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>#define SERVER_IP "127.0.0.1"   // 服务器IP地址,如果为 0.0.0.0则是监听所有的IP
#define SERVER_PORT 8080        // 服务器端口
#define BUFFER_SIZE 1024        // 缓冲区大小
#define BACKLOG 5               // 监听队列的最大长度void error_exit(const char *msg) 
{perror(msg);exit(EXIT_FAILURE);
}void handle_client(int client_sockfd) 
{char buffer[BUFFER_SIZE];ssize_t bytes_received;while ((bytes_received = recv(client_sockfd, buffer, BUFFER_SIZE - 1, 0)) > 0) {buffer[bytes_received] = '\0'; // 确保字符串以null结尾printf("Received %zd bytes from client: %s\n", bytes_received, buffer);// 回显接收到的数据if (send(client_sockfd, buffer, bytes_received, 0) < 0) {perror("Send failed");break;}}if (bytes_received == 0) {printf("Client closed the connection\n");} else if (bytes_received < 0) {perror("Receive failed");}close(client_sockfd); // 关闭客户端套接字printf("Client socket closed\n");
}int main(int argc, const char **argv) 
{int server_sockfd, client_sockfd;struct sockaddr_in server_addr, client_addr;socklen_t client_addr_len = sizeof(client_addr);pid_t child_pid;//1、创建一个TCP套接字server_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (server_sockfd < 0) {error_exit("Socket creation failed");}printf("Socket created successfully\n");// 配置服务器地址结构memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT); // 设置服务器端口if (inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr) <= 0) {error_exit("Invalid server address/ Address not supported");}//2、绑定套接字到指定的地址和端口if (bind(server_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {error_exit("Bind failed");}printf("Bind completed\n");//3、开始监听连接请求if (listen(server_sockfd, BACKLOG) < 0)  // backlog 设置为 5{error_exit("Listen failed");}printf("Server listening on port %d\n", SERVER_PORT);//4、接受并处理客户端连接while (1) {client_addr_len = sizeof(client_addr);client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_addr, &client_addr_len);if (client_sockfd < 0) {perror("Accept failed");continue;}printf("Accepted connection from %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));// 创建子进程处理客户端连接child_pid = fork();if (child_pid < 0) {perror("Fork failed");close(client_sockfd);continue;} else if (child_pid == 0) {// 子进程:关闭监听套接字,处理客户端连接close(server_sockfd);handle_client(client_sockfd);exit(EXIT_SUCCESS);} else {// 父进程:关闭客户端套接字,继续监听新的连接close(client_sockfd);// 等待子进程结束,避免僵尸进程waitpid(-1, NULL, WNOHANG);}}//5、关闭监听套接字close(server_sockfd);printf("Server socket closed\n");return 0;
}

        如下图所示的是TCP的客户端与服务器的API调用过程的核心梳理总结。

4、TCP通信展示

        上述TCP客户端与服务器代码编译运行后,实现的效果如下所示。

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

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

相关文章

【最新】北大数字普惠金融指数数据集-省市县(2011-2023年)

一、数据介绍 数据名称&#xff1a;第六期北大数字普惠金融指数-省市县 数据年份&#xff1a;2011-2023年 数据范围&#xff1a;全国31个省、337个地级以上城市以及2800个县 数据说明&#xff1a;编制方法请参阅《经济学&#xff08;季刊&#xff09;》中的《测度中国数字普…

GNSS误差源及差分定位

GNSS误差源&#xff1a; &#xff08;一&#xff09;卫星星历误差 由星历信息所得出的卫星位置坐标与实际位置坐标的偏差就是星历误差。星历信息是由 GPS 地面部分测量计算后传入空间部分的。由于卫星在运动中要受到各种摄动力的作用, 而地面部分又很难精确测量这些作用力,…

频域采样引起Gibbs效应——频域采样FIR滤波器设计的主要问题(答作者问)

还是这个图&#xff0c;我不明白廖老师为什么纠结这几个图不放过。Rafael Gonzalez的《数字图像处理》概念不清楚的地方&#xff0c;我就直接放过了&#xff0c;我为什么要和基础差的人纠结。 现在的问题是图(c )到图(d)为什么会产生Gibbs效应。这与补零&#xff08;哪怕是异想…

软考中级-软件设计师通过心路经验分享

执念&#xff0c;第四次终于通过了 没买书&#xff0c;下班后每天2小时&#xff0c;四个2个月终于过了 学习经验&#xff1a; 1.下班后学习真的靠毅力&#xff0c;和上学的时候考证不是一个状态&#xff0c;大家要及时调整&#xff0c;否则过程很痛苦 2.失败三次的经验&#xf…

Cesium中实现仿ArcGIS三维的动态图层加载方式

Cesium 加载 ArcGIS 动态图层的方式 如果你在 Cesium 中加载过 ArcGIS 的动态图层&#xff0c;你会发现&#xff0c;Cesium 对于动态图层仍然采用类似切片图层的逻辑进行加载。也就是每个固定的瓦片 export 一张图片。 这样会造成一些问题&#xff1a; 请求量大&#xff0c;…

zerotier实现内网穿透(访问内网服务器)

moo 内网穿透工具 实用工具&#xff1a;zerotier 目录 内网穿透工具 Windows下zerotier安装 ubuntu系统下的zerotier安装 使用moon加速 Windows下zerotier安装 有了网络之后&#xff0c;会给你一个网络id&#xff0c;这个网络id是非常重要的&#xff0c;其它设备要加入…

selenium自动爬虫工具

一、介绍selenium爬虫工具 selenium 是一个自动化测试工具&#xff0c;可以用来进行 web 自动化测试、爬虫 selenium 本质是通过驱动浏览器&#xff0c;完全模拟浏览器的操作&#xff0c;比如跳转、输入、点击、下拉等&#xff0c;来拿到网页渲染之后的结果&#xff0c;可支持…

软考-软件设计师-基础知识Chapter01-计算机系统

第一章 计算机系统 计算机系统基础知识 计算机系统硬件基本组成 计算器的基本硬件系统由运算器、控制器、存储器、输入设备、输出设备的5大部件组成。 中央处理单元 中央处理单元&#xff08;CPU&#xff09; 是计算机系统的核心部件&#xff0c;它负责获取程序指令、对指…

蓝桥杯新年题解 | 第15届蓝桥杯迎新篇

蓝桥杯新年题解 | 第15届蓝桥杯迎新篇 2024年的蓝桥杯即将拉开序幕&#xff01;对于许多编程爱好者来说&#xff0c;这不仅是一次展示自我能力的舞台&#xff0c;更是一次学习和成长的机会。作为一名大一新生的小蓝&#xff0c;对蓝桥杯充满了期待&#xff0c;但面对初次参赛的…

【漫话机器学习系列】005.神经网络的结构(architecture on the neural network)

神经网络&#xff08;Neural Network&#xff09;是一种模拟人脑神经系统的计算模型&#xff0c;由大量相互连接的神经元&#xff08;节点&#xff09;组成&#xff0c;广泛应用于深度学习和机器学习领域。以下是神经网络的基本结构及关键组成部分。 1. 神经网络的基本组成 一…

Python使用Selenium库获取 网页节点元素、名称、内容的方法

我们要用到一些网页源码信息&#xff0c;例如获取一些节点的class内容&#xff0c; 除了使用Beautifulsoup来解析&#xff0c;还可以直接用Selenium库打印节点&#xff08;元素&#xff09;名称&#xff0c;用来获取元素的文本内容或者标签名。 例如获取下面的class的内容&am…

数字化招聘系统如何帮助企业实现招聘效率翻倍提升?

众所周知&#xff0c;传统的招聘方式已经难以满足现代企业对人才的需求&#xff0c;而数字化招聘系统的出现&#xff0c;为企业提供了全新的解决方案。通过数字化招聘系统&#xff0c;企业可以自动化处理繁琐的招聘流程&#xff0c;快速筛选合适的候选人&#xff0c;从而大幅提…

(笔记)解决select下拉框默认选中selected属性不起作用问题

在 vue3 中使用 HTML原生开发&#xff0c;想给 select 下拉框选中 selected 属性不起作用。这是因为 vue3中使用了 Composition API&#xff08;组合式 api&#xff09;&#xff0c;而 Composition API 中的响应式数据是独立的&#xff0c;不会自动更新到 DOM 中。可以使用 v-m…

iPhone苹果相册视频怎么提取音频?

在数字时代&#xff0c;视频已成为我们记录生活、分享故事的重要方式。然而&#xff0c;有时候我们只想保留视频中的音频部分&#xff0c;比如一段动人的背景音乐或是一段珍贵的对话。那么&#xff0c;苹果相册视频怎么提取音频呢&#xff1f;本文将介绍三种简单且实用的方法&a…

若依实现图片上传时自动添加水印

文章目录 总体思路1. 修改通用上传方法2. 去除文件路径前两级目录3. 添加水印方法运行效果总结 为了解决图盗用&#xff0c;并有效保护图片版权&#xff0c;若依项目需要实现一个功能&#xff1a;上传图片时&#xff0c;自动在图片上添加水印。这不仅可以有效防止盗用&#xff…

ctfshow-web 151-170-文件上传

151. 我们首先想到就是上传一句话木马。但是看源代码限制了png。 &#xff08;1&#xff09;改前端代码。 这里是前端限制了上传文件类型&#xff0c;那我们就改一下就好了嘛,改成php。 这里直接修改不行&#xff0c;给大家推荐一篇简短文章&#xff0c;大家就会了&#xff08…

数据库同步中间件DBSyncer安装配置及使用

1、介绍 DBSyncer&#xff08;英[dbsɪŋkɜː]&#xff0c;美[dbsɪŋkɜː 简称dbs&#xff09;是一款开源的数据同步中间件&#xff0c;提供MySQL、Oracle、SqlServer、PostgreSQL、Elasticsearch(ES)、Kafka、File、SQL等同步场景。支持上传插件自定义同步转换业务&#xf…

SpringBoot左脚进门之常用注解

类级别注解 SpringBootApplication Configuration //表明这是一个配置类 EnableAutoConfiguration //开启自动配置 ComponentScan() //开启组件扫描1、Configuration&#xff1a; 当一个类被 Configuration 注解…

CNCF云原生生态版图

CNCF云原生生态版图 概述什么是云原生生态版图如何使用生态版图 项目和产品&#xff08;Projects and products&#xff09;会员&#xff08;Members&#xff09;认证合作伙伴与提供商&#xff08;Certified partners and providers&#xff09;无服务&#xff08;Serverless&a…

电子应用设计方案-50:智能牙刷系统方案设计

智能牙刷系统方案设计 一、引言 随着人们对口腔健康的重视程度不断提高&#xff0c;智能牙刷作为一种创新的口腔护理工具&#xff0c;能够更有效地帮助用户改善刷牙习惯和清洁效果。本方案旨在设计一款功能丰富、智能化程度高的智能牙刷系统。 二、系统概述 1. 系统目标 - 准…