当前位置: 首页 > news >正文

网络编程 | 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客户端与服务器代码编译运行后,实现的效果如下所示。

相关文章:

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

1、TCP基础铺垫 TCP/IP协议簇中包含了如TCP、UDP、IP、ICMP、ARP、HTTP等通信协议。TCP协议是TCP/IP协议簇中最为常见且重要的通信方式之一&#xff0c;它为互联网上的数据传输提供了可靠性和连接管理。 TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议…...

SAP导出表结构并保存到Excel 源码程序

SAP导出表结构并保存到Excel,方便写代码时复制粘贴 经常做接口,需要copy表结构,找到了这样一个程程,特别有用。 01. 先看结果...

Linux下redis环境的搭建

1.redis的下载 redis官网下载redis的linux压缩包&#xff0c;官网地址:Redis下载 网盘链接&#xff1a; 通过网盘分享的文件&#xff1a;redis-5.0.4.tar.gz 链接: https://pan.baidu.com/s/1cz3ifYrDcHWZXmT1fNzBrQ?pwdehgj 提取码: ehgj 2.redis安装与配置 将包上传到 /…...

REDMI瞄准游戏赛道,推出小屏平板

近日&#xff0c;REDMI推出了一款8.8英寸的小屏平板&#xff0c;引发市场关注。该平板采用LCD屏幕&#xff0c;搭载天玑9400处理器&#xff0c;定位游戏市场&#xff0c;意在开拓小屏平板的新领域‌。 ‌小屏平板新尝试‌ 这款REDMI平板未追随大屏潮流&#xff0c;而是选择了8…...

springai结合ollama

目录 ollama 介绍 使用 下载&#xff1a; 安装&#xff1a; 点击这个玩意next就行了。 运行 spring ai使用ollama调用本地部署的大模型 加依赖 配置yml 写代码 ollama 介绍 官网&#xff1a;Ollama Ollama是一个用于部署和运行各种开源大模型的工具&#xff1b; …...

React第十三节开发中常见问题之(视图更新、事件处理)

一、视图更新有哪些方案&#xff1f; useState用法介绍 1、对于数据变量 正常的增删改查&#xff0c;只会让数据更新&#xff0c;但是不会触发 React 视图的更新&#xff1b; 如&#xff1a; <script lang"jsx">const baseTable [{name:Andy, age: 18, id…...

【Appium报错】安装uiautomator2失败

目录 1、通过nmp安装uiautomator2&#xff1a;失败 2、通过 Appium 的平台直接安装驱动程序 3、通过pip 来安装 uiautomator2 1、通过nmp安装uiautomator2&#xff1a;失败 我先是通过npm安装的uiautomator2&#xff0c;也显示已经安装成功了&#xff1a; npm install -g …...

DataSophon集成CMAK KafkaManager

本次集成基于DDP1.2.1 集成CMAK-3.0.0.6 设计的json和tar包我放网盘了. 通过网盘分享的文件&#xff1a;DDP集成CMAK 链接: https://pan.baidu.com/s/1BR70Ajj9FxvjBlsOX4Ivhw?pwdcpmc 提取码: cpmc CMAK github上提供了zip压缩包.将压缩包解压之后 在根目录下加入启动脚本…...

Ubuntu22.04深度学习环境安装【显卡驱动安装】

前言 使用Windows配置环境失败&#xff0c;其中有一个包只有Linux版本&#xff0c;Windows版本的只有python3.10的&#xff0c;所以直接选用Linux来配置环境&#xff0c;显卡安装比较麻烦&#xff0c;单独出一期。 显卡驱动安装 方法一&#xff1a;在线安装&#xff08;操作…...

21届秋/校招面经

开篇先说一下我自身情况&#xff0c;东南大学本科计算机科学与技术专业毕业&#xff0c;gpa3.2/4.8。零零散散搞过一年多ACM&#xff0c;去年&#xff08;2019&#xff09;在icpc上海站拿了铜之后增加了信心&#xff08;因为当时训练总时间半年不到&#xff09;&#xff0c;于是…...

相机动态/在线标定

图1 图2 基本原理 【原理1】平行线在射影变换后会交于一点。如图所示,A为相机光心,蓝色矩形框为归一化平面,O为平面中心。地面四条黄色直线为平行且等距的车道线。HI交其中两条车道线于H、I, 过G作HI的平行线GM交车道线于M。HI、GM在归一化平面上的投影分别为JK、PN,二者会…...

MySQL 8.0 新特性汇总

文章目录 前言1. 运维管理 1.1 可持久化变量1.2 管理员端口1.3 资源组1.4 数据库粒度只读1.5 show processlist 实现方式1.6 加速索引创建速度1.7 控制连接的内存使用量1.8 克隆插件1.9 mysqldump 新增参数1.10 慢日志增强1.11 快速加列1.12 InnoDB 隐藏主键1.13 Redo 配置1.14…...

Resnet C ++ 部署 tensort 部署(四)

Resnet C 部署 pytorch功能测试&#xff08;一&#xff09; Resnet C 部署 模型训练&#xff08;二&#xff09; Resnet C 部署 模型测试&转 onnx&#xff08;三&#xff09; Resnet C 部署 tensort 部署&#xff08;四&#xff09; 之后&#xff0c;开始onnx 转trt 部…...

《Java核心技术I》对并发散列映射的批操作

对并发散列映射的批操作 Java API提供了批处理&#xff0c;计时其他线程处理映射&#xff0c;这些操作也能安全的执行。 3种不同操作&#xff1a; search(搜索)&#xff0c;为每个键或值应用一个函数&#xff0c;直到函数生成一个非null的结果&#xff0c;然后搜索终止&…...

记录一次使用git无权限的问题排查

正常的配置了公私钥之后&#xff0c;在gitlab中也存储了配对的公钥&#xff0c;但当使用git clone 时&#xff0c;总是报无权限 由于在这台机器中添加了多个公私钥&#xff0c;有点复杂&#xff0c;我们可以使用命令 ssh -vvvT 调试一下 ssh -vvvT yourGitlabAddr...

appium学习之二:adb命令

1、查看设备 adb devices 2、连接 adb connect IP:端口 3、安装 adb install xxx.apk 4、卸载 adb uninstall 【包名】 5、把对应目录下的1.txt文件传到手机sdcard下 adb push 1.txt /sdcard 6、进入对应的设备里 adb shell 7、切入sdcard目录 cd /sdcard 8、ls 查…...

Linux Vi/Vim使用 ⑥

掌握 CentOS 7 下的 Vi/Vim 编辑器&#xff1a;从安装到精通 在 CentOS 7 系统的日常运维、编程开发以及各类文本处理场景中&#xff0c;Vi/Vim 编辑器都是不可或缺的得力工具。它以轻量、高效、功能强大著称&#xff0c;虽然初次上手有一定学习门槛&#xff0c;但掌握之后便能…...

JCR一区牛顿-拉夫逊优化算法+分解对比!VMD-NRBO-Transformer-BiLSTM多变量时序光伏功率预测

JCR一区牛顿-拉夫逊优化算法分解对比&#xff01;VMD-NRBO-Transformer-BiLSTM多变量时序光伏功率预测 目录 JCR一区牛顿-拉夫逊优化算法分解对比&#xff01;VMD-NRBO-Transformer-BiLSTM多变量时序光伏功率预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.中科院…...

easyExcel实现表头批注

背景&#xff1a; 网上大部分都不能直接使用&#xff0c;为此总结一个方便入手且可用的工具&#xff0c;用自定义注解实现 依赖包&#xff1a; <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>…...

Pytest测试用例使用小结

基础使用 Pytest 测试用例实现代码 import pytest from server.service import Servicepytest.fixture def service():return Service(logger)class TestService:classmethoddef setup_class(cls):"""初始化设置一次:return:"""logger.info(&q…...

LeetCode题练习与总结:132 模式--456

一、题目描述 给你一个整数数组 nums &#xff0c;数组中共有 n 个整数。132 模式的子序列 由三个整数 nums[i]、nums[j] 和 nums[k] 组成&#xff0c;并同时满足&#xff1a;i < j < k 和 nums[i] < nums[k] < nums[j] 。 如果 nums 中存在 132 模式的子序列 &a…...

IdentityServer4框架、ASP.NET core Identity

OAuth2.0 IdentityServer4 官网 中文官网 ASP.NET Core Identity提供了一个用来管理和存储用户账户的框架. IdentityServer4是基于ASP.NET Core实现的认证和授权框架&#xff0c;是对OpenID Connect和OAuth 2.0协议的实现。 IdentityServer是一个中间件,它可以添加符合OpenID…...

【分子材料发现】——GAP:催化过程中吸附构型的多模态语言和图学习(数据集处理详解)(二)

Multimodal Language and Graph Learning of Adsorption Configuration in Catalysis https://arxiv.org/abs/2401.07408Paper Data: https://doi.org/10.6084/m9.figshare.27208356.v2 1 Dataset CatBERTa训练的文本字符串输入来源于Open Catalyst 2020 &#xff08;OC20…...

SpringBoot开发过程中经常遇到问题解决方案分享

目录 1. Spring Boot应用启动缓慢 2. 数据库连接池配置问题 3. Spring Boot应用无法连接外部服务 4. 配置文件读取不生效 5. Spring Boot应用的日志输出不完整 6. Spring Boot中的Transactional事务管理问题 1. Spring Boot应用启动缓慢 问题原因&#xff1a; Spring Boo…...

AR眼镜_消费级工业AR智能眼镜主板硬件解决方案

AR眼镜的研发是一项复杂的软硬件集成工程&#xff0c;它需要在摄影、音频、交互和连接等多个方面提供卓越的基础体验&#xff0c;因此产品的每个细节都显得尤为重要。 在设计AR眼镜时&#xff0c;重量、体积和散热性能都是必须认真考量的关键因素。在芯片平台的选择上&#xff…...

Springboot 核心注解

Spring Boot 是一个基于 Spring 框架的扩展&#xff0c;旨在简化新 Spring 应用的初始搭建以及开发过程。它通过自动配置和约定优于配置的原则&#xff0c;减少了开发者的工作量。Spring Boot 提供了一组核心注解和 Starter 依赖管理工具来帮助开发者快速启动项目。 1. Spring…...

Nacos集群搭建【Oracle作外部数据源】

一、知识点分析 1.Nocas是什么&#xff1f; Nacos是一个动态服务发现、配置管理和服务管理平台‌。 1‌.1定义与背景‌&#xff1a; Nacos&#xff0c;全称为Dynamic Naming and Configuration Service&#xff0c;是由阿里巴巴开源的云原生应用配套工具。它旨在简化微服务架…...

云轴科技ZStack出席中国电信国际EMCP平台香港发布会,持续推动海外合作

近日&#xff0c;以“云聚未来 翼起新篇”为主题的中国电信国际多云服务一站式平台&#xff08;E-surfing Managed Cloud Platform&#xff0c;简称EMCP平台&#xff09;新闻发布会在香港成功举办&#xff0c;标志着中国电信国际在云计算服务领域取得了又一重大进展。云轴科技…...

爬虫自动化之drissionpage+SwitchyOmega实现随时切换代理ip

本文介绍了如何使用DrizzlePage进行爬虫自动化,并重点讲解了首次启动时设置代理IP以及通过SwitchyOmega插件实现随时切换代理IP的方法。 安装一次,后面调用就不会再去安装了 下载地址:https://github.com/FelisCatus/SwitchyOmega/releases 这两个文件随便那个都可以,下载…...

docker安装kettle(PDI)并实现web访问

我是MAC电脑M1版本&#xff0c;希望把软件交给docker进行管理&#xff0c;最近公司同事都通过kettle来实现外部数据对接&#xff0c;所以我本地也有安装kettle需求&#xff0c;在网上找到了这个解决方案操作很简单&#xff0c;但出现了无法访问的情况。我的排查方式是&#xff…...

怎样安全做黑色彩票网站/台州关键词优化平台

服务器电源规范解析服务器的动力之源、稳定之源服务器电源有ATX电源和SSI电源两大类产品。ATX电源就是我们平常台式机使用的电源&#xff0c;在服务器领域这种电源主要被用在低端的塔式服务器及工作站上。而SSI电源则是Intel推出的一种服务器专用电源规范。台式PC电源的发展经过…...

做网站送的小程序有什么用/百度seo培训公司

文章目录介绍手动标注少数图转化VOC到YOLO转移数据到Linux服务器训练YOLOV5添加数据yaml文件选一个合适的权重训练开服务写labelstxt文件介绍 以条形码为例&#xff0c;下载数据集muenster_barcodedb&#xff0c;想使用yolov5对条形码进行检测。muenster_barcodedb数据集有几千…...

网站开发是前端还是/互联网平台公司有哪些

​目前的BI市场上有很多大数据的厂商&#xff0c;这也让许多客户眼花缭乱&#xff0c;不知如何下手。比如国内的亿信华辰跟国外的Tableau&#xff0c;QLK&#xff0c; PowerBI 都有非常多便于分析的可视化工具&#xff0c;但是在数据处理分析的功能性上&#xff0c;与SAS, Micr…...

淘宝客wordpress想/广州网络运营课程培训班

图的存在&#xff0c;让数据变得形象化。无论多么复杂的东西&#xff0c;都是简单的组合。 1 import matplotlib.pyplot as plt 2 import numpy as np 3 dict {A: 40, B: 70, C: 30, D: 85} 4 for i, key in enumerate(dict):#Circulate both index and value(Here is key) 5…...

益保网做推广网站吗/品牌广告视频

本人python新手&#xff0c;答案自己做的&#xff0c;如果有问题&#xff0c;欢迎大家评论和讨论&#xff01; 更新会在本随笔中直接更新。 5-1.整型。讲讲Python普通整型和长整型的区别。 Python的标准整形类型是最通用的数字类型。在大多数32位机器上&#xff0c;标准整形类型…...

做网贷网站/运营培训班

题目链接&#xff1a;戳我 【问题描述】 小A在玩打地鼠游戏。有一个nm的网格&#xff0c;每个位置上地鼠都会要么冒出头要么缩进去。地鼠很狡猾&#xff0c;每次小A选一个地鼠冒出头的格子(x,y)把它打下去&#xff0c;但同一行同一列的地鼠全都会冒出头来。 小A发现这个游戏好像…...