Linux- 网络编程初探
原始套接字(Raw Socket)
原始套接字(Raw Socket)是一种提供较低级别网络访问的套接字。通过使用原始套接字,应用程序可以直接发送或接收网络层如IP的数据包,或者传输层如TCP、UDP的段,而无需通过常规的套接字API提供的协议处理。
以下是原始套接字的一些关键点:
-
协议独立性:使用原始套接字,我们可以操作或构建自己的协议,或者直接与现有协议(如ICMP)交互。
-
绕过内核处理:通常,当发送或接收数据包时,操作系统内核会为我们处理很多细节,例如TCP的三次握手或IP头的填充。但是,使用原始套接字,我们可以直接构建或解析这些协议,从而绕过标准的内核处理。
-
特权:由于原始套接字提供了对网络的低级访问,使用它们通常需要特权,例如root权限。
-
应用场景:
- 网络诊断和测试工具:例如,
ping使用原始套接字发送和接收ICMP回显请求和回显响应。 - 定制协议的实现:例如,如果我们想实验一个新的传输层协议。
- 安全研究和网络攻击:例如,执行某些类型的DoS攻击或网络扫描。
- 网络诊断和测试工具:例如,
-
创建原始套接字:在Linux中,我们可以使用
socket函数并为其提供AF_INET(对于IPv4)或AF_INET6(对于IPv6)以及SOCK_RAW来创建一个原始套接字。 -
手动头部处理:使用原始套接字,需要手动构建或解析协议头部。例如,如果我们正在发送一个TCP段,我们需要手动构建IP和TCP头部,并设置所有必要的字段。同样,当从一个原始套接字接收数据时,我们将获取整个数据包,需要自己解析它。
-
混杂模式:如果我们想使用原始套接字捕获一个接口上的所有流量(而不仅仅是发给特定地址的流量),我们需要将接口设置为混杂模式。
需要注意的是,虽然原始套接字提供了强大的功能,但也需要小心使用。手动处理协议细节容易导致错误,并可能引起网络问题或安全隐患。
Socket()
socket()函数是计算机网络编程中的核心函数之一,用于创建一个新的套接字。套接字是端到端的通信链路,是进程之间进行网络通信的主要手段。
下面详细介绍socket()函数:
函数原型
int socket(int domain, int type, int protocol);
参数
-
domain(或称为family):指定使用哪种地址族。常见的选择包括:
AF_INET:IPv4 地址族。用于IPv4网络通信。AF_INET6:IPv6 地址族。用于IPv6网络通信。AF_UNIX:本地套接字(UNIX 域套接字)地址族。用于同一机器上的进程间通信。
-
type:指定套接字的类型。常见的选择包括:
SOCK_STREAM:提供面向连接、可靠、双向的字节流服务。典型的协议有TCP。SOCK_DGRAM:提供无连接的、不可靠的数据报服务。典型的协议有UDP。SOCK_RAW:提供原始套接字访问,允许直接发送或接收协议如IP的数据包。SOCK_SEQPACKET:提供面向连接的、可靠的、固定最大长度的记录序列。
-
protocol:指定要使用的协议。通常,当给定了套接字的类型时,可以将此参数设置为0,让系统自动选择合适的协议。例如,当
type是SOCK_STREAM时,系统通常选择TCP作为协议。
返回值
- 成功:返回一个非负描述符,代表新创建的套接字。
- 失败:返回-1,并设置相应的错误码。
使用示例
创建一个用于IPv4 TCP通信的套接字:
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {perror("socket creation failed");exit(EXIT_FAILURE);
}
创建一个用于IPv4 UDP通信的套接字:
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1) {perror("socket creation failed");exit(EXIT_FAILURE);
}
注意事项
- 创建套接字只是第一步。为了实际上进行通信,还需要其他函数,如
bind(),listen(),accept(),connect(),send(), 和recv(),来配置并操作这个套接字。 - 对于TCP服务端,通常在
socket()之后会调用bind(),listen()和accept()来绑定地址、监听连接和接受连接。 - 对于TCP客户端,通常在
socket()之后会调用connect()来连接到服务器。 - 对于UDP,没有建立或接受连接的概念,所以只需创建套接字,然后可以直接使用
sendto()和recvfrom()进行通信。
socket()函数是网络编程中的基础,几乎所有的网络应用程序都会在某个地方使用它来开始其网络通信。
bind()
bind() 是套接字编程中的一个关键函数,用于将套接字与特定的IP地址和端口号绑定。它通常在服务器设置期间使用,以指定服务器将在哪个地址和端口监听即将到来的客户端连接。
函数原型:
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
-
sockfd: 这是一个套接字描述符,它表示要绑定的套接字。
-
addr: 这是一个指向
struct sockaddr的指针,该结构定义了套接字的地址(IP和端口)。在实践中,通常使用特定于协议的结构(如struct sockaddr_in对于IPv4)来填充这个参数,并将其指针类型强制转换为struct sockaddr *。 -
addrlen: 这是地址结构的大小(例如,对于IPv4,这将是
sizeof(struct sockaddr_in))。
返回值:
- 成功时,
bind()返回0。 - 失败时,返回-1,并设置
errno以指示错误的原因。
常见的使用模式:
在服务器中,通常首先创建一个套接字,然后使用bind()将它绑定到一个地址和端口。以下是一个简化的示例,演示如何使用bind()为IPv4地址绑定套接字:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>int main() {int sockfd = socket(AF_INET, SOCK_STREAM, 0); // Create a TCP socketif (sockfd < 0) {perror("Error creating socket");return 1;}struct sockaddr_in server_addr;server_addr.sin_family = AF_INET; // Address family for IPv4server_addr.sin_addr.s_addr = INADDR_ANY; // Listen on any interfaceserver_addr.sin_port = htons(8080); // Listen on port 8080if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {perror("Error binding socket");return 1;}// ... the server can then proceed to listen and accept connectionsreturn 0;
}
常见错误和注意事项:
-
Address already in use: 如果尝试绑定到已被另一个套接字使用的地址和端口,将会出现此错误。这通常发生在服务器崩溃并尝试重新启动,但由于之前的套接字仍处于“TIME_WAIT”状态,所以它不能立即绑定。使用
setsockopt()和SO_REUSEADDR可以帮助解决此问题。 -
Permission denied: 通常,只有root用户才能绑定到低于1024的端口。
-
确保在绑定套接字之前填充了整个
sockaddr_in结构,并正确设置了sin_family、sin_addr.s_addr和sin_port字段。 -
使用
htons()函数确保端口号是网络字节顺序。
通过合理地使用bind()函数,开发人员可以确保他们的服务器监听特定的IP地址和端口,从而等待客户端的连接。
listen()
listen() 是套接字API中的一个函数,用于让一个套接字进入监听模式,从而能够接收来自客户端的连接请求。这是创建服务器应用程序的必要步骤之一。
函数原型:
int listen(int sockfd, int backlog);
参数:
-
sockfd: 一个套接字描述符,它应该先前已经使用
socket()创建并使用bind()绑定到一个特定的地址和端口。 -
backlog: 这个参数定义了等待队列的大小,也就是说,系统应该允许等待处理(未
accept()的)的连接数量。当有更多的客户端尝试连接,超过了backlog指定的数量时,系统会开始拒绝这些新的连接请求。
返回值:
- 如果函数调用成功,则返回0。
- 如果出现错误,则返回-1,并设置
errno以指示出现的特定错误。
使用:
一旦使用bind()函数将套接字绑定到一个地址和端口后,我们可以调用listen()以进入监听模式。在此模式下,套接字准备接受来自客户端的连接请求。
这是一个简单的示例:
int sockfd = socket(AF_INET, SOCK_STREAM, 0); // Create a socket// ... (bind the socket to an address using bind() here)if (listen(sockfd, 5) < 0) { // Allow up to 5 pending connectionsperror("Error while trying to listen");return 1;
}
此示例创建了一个套接字,并设置其最大待处理连接数为5。当超过5个客户端连接并等待被accept()时,任何进一步的连接请求都将被拒绝,直到有一个连接被accept()为止。
注意:
- 在调用
listen()之前,必须先调用bind()。 backlog参数的具体含义和行为可能因操作系统而异。在某些系统上,它表示待处理的连接数量,而在其他系统上,它可能包括已被accept()但尚未由应用程序处理的连接。- 当
backlog队列已满,进一步的连接请求可能会被拒绝。因此,为了避免这种情况,服务器应该尽快处理连接。 - 通常,在
listen()之后,会进入一个循环,不断地调用accept()来接受并处理来自客户端的连接。
总之,listen() 函数是服务器套接字编程中的关键步骤,使得服务器能够开始接受客户端的连接请求。
accept()
accept() 是套接字编程中的一个关键函数,用于从已经处于监听模式的套接字中提取连接请求,并返回一个新的套接字描述符,该描述符代表与客户端之间的新连接。此函数在服务器应用程序中经常使用,以处理来自客户端的连接请求。
函数原型:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
-
sockfd: 这是一个处于监听模式的套接字描述符,通常先前已经通过
socket()创建并通过bind()和listen()函数配置。 -
addr: 这是一个指向
struct sockaddr的指针,当accept()调用返回时,这个结构将被填充与已经接受的连接的远程端(客户端)的地址信息。 -
addrlen: 这是一个值-结果参数。在调用
accept()之前,它应该被设置为addr指向的地址结构的大小。当函数返回时,addrlen将被设置为实际的地址大小。
返回值:
-
成功时,
accept()返回一个新的套接字描述符,代表与客户端的新连接。此新描述符应用于后续的所有通信(例如send()和recv()调用)。 -
失败时,返回 -1,并设置
errno以指示出现的错误。
使用和注意事项:
-
在服务器应用程序中,通常在
listen()函数调用后立即调用accept(),等待客户端的连接。 -
accept()函数是阻塞的,这意味着它将等待,直到一个连接请求可用,除非套接字已被配置为非阻塞。 -
返回的新套接字描述符与原始的监听套接字是独立的。应使用新的套接字描述符进行与客户端的所有通信,并继续使用原始的监听套接字来接受其他连接请求。
-
通常,服务器将为每个接受的连接启动一个新的线程或进程以并行处理多个连接。
-
addr和addrlen参数是可选的;如果我们不关心客户端的地址,我们可以设置这两个参数为 NULL。
示例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>int main() {int server_sock, client_sock;struct sockaddr_in server_addr, client_addr;socklen_t client_addr_len = sizeof(client_addr);// Assuming server_sock has been created and set up for listening...client_sock = accept(server_sock, (struct sockaddr *) &client_addr, &client_addr_len);if (client_sock < 0) {perror("Error on accept");return 1;}// Use client_sock for communication with the client...close(client_sock);return 0;
}
这个简单的例子展示了如何使用 accept() 函数从 server_sock 监听套接字中接受一个新的连接,并使用 client_sock 与客户端进行通信。
connect()
connect() 是套接字编程中的一个函数,主要用于客户端应用程序。该函数使客户端尝试与服务器端的指定地址建立连接。
函数原型:
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
-
sockfd: 这是我们想要与远程主机连接的套接字的描述符。
-
addr: 这是一个指向
struct sockaddr的指针,包含我们想要连接的远程主机的地址信息。在实际应用中,通常使用特定于协议的结构(如struct sockaddr_in对于IPv4)并将其类型强制转换为struct sockaddr *。 -
addrlen: 这是地址结构的大小,例如对于IPv4地址,这通常是
sizeof(struct sockaddr_in)。
返回值:
-
成功时,
connect()返回0。 -
失败时,返回-1,并设置
errno以指示错误原因。
使用和注意事项:
-
在客户端应用程序中,我们通常首先使用
socket()函数创建一个套接字,然后使用connect()函数尝试与服务器连接。 -
如果
connect()成功,客户端可以开始使用send()和recv()或其他相关函数与服务器通信。 -
如果连接尝试失败,
connect()将返回-1。这可能是由于多种原因,例如服务器未在指定的地址和端口上运行,网络故障,或服务器拒绝连接。 -
connect()在默认情况下是阻塞的,这意味着它会等待,直到连接成功或发生错误。但是,我们可以将套接字设置为非阻塞模式,使connect()立即返回,并后续使用select()或poll()来等待连接完成。
示例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>int main() {int client_sock;struct sockaddr_in server_addr;// Assuming client_sock has been created...server_addr.sin_family = AF_INET;server_addr.sin_port = htons(8080); // server portinet_pton(AF_INET, "192.168.1.1", &server_addr.sin_addr); // server IPif (connect(client_sock, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {perror("Error on connect");return 1;}// Now client_sock is connected and can be used to send or receive data...return 0;
}
这个简单的例子展示了如何使用 connect() 函数尝试与运行在192.168.1.1的服务器上的服务连接,该服务监听端口8080。
send()
send() 是套接字编程中的一个函数,用于向一个已连接的套接字发送数据。它通常用于TCP套接字,但也可以与其他类型的套接字一起使用。
函数原型:
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
参数:
-
sockfd: 这是一个已连接的套接字的描述符。
-
buf: 这是一个指针,指向我们想要发送的数据的缓冲区。
-
len: 这是
buf中我们想要发送的数据的字节数。 -
flags: 这是一个修改函数操作的标志集合。常见的标志包括
MSG_OOB(用于发送"out-of-band"数据) 和MSG_NOSIGNAL(阻止在连接断开时发送SIGPIPE信号)。大多数情况下,我们可以简单地将此参数设置为0。
返回值:
-
成功时,
send()返回实际发送的字节数。请注意,这可能少于我们请求发送的数量。 -
失败时,返回-1,并设置
errno以指示错误原因。
使用和注意事项:
-
在一个已连接的TCP套接字上使用
send()之前,必须先成功地调用connect()(对于客户端)或accept()(对于服务器)。 -
TCP是一个流协议,这意味着没有消息边界。连续的
send()调用可能会在接收方看起来像一个连续的数据流,而不是单独的消息。 -
如果套接字是阻塞的(默认情况),
send()可能会阻塞,直到有足够的网络缓冲区可用以发送数据。如果套接字是非阻塞的,而网络缓冲区不可用,则send()将立即返回-1,并将errno设置为EAGAIN或EWOULDBLOCK。 -
在连接断开的套接字上调用
send()将导致发送一个SIGPIPE信号,除非设置了MSG_NOSIGNAL标志。此信号的默认行为是终止进程,但可以捕获或忽略它。
示例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>int main() {int sockfd; // assuming it's already connectedconst char *message = "Hello, server!";ssize_t bytes_sent;bytes_sent = send(sockfd, message, strlen(message), 0);if (bytes_sent < 0) {perror("Error on send");return 1;}printf("Sent %zd bytes to server.\n", bytes_sent);return 0;
}
这个简单的示例展示了如何使用send()函数将一条消息发送到一个已连接的服务器。
recv()
recv() 函数用于从已连接的套接字接收数据。它主要用于 TCP 套接字,但也可以与其他类型的套接字一起使用。
函数原型:
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
参数:
-
sockfd: 这是一个已连接的套接字的描述符。
-
buf: 这是一个指针,指向一个缓冲区,用于存储接收到的数据。
-
len: 这是缓冲区的大小,即我们期望接收的最大字节数。
-
flags: 这是一个修改函数操作的标志集合。一些常见的标志包括:
MSG_PEEK: 查看即将到来的数据,但不从队列中删除它。MSG_WAITALL: 尝试接收指定的len字节。与默认行为不同,该标志会使函数等待,直到请求的字节数量可用或发生某些错误。MSG_OOB: 用于接收 “out-of-band” 数据。
在大多数常规操作中,我们可以简单地将此参数设置为0。
返回值:
-
成功时,
recv()返回实际接收到的字节数。如果连接已关闭,返回0。 -
失败时,返回-1,并设置
errno以指示错误原因。
使用和注意事项:
-
在一个已连接的 TCP 套接字上使用
recv()之前,我们需要先成功调用connect()(对于客户端)或accept()(对于服务器)。 -
TCP 是一个流协议,这意味着没有消息边界。连续的
recv()调用可能会接收到之前调用的数据的剩余部分。 -
如果套接字是阻塞的(默认情况),并且没有数据可用,
recv()会阻塞,直到数据可用。如果套接字是非阻塞的,并且没有数据可用,recv()会立即返回-1,并将errno设置为EAGAIN或EWOULDBLOCK。 -
当连接断开或关闭时,
recv()将返回0。因此,接收到0字节通常意味着对端关闭了连接。
示例:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>int main() {int sockfd; // assuming it's already connectedchar buffer[1024];ssize_t bytes_received;bytes_received = recv(sockfd, buffer, sizeof(buffer) - 1, 0);if (bytes_received < 0) {perror("Error on recv");return 1;} else if (bytes_received == 0) {printf("The peer has closed the connection.\n");return 0;}buffer[bytes_received] = '\0'; // Null-terminate the stringprintf("Received: %s\n", buffer);return 0;
}
这个简单的示例展示了如何使用 recv() 函数从已连接的服务器接收消息,并将其打印出来。
综合案例
下面是一个简单的TCP套接字编程的例子,其中包括一个服务器和一个客户端。服务器接收来自客户端的消息,然后返回相同的消息。
服务器端:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>#define SERVER_PORT 8080
#define BUFFER_SIZE 1024int main() {int server_sock, client_sock;struct sockaddr_in server_addr, client_addr;char buffer[BUFFER_SIZE];int bytes_read;// 创建套接字server_sock = socket(AF_INET, SOCK_STREAM, 0);if (server_sock == -1) {perror("Socket creation failed");exit(1);}// 设置服务器地址结构memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT);server_addr.sin_addr.s_addr = INADDR_ANY;// 绑定套接字if (bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {perror("Bind failed");exit(1);}// 监听连接请求listen(server_sock, 5);printf("Server is listening on port %d...\n", SERVER_PORT);socklen_t client_addr_len = sizeof(client_addr);client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &client_addr_len);if (client_sock == -1) {perror("Accept failed");exit(1);}// 读取和响应客户端的消息bytes_read = recv(client_sock, buffer, BUFFER_SIZE, 0);buffer[bytes_read] = '\0';printf("Received from client: %s\n", buffer);send(client_sock, buffer, bytes_read, 0);close(client_sock);close(server_sock);return 0;
}
客户端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>#define SERVER_PORT 8080
#define SERVER_IP "127.0.0.1"
#define BUFFER_SIZE 1024int main() {int client_sock;struct sockaddr_in server_addr;char buffer[BUFFER_SIZE];// 创建套接字client_sock = socket(AF_INET, SOCK_STREAM, 0);if (client_sock == -1) {perror("Socket creation failed");exit(1);}// 设置服务器地址结构memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT);inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);// 连接到服务器if (connect(client_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {perror("Connect failed");exit(1);}strcpy(buffer, "Hello, Server!");send(client_sock, buffer, strlen(buffer), 0);int bytes_received = recv(client_sock, buffer, BUFFER_SIZE, 0);buffer[bytes_received] = '\0';printf("Received from server: %s\n", buffer);close(client_sock);return 0;
}
运行结果如下:

上述示例中,服务器创建一个套接字,绑定到本地地址并监听连接。当客户端连接时,服务器接收来自客户端的消息,并将相同的消息发送回客户端。客户端则发送一个简单的消息,并从服务器接收响应。
相关文章:
Linux- 网络编程初探
原始套接字(Raw Socket) 原始套接字(Raw Socket)是一种提供较低级别网络访问的套接字。通过使用原始套接字,应用程序可以直接发送或接收网络层如IP的数据包,或者传输层如TCP、UDP的段,而无需通…...
AVLoadingIndicatorView - 一个很好的Android加载动画集合
官网 GitHub - HarlonWang/AVLoadingIndicatorView: DEPRECATED 项目简介 AVLoadingIndicatorView is a collection of nice loading animations for Android. You can also find iOS version of this here. Now AVLoadingIndicatorView was updated version to 2.X , If …...
我想设计一套游戏的奖励系统,有什么值得注意的?
游戏上: 游戏成就系统的价值 游戏中的成就可以延长游戏时间,让玩家不仅仅是将游戏通关,而是必须完成游戏内所有挑战及发现秘密,这些成就可以与游戏本身的目标一致,也可以独立于游戏的主要或次要目标之外,…...
精通git,没用过git cherry-pick?
前言 git cherry-pick是git中非常有用的一个命令,cherry是樱桃的意思,cherry-pick就是挑樱桃,从一堆樱桃中挑选自己喜欢的樱桃,在git中就是多次commit中挑选一个或者几个commit出来,也可以理解为把特定的commit复制到…...
QT5|C++|通过创建子线程方式实现进度条更新
背景: 一开始是通过在主线程中写一个for循环,每次加1后睡眠1s进行进度条更新。但这样写的结果是 --> 无法动态显示进度条进度。后通过上一篇文章 [ QT5|C|通过信号槽机制实现进度条更新 ] 中的写信号槽机制实现。实现后 考虑了下有没有其他方式实现&a…...
基于mediasoup的webrtc server,性能压测时发现带宽利用率偏低(40%)
基于mediasoup的webrtc server,进行性能压测时发现,在1gbps的网络条件下,带宽利用率在40%(400Mbps)时,就会出现过高丢包率的(packet loss > 10%)的情况。这个结果是合理的吗?如果不合理,要如何提升性能&…...
Ubuntu Redis开机自启动服务
1. 建立service文件 sudo vim /etc/systemd/system/redis-server.service2. redis service文件 [Unit] DescriptionAdvanced key-value store Afternetwork.target Documentationhttp://redis.io/documentation, man:redis-server(1)[Service] Typenotify ExecStart/usr/bin/…...
Stm32_标准库_呼吸灯_按键控制
Stm32按键和输出差不多 PA1为LED供给正电,PB5放置按键,按键一端接PB5,另一端接负极 void Key_Init(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //APB2总线连接着GPIOBGPIO_InitStructur.GPIO_Mode GPIO_Mode_IPU;GPIO_InitStructur.…...
MySQL作业:索引、视图、存储、函数
学生表:Student (Sno, Sname, Sex , Sage, Sdept) 学号,姓名,性别,年龄,所在系 Sno为主键 课程表:Course (Cno, Cname,) 课程号,课程名 Cno为主键 学生选课表:SC (Sno, Cno, Score) …...
漫谈:C语言 C++ 所有编程语言 =和==的麻烦
这次不只是C语言很麻拐,是所有编程语言都很麻拐了。 赋值和比较是编程语言最基本的操作之二,C和所有类C语言都使用“”和“”来分别表示赋值和比较。 数学上等号“”是个单一的概念,含义是“相等”,左右两边是等价的,很…...
十五、异常(1)
本章概要 异常概念基本异常 异常参数 异常捕获 try 语句块异常处理程序终止与恢复 Java 的基本理念是“结构不佳的代码不能运行”。 改进的错误恢复机制是提高代码健壮性的最强有力的方式。错误恢复在我们所编写的每一个程序中都是基本的要素,但是在 Java 中它显得…...
Redis 哨兵模式搭建教程
一、介绍 本文实战搭建一主两从三哨兵,通过使用哨兵模式,可以有效避免某台服务器的 Redis 挂掉出现的不可用问题,保障系统的高可用。 本文通过虚拟机搭建的三台 Centos7 服务器进行测试,使用的 Redis 版本为 6.25。 二、准备环…...
【C++】gnustl_static 与 c++_shared 的区别
参考:GNU与cSTL的区别与联系-爱代码爱编程 gnustl_static 与 c_shared 的区别: 不同版本的 STL TSL是一个与STL兼容的多线程支持库。 STLport是一个可移植、高度兼容的STL实现。 SGI STL是最早的STL实现之一,对STL的发展起到了重要的作用…...
怎样选择第三方检测机构获取功能测试报告?
数字化时代,软件给人们的生活带来了越来越多的便利,产品功能测试也成为了软件开发方在研发时的重要环节,这关系到用户使用产品的体验感。所以做好软件功能测试对把控产品质量有着很大影响,通过有效的功能测试能够发现系统潜在的问…...
【@PostConstruct、 @Autowired与构造函数的执行顺序】
PostConstruct、 Autowired与构造函数的执行顺序 一、PostConstruct介绍二、Spring框架中在bean初始化和销毁时候执行实现方式三、项目验证1.MyServiceImpl2.测试结果3. 项目源码 最近对同事代码进行codeReview时候发现用PostConstruct注解,特地对此注解执行顺序进行…...
用vite搭建vue3+ts项目
一、环境搭建 1:首先vite环境安装 npm init vite 或者 yarn init vite 2:输入项目的名字,在这里用vue3_test ? Project name: › vite-project 3:选择项目类型,这里选择Vuets 4:出现下图,初始化基本…...
前端求职指南
简历求职指南 为什么没有面试? 1、简历写的不好 2、简历投递不好 简历的定义是什么? 是求职者向未来雇主展示自己专业技能和职业素养的自我推销工具,以找到工作为目的。 什么时候改简历? 每半年或一年更新一次工作中的成长 再工…...
datax同步数据翻倍,.hive-staging 导致的问题分析
一、背景 有同事反馈 Datax 从 Hive 表同步数据到 Mysql 数据翻倍了。通过查看 Datax 任务日志发现,翻倍的原因是多读取了 .hive-staging_xx 开头的文件。接下里就是有关 .hive-staging 的分析。 二、环境 Hive 版本 2.1.1 三、分析 3.1 .hive-staging_hive 产…...
DataGrip 恢复数据
DataGrip 恢复数据 准备数据库 create database chap02; use chap02;恢复数据 运行sql脚本方式恢复数据到刚刚创建的数据库 数据右键—> SQL Scripts —> Run SQL Script… 选择sql脚本...
【深度学习实验】前馈神经网络(一):使用PyTorch构建神经网络的基本步骤
目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入库 1. 定义x,w,b 2. 计算净活性值z 3. 实例化线性层并进行前向传播 4. 打印结果 5. 代码整合 一、实验介绍 本实验使用了PyTorch库来构建和操作神经网络模型,主要是关…...
华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...
iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...
