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

Linux- 网络编程初探

原始套接字(Raw Socket)

原始套接字(Raw Socket)是一种提供较低级别网络访问的套接字。通过使用原始套接字,应用程序可以直接发送或接收网络层如IP的数据包,或者传输层如TCP、UDP的段,而无需通过常规的套接字API提供的协议处理。

以下是原始套接字的一些关键点:

  1. 协议独立性:使用原始套接字,我们可以操作或构建自己的协议,或者直接与现有协议(如ICMP)交互。

  2. 绕过内核处理:通常,当发送或接收数据包时,操作系统内核会为我们处理很多细节,例如TCP的三次握手或IP头的填充。但是,使用原始套接字,我们可以直接构建或解析这些协议,从而绕过标准的内核处理。

  3. 特权:由于原始套接字提供了对网络的低级访问,使用它们通常需要特权,例如root权限。

  4. 应用场景

    • 网络诊断和测试工具:例如,ping使用原始套接字发送和接收ICMP回显请求和回显响应。
    • 定制协议的实现:例如,如果我们想实验一个新的传输层协议。
    • 安全研究和网络攻击:例如,执行某些类型的DoS攻击或网络扫描。
  5. 创建原始套接字:在Linux中,我们可以使用socket函数并为其提供AF_INET(对于IPv4)或AF_INET6(对于IPv6)以及SOCK_RAW来创建一个原始套接字。

  6. 手动头部处理:使用原始套接字,需要手动构建或解析协议头部。例如,如果我们正在发送一个TCP段,我们需要手动构建IP和TCP头部,并设置所有必要的字段。同样,当从一个原始套接字接收数据时,我们将获取整个数据包,需要自己解析它。

  7. 混杂模式:如果我们想使用原始套接字捕获一个接口上的所有流量(而不仅仅是发给特定地址的流量),我们需要将接口设置为混杂模式。

需要注意的是,虽然原始套接字提供了强大的功能,但也需要小心使用。手动处理协议细节容易导致错误,并可能引起网络问题或安全隐患。

Socket()

socket()函数是计算机网络编程中的核心函数之一,用于创建一个新的套接字。套接字是端到端的通信链路,是进程之间进行网络通信的主要手段。

下面详细介绍socket()函数:

函数原型

int socket(int domain, int type, int protocol);

参数

  1. domain(或称为family):指定使用哪种地址族。常见的选择包括:

    • AF_INET:IPv4 地址族。用于IPv4网络通信。
    • AF_INET6:IPv6 地址族。用于IPv6网络通信。
    • AF_UNIX:本地套接字(UNIX 域套接字)地址族。用于同一机器上的进程间通信。
  2. type:指定套接字的类型。常见的选择包括:

    • SOCK_STREAM:提供面向连接、可靠、双向的字节流服务。典型的协议有TCP。
    • SOCK_DGRAM:提供无连接的、不可靠的数据报服务。典型的协议有UDP。
    • SOCK_RAW:提供原始套接字访问,允许直接发送或接收协议如IP的数据包。
    • SOCK_SEQPACKET:提供面向连接的、可靠的、固定最大长度的记录序列。
  3. protocol:指定要使用的协议。通常,当给定了套接字的类型时,可以将此参数设置为0,让系统自动选择合适的协议。例如,当typeSOCK_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);
}

注意事项

  1. 创建套接字只是第一步。为了实际上进行通信,还需要其他函数,如bind(), listen(), accept(), connect(), send(), 和 recv(),来配置并操作这个套接字。
  2. 对于TCP服务端,通常在socket()之后会调用bind(), listen()accept()来绑定地址、监听连接和接受连接。
  3. 对于TCP客户端,通常在socket()之后会调用connect()来连接到服务器。
  4. 对于UDP,没有建立或接受连接的概念,所以只需创建套接字,然后可以直接使用sendto()recvfrom()进行通信。

socket()函数是网络编程中的基础,几乎所有的网络应用程序都会在某个地方使用它来开始其网络通信。

bind()

bind() 是套接字编程中的一个关键函数,用于将套接字与特定的IP地址和端口号绑定。它通常在服务器设置期间使用,以指定服务器将在哪个地址和端口监听即将到来的客户端连接。

函数原型:

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数:

  1. sockfd: 这是一个套接字描述符,它表示要绑定的套接字。

  2. addr: 这是一个指向struct sockaddr的指针,该结构定义了套接字的地址(IP和端口)。在实践中,通常使用特定于协议的结构(如struct sockaddr_in对于IPv4)来填充这个参数,并将其指针类型强制转换为struct sockaddr *

  3. 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;
}

常见错误和注意事项:

  1. Address already in use: 如果尝试绑定到已被另一个套接字使用的地址和端口,将会出现此错误。这通常发生在服务器崩溃并尝试重新启动,但由于之前的套接字仍处于“TIME_WAIT”状态,所以它不能立即绑定。使用setsockopt()SO_REUSEADDR可以帮助解决此问题。

  2. Permission denied: 通常,只有root用户才能绑定到低于1024的端口。

  3. 确保在绑定套接字之前填充了整个sockaddr_in结构,并正确设置了sin_familysin_addr.s_addrsin_port字段。

  4. 使用htons()函数确保端口号是网络字节顺序。

通过合理地使用bind()函数,开发人员可以确保他们的服务器监听特定的IP地址和端口,从而等待客户端的连接。

listen()

listen() 是套接字API中的一个函数,用于让一个套接字进入监听模式,从而能够接收来自客户端的连接请求。这是创建服务器应用程序的必要步骤之一。

函数原型:

int listen(int sockfd, int backlog);

参数:

  1. sockfd: 一个套接字描述符,它应该先前已经使用 socket() 创建并使用 bind() 绑定到一个特定的地址和端口。

  2. 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);

参数:

  1. sockfd: 这是一个处于监听模式的套接字描述符,通常先前已经通过 socket() 创建并通过 bind()listen() 函数配置。

  2. addr: 这是一个指向struct sockaddr的指针,当 accept() 调用返回时,这个结构将被填充与已经接受的连接的远程端(客户端)的地址信息。

  3. addrlen: 这是一个值-结果参数。在调用 accept() 之前,它应该被设置为addr指向的地址结构的大小。当函数返回时,addrlen 将被设置为实际的地址大小。

返回值:

  • 成功时,accept() 返回一个新的套接字描述符,代表与客户端的新连接。此新描述符应用于后续的所有通信(例如 send()recv() 调用)。

  • 失败时,返回 -1,并设置 errno 以指示出现的错误。

使用和注意事项:

  1. 在服务器应用程序中,通常在 listen() 函数调用后立即调用 accept(),等待客户端的连接。

  2. accept() 函数是阻塞的,这意味着它将等待,直到一个连接请求可用,除非套接字已被配置为非阻塞。

  3. 返回的新套接字描述符与原始的监听套接字是独立的。应使用新的套接字描述符进行与客户端的所有通信,并继续使用原始的监听套接字来接受其他连接请求。

  4. 通常,服务器将为每个接受的连接启动一个新的线程或进程以并行处理多个连接。

  5. addraddrlen 参数是可选的;如果我们不关心客户端的地址,我们可以设置这两个参数为 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);

参数:

  1. sockfd: 这是我们想要与远程主机连接的套接字的描述符。

  2. addr: 这是一个指向struct sockaddr的指针,包含我们想要连接的远程主机的地址信息。在实际应用中,通常使用特定于协议的结构(如struct sockaddr_in对于IPv4)并将其类型强制转换为struct sockaddr *

  3. addrlen: 这是地址结构的大小,例如对于IPv4地址,这通常是sizeof(struct sockaddr_in)

返回值:

  • 成功时,connect() 返回0。

  • 失败时,返回-1,并设置 errno 以指示错误原因。

使用和注意事项:

  1. 在客户端应用程序中,我们通常首先使用 socket() 函数创建一个套接字,然后使用 connect() 函数尝试与服务器连接。

  2. 如果 connect() 成功,客户端可以开始使用 send()recv() 或其他相关函数与服务器通信。

  3. 如果连接尝试失败,connect() 将返回-1。这可能是由于多种原因,例如服务器未在指定的地址和端口上运行,网络故障,或服务器拒绝连接。

  4. 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);

参数:

  1. sockfd: 这是一个已连接的套接字的描述符。

  2. buf: 这是一个指针,指向我们想要发送的数据的缓冲区。

  3. len: 这是buf中我们想要发送的数据的字节数。

  4. flags: 这是一个修改函数操作的标志集合。常见的标志包括 MSG_OOB (用于发送"out-of-band"数据) 和 MSG_NOSIGNAL (阻止在连接断开时发送SIGPIPE信号)。大多数情况下,我们可以简单地将此参数设置为0。

返回值:

  • 成功时,send() 返回实际发送的字节数。请注意,这可能少于我们请求发送的数量。

  • 失败时,返回-1,并设置 errno 以指示错误原因。

使用和注意事项:

  1. 在一个已连接的TCP套接字上使用send()之前,必须先成功地调用connect()(对于客户端)或accept()(对于服务器)。

  2. TCP是一个流协议,这意味着没有消息边界。连续的send()调用可能会在接收方看起来像一个连续的数据流,而不是单独的消息。

  3. 如果套接字是阻塞的(默认情况),send()可能会阻塞,直到有足够的网络缓冲区可用以发送数据。如果套接字是非阻塞的,而网络缓冲区不可用,则send()将立即返回-1,并将errno设置为EAGAINEWOULDBLOCK

  4. 在连接断开的套接字上调用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);

参数:

  1. sockfd: 这是一个已连接的套接字的描述符。

  2. buf: 这是一个指针,指向一个缓冲区,用于存储接收到的数据。

  3. len: 这是缓冲区的大小,即我们期望接收的最大字节数。

  4. flags: 这是一个修改函数操作的标志集合。一些常见的标志包括:

    • MSG_PEEK: 查看即将到来的数据,但不从队列中删除它。
    • MSG_WAITALL: 尝试接收指定的len字节。与默认行为不同,该标志会使函数等待,直到请求的字节数量可用或发生某些错误。
    • MSG_OOB: 用于接收 “out-of-band” 数据。

    在大多数常规操作中,我们可以简单地将此参数设置为0。

返回值:

  • 成功时,recv() 返回实际接收到的字节数。如果连接已关闭,返回0。

  • 失败时,返回-1,并设置 errno 以指示错误原因。

使用和注意事项:

  1. 在一个已连接的 TCP 套接字上使用 recv() 之前,我们需要先成功调用 connect()(对于客户端)或 accept()(对于服务器)。

  2. TCP 是一个流协议,这意味着没有消息边界。连续的 recv() 调用可能会接收到之前调用的数据的剩余部分。

  3. 如果套接字是阻塞的(默认情况),并且没有数据可用,recv() 会阻塞,直到数据可用。如果套接字是非阻塞的,并且没有数据可用,recv() 会立即返回-1,并将 errno 设置为 EAGAINEWOULDBLOCK

  4. 当连接断开或关闭时,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- 网络编程初探

原始套接字&#xff08;Raw Socket&#xff09; 原始套接字&#xff08;Raw Socket&#xff09;是一种提供较低级别网络访问的套接字。通过使用原始套接字&#xff0c;应用程序可以直接发送或接收网络层如IP的数据包&#xff0c;或者传输层如TCP、UDP的段&#xff0c;而无需通…...

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 …...

我想设计一套游戏的奖励系统,有什么值得注意的?

游戏上&#xff1a; 游戏成就系统的价值 游戏中的成就可以延长游戏时间&#xff0c;让玩家不仅仅是将游戏通关&#xff0c;而是必须完成游戏内所有挑战及发现秘密&#xff0c;这些成就可以与游戏本身的目标一致&#xff0c;也可以独立于游戏的主要或次要目标之外&#xff0c;…...

精通git,没用过git cherry-pick?

前言 git cherry-pick是git中非常有用的一个命令&#xff0c;cherry是樱桃的意思&#xff0c;cherry-pick就是挑樱桃&#xff0c;从一堆樱桃中挑选自己喜欢的樱桃&#xff0c;在git中就是多次commit中挑选一个或者几个commit出来&#xff0c;也可以理解为把特定的commit复制到…...

QT5|C++|通过创建子线程方式实现进度条更新

背景&#xff1a; 一开始是通过在主线程中写一个for循环&#xff0c;每次加1后睡眠1s进行进度条更新。但这样写的结果是 --> 无法动态显示进度条进度。后通过上一篇文章 [ QT5|C|通过信号槽机制实现进度条更新 ] 中的写信号槽机制实现。实现后 考虑了下有没有其他方式实现&a…...

基于mediasoup的webrtc server,性能压测时发现带宽利用率偏低(40%)

基于mediasoup的webrtc server&#xff0c;进行性能压测时发现&#xff0c;在1gbps的网络条件下&#xff0c;带宽利用率在40%(400Mbps)时&#xff0c;就会出现过高丢包率的(packet loss > 10%)的情况。这个结果是合理的吗&#xff1f;如果不合理&#xff0c;要如何提升性能&…...

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供给正电&#xff0c;PB5放置按键&#xff0c;按键一端接PB5,另一端接负极 void Key_Init(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //APB2总线连接着GPIOBGPIO_InitStructur.GPIO_Mode GPIO_Mode_IPU;GPIO_InitStructur.…...

MySQL作业:索引、视图、存储、函数

学生表&#xff1a;Student (Sno, Sname, Sex , Sage, Sdept) 学号&#xff0c;姓名&#xff0c;性别&#xff0c;年龄&#xff0c;所在系 Sno为主键 课程表&#xff1a;Course (Cno, Cname,) 课程号&#xff0c;课程名 Cno为主键 学生选课表&#xff1a;SC (Sno, Cno, Score) …...

漫谈:C语言 C++ 所有编程语言 =和==的麻烦

这次不只是C语言很麻拐&#xff0c;是所有编程语言都很麻拐了。 赋值和比较是编程语言最基本的操作之二&#xff0c;C和所有类C语言都使用“”和“”来分别表示赋值和比较。 数学上等号“”是个单一的概念&#xff0c;含义是“相等”&#xff0c;左右两边是等价的&#xff0c;很…...

十五、异常(1)

本章概要 异常概念基本异常 异常参数 异常捕获 try 语句块异常处理程序终止与恢复 Java 的基本理念是“结构不佳的代码不能运行”。 改进的错误恢复机制是提高代码健壮性的最强有力的方式。错误恢复在我们所编写的每一个程序中都是基本的要素&#xff0c;但是在 Java 中它显得…...

Redis 哨兵模式搭建教程

一、介绍 本文实战搭建一主两从三哨兵&#xff0c;通过使用哨兵模式&#xff0c;可以有效避免某台服务器的 Redis 挂掉出现的不可用问题&#xff0c;保障系统的高可用。 本文通过虚拟机搭建的三台 Centos7 服务器进行测试&#xff0c;使用的 Redis 版本为 6.25。 二、准备环…...

【C++】gnustl_static 与 c++_shared 的区别

参考&#xff1a;GNU与cSTL的区别与联系-爱代码爱编程​ gnustl_static 与 c_shared 的区别&#xff1a; 不同版本的 STL TSL是一个与STL兼容的多线程支持库。 STLport是一个可移植、高度兼容的STL实现。 SGI STL是最早的STL实现之一&#xff0c;对STL的发展起到了重要的作用…...

怎样选择第三方检测机构获取功能测试报告?

数字化时代&#xff0c;软件给人们的生活带来了越来越多的便利&#xff0c;产品功能测试也成为了软件开发方在研发时的重要环节&#xff0c;这关系到用户使用产品的体验感。所以做好软件功能测试对把控产品质量有着很大影响&#xff0c;通过有效的功能测试能够发现系统潜在的问…...

【@PostConstruct、 @Autowired与构造函数的执行顺序】

PostConstruct、 Autowired与构造函数的执行顺序 一、PostConstruct介绍二、Spring框架中在bean初始化和销毁时候执行实现方式三、项目验证1.MyServiceImpl2.测试结果3. 项目源码 最近对同事代码进行codeReview时候发现用PostConstruct注解&#xff0c;特地对此注解执行顺序进行…...

用vite搭建vue3+ts项目

一、环境搭建 1&#xff1a;首先vite环境安装 npm init vite 或者 yarn init vite 2&#xff1a;输入项目的名字,在这里用vue3_test ? Project name: › vite-project 3&#xff1a;选择项目类型&#xff0c;这里选择Vuets 4&#xff1a;出现下图&#xff0c;初始化基本…...

前端求职指南

简历求职指南 为什么没有面试&#xff1f; 1、简历写的不好 2、简历投递不好 简历的定义是什么&#xff1f; 是求职者向未来雇主展示自己专业技能和职业素养的自我推销工具&#xff0c;以找到工作为目的。 什么时候改简历&#xff1f; 每半年或一年更新一次工作中的成长 再工…...

datax同步数据翻倍,.hive-staging 导致的问题分析

一、背景 有同事反馈 Datax 从 Hive 表同步数据到 Mysql 数据翻倍了。通过查看 Datax 任务日志发现&#xff0c;翻倍的原因是多读取了 .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库来构建和操作神经网络模型&#xff0c;主要是关…...

【Unity】LODGroup 计算公式

Unity 在配置 LodGroup 时&#xff0c;其分级切换的计算方法是按照物体在相机视野中占据的比例计算的。在运行时&#xff0c;如果相机视野范围&#xff08;Field of View&#xff09;没有改变&#xff0c;那么这个值可以直接换算成物体距离相机的距离。这里就讨论下如何计算得到…...

【数学建模】2023华为杯研究生数学建模F题思路详解

强对流降水临近预报 我国地域辽阔&#xff0c;自然条件复杂&#xff0c;因此灾害性天气种类繁多&#xff0c;地区差异大。其中&#xff0c;雷雨大风、冰雹、龙卷、短时强降水等强对流天气是造成经济损失、危害生命安全最严重的一类灾害性天气[1]。以2022年为例&#xff0c;我国…...

docker 安装 nessus新版、awvs15-简单更快捷

一、docker 安装 nessus 参考项目地址&#xff1a; https://github.com/elliot-bia/nessus 介绍&#xff1a;几行代码即可一键安装更新 nessus -推荐 安装好 docker后执行以下命令 #拉取镜像创建容器 docker run -itd --nameramisec_nessus -p 8834:8834 ramisec/nessus …...

使用API Monitor工具巧妙探测C++程序中监听某端口的模块

目录 1、问题说明 2、API Monitor工具介绍 2.1、API Monitor主要用途 2.2、如何使用API Monitor工具 3、使用API Monitor监测程序对bind函数的调用&#xff0c;定位启用2620端口的模块 3.1、为啥要监控socket API函数bind 3.2、编写演示代码进行说明 3.3、使用API Moni…...

vue-grid-layout移动卡片到页面底部时页面滚动条跟随滚动

问题描述&#xff1a;默认情况下 vue-grid-layout 移动卡片到页面底部时页面滚动条并不会跟随卡片滚动。 问题解决&#xff1a; 在 grid-item中的move事件中&#xff0c;获取到当前移动的元素&#xff0c;并使用scrollIntoView方法来实现滚动条跟随。 代码如下&#xff1a; c…...

git查看自己所在的分支

很多时候可能大家不太想切换其他工具&#xff0c;又不知道自己是否在自己需要操作的分支 可以直接终端执行 git branch此时 他就会在终端将所有的本地分支输出出来 并特殊标注自己所在的分支 这样我们就可以进一步去做自己想要做的操作了 当然 随着各种编辑器的发展 这个命令…...

竞赛 基于视觉的身份证识别系统

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于机器视觉的身份证识别系统 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng-sen…...

Redis的softMinEvictableIdleTimeMillis和minEvictableIdleTimeMillis参数

背景&#xff1a; Redis的softMinEvictableIdleTimeMillis&#xff0c;minEvictableIdleTimeMillis是一个令人疑惑两个参数&#xff0c;特别是当它和minIdle组合起来时就更难理解了&#xff0c;本文就来梳理下他们的之间的关系 softMinEvictableIdleTimeMillis&#xff0c;mi…...

向量数据库库Milvus Cloud2.3 的QA问题

1. Milvus 从 2.2.x 升级至 2.3.x 的最大变化是什么? 如果用一句话来总结,那就是使用的场景更加丰富了。具体可以从两个方面来体现,即部署环境和用户的使用感。 例如,从部署环境来看,Milvus 原来只支持 X86 架构的 CPU,版本升级后,不仅可以支持 GPU,还能够支持 ARM 架构…...

嵌入式 - 经典的有刷电机和先进的无刷电机

自从无刷直流电机诞生&#xff0c;“古老的”有刷电机就开始没落&#xff0c;但它依然是低成本应用的可靠选择&#xff0c;并且实现起来简单。 在有刷电机中&#xff0c;磁极方向的跳转是通过移动固定位置的接触点来完成的&#xff0c;该接触点在电机转子上与电触点相对连接。这…...