【C语言】socket编程接收问题

一、recv()函数接收到的返回值为0表示对端已经关闭

在TCP套接字编程中,通过recv()函数接收到的返回值为0通常表示对端已经关闭了套接字的发送部分。这是因为TCP是一个基于连接的协议,其中有定义明确的连接建立和终止流程;当对端调用close()或者shutdown(socket, SHUT_WR)来关闭套接字或其发送部分时,本端的recv()函数将返回0。
在大多数情况下,接收到0个字节足够用作连接终止的信号。一个好的实践是检查recv()是否返回0,并随后关闭套接字。
额外使用send()来确认连接的关闭并不是一个通用的解决方案。尝试向一个已经报告连接关闭的套接字发送数据可能会导致不符合预期的行为。例如:
1. 如果另一方关闭了连接,send()将可能导致SIGPIPE信号的生成,该信号默认会终止应用程序,除非信号被捕获或者使用send函数的MSG_NOSIGNAL标志。
2. send()可能返回-1并设置errno为EPIPE,表示对端套接字被关闭。
3. 在某些情况下(就像网络问题或关闭时的临时状态),send()可能会成功(返回非零值),在这种情况下,并不能使用它来判断对端是否关闭了连接。
对于验证TCP连接是否确实被关闭,建议的方式通常是:
- 监控输入流。如果recv()返回0,则对端正常关闭了连接。
- 在接收0后,可以调用shutdown()来禁用套接字的接收部分,或直接调用close()来关闭套接字,并进行必要的资源清理。
总而言之,如果recv()返回0,则可以合理地假设对端的套接字已被关闭,没有必要进一步使用send()来做确认。不过,连接异常关闭(如网络故障、对端崩溃等)的情况除外,这些情况可能需要通过错误检测机制(例如心跳包、超时检测等)来处理。

二、recv() 函数返回 0时,UDP和TCP对比

在 Linux 和大多数操作系统上,使用 TCP 协议的 sokcet 编程时,recv() 函数返回 0 通常表示连接已经被对端正常关闭(graceful shutdown)。这意味着对端调用了 close() 或者 shutdown(),并发送了一个完整的 TCP FIN 分组来优雅关闭连接。
对于 TCP 套接字来说,无法发送长度为 0 的数据包,因为 TCP 是一个面向流的协议,它没有消息边界。所以,如果发送方调用 send() 或 sendto() 函数,即使指定了长度为 0,也不会发送任何数据包,接收方也就不会在 recv() 调用中返回 0。
UDP 协议是不同的,它是一个基于消息(datagram)的协议,发送方可以发送一个空的数据报,这时候接收端的 recv() 或 recvfrom() 会返回 0,但这并不代表连接关闭,因为 UDP 是无连接的协议。在这种情况下,返回 0 就是实际接收到的数据长度,而不是一个连接关闭的指示。
如果在使用 TCP 协议中接收到了 recv() 返回的 0,可以安全地假定对端已关闭连接。如果处理的是 UDP,那么需要根据上下文来判断,因为这只是表明这个特定的数据包是空的,并不表示后续不会有数据到来或者远端已经关闭。

三、为什么使用recv而不是recvfrom接收TCP数据?

在编程中使用 recv 和 recvfrom 函数是针对套接字(socket)中数据传输的两种不同的情况。这两个函数通常用在网络编程中,它们都属于 BSD sockets API,用于从套接字接收数据,但它们的应用场景和具体行为有所不同。

1. recv:

   recv 函数用于接收一个已经连接的套接字(通常是TCP套接字)上的数据。它的原型如下(以C语言为例):

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

   其中,sockfd是指向一个已建立连接的套接字的文件描述符,buf是接收数据的缓冲区,len是缓冲区的大小,flags是一组指定接收行为的标志,位掩码。

2. recvfrom:

   recvfrom 函数通常用于无连接的套接字(如UDP套接字),可以接收数据并获取发送方的地址。其原型如下:

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                    struct sockaddr *src_addr, socklen_t *addrlen);

    recvfrom 与 recv 相比多出两个参数:src_addr是发送方地址结构的指针,addrlen是地址长度的指针。这使得 recvfrom 能够在接收数据的同时,返回发送方的地址信息。
   对于已经连接的套接字(TCP),recv可以确定数据的来源,因此不需要 src_addr 和 addrlen 参数。但是对于未连接的套接字(UDP),这些参数允许接收方得知每个数据报的源地址信息。

当处理TCP数据时,通常使用 recv 函数,而不是 recvfrom,主要出于以下原因:
- 在TCP传输中,一旦客户端与服务器建立了连接,数据的发送和接收方地址就是确定的。因此,没有必要在每次调用接收函数时都提供地址信息,这可以简化代码并提高效率。
- TCP是一个面向连接的协议,它通过三次握手过程来建立连接。`recv` 更适合接收已经建立连接的、来自特定远端的数据。
- 使用 recv 可以使TCP代码更清晰和直接,而使用 recvfrom 可能会造成混乱,因为它更多地用于处理无连接协议,比如UDP。
- recv 的参数比 recvfrom 少(不需要地址参数),这在已连接的TCP流量中可以减少不必要的参数管理。
简而言之,在处理TCP数据时,推荐使用 recv,因为它正是为处理已连接的流式(stream)套接字设计的。相反,recvfrom用于无连接的数据报(datagram)套接字,它提供了获取发送方信息的能力,这在处理UDP数据时是必要的。

四、为什么通常使用recvfrom接收UDP数据

对于 UDP —— 一个无连接的协议 —— 接收函数需要能够处理来自不同发送方的数据包,并能提供发送者的信息。recvfrom是设计用来处理这种情况的,因为它允许你接收数据和源地址信息。在 UDP 通信中,每个接收到的数据包可能来自不同的发送方,而 recvfrom 可以知道每个数据包来自哪里。
反之,recv 函数虽然也可以用于 UDP,但它不提供这种灵活性,因为它无法返回发送方地址。所以,即便在技术上可以用 recv 接收 UDP 数据,但在实际应用中,使用 recvfrom 通常更加合适和直接。

在使用socket进行网络编程时,接收UDP数据包确实可以只用recv函数,而不需要获取发送方的地址信息。recv函数允许从与socket关联的连接中接收数据,但不提供关于数据包来源的信息。
不过,UDP作为一种无连接的协议,通常会使用recvfrom函数,它允许接收方在接收数据时同时获取发送方的地址信息。这是因为UDP数据包是独立的消息,每个数据包都可能来自不同的发送方,而知道每个数据包的来源是很重要的,尤其是在服务器需要处理来自多个客户端的数据的场景中。
例如,一个使用UDP的回声服务器需要能够接收来自许多不同客户端的消息,并将相应的回复发送回正确的发送方。在这种情况下,如果不使用recvfrom来获取发送方的具体地址信息,服务器则无法知道该将响应发送给哪个客户端。
如果应用场景中,服务器只接收来自一个固定来源的数据,或者发送方的地址信息并不重要,那么使用recv可能是可以接受的。但在大多数情况下,了解数据包的来源很重要,因为它可以帮助应用程序处理各种网络事件,包括数据路由、认证、日志记录或其他基于网络信息的操作。
总而言之,使用recvfrom而不仅是recv,主要是因为UDP是一种无连接的、面向消息的协议,每个数据包都是独立传输的,并且通信的双方没有固定的连接状态。获取发送方的地址信息对于确保数据能够被正确处理和响应是非常重要的。 

五、错误检测

在网络编程中,对端套接字的关闭通常通过recv()函数返回0来检测,表明对端执行了正常的套接字关闭操作。但是,如果连接异常关闭(如网络故障、对端崩溃等),这种情况下recv()可能返回-1或者遇到ECONNRESET错误。这时需要实现一套更健壮的错误检测机制来监控和处理这类情况,以下是一些常见的方法:

1. 心跳机制(Keepalive):

   心跳是一种定期发送的轻量级数据包,用以检测对端是否仍然响应。如果一定时间内没有接收到心跳响应,那么可以认为对端可能出现了问题。心跳包常用于检测半开连接或非活动连接。
   在TCP中,你可以通过设置套接字选项启用TCP的keepalive机制:

   int optval = 1;
   setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));

 TCP的keepalive机制是一种检测对端是否仍然可达的功能,其在网络编程中被用来监测TCP连接是否断开,但没有正常关闭。如果启用了keepalive,即使没有数据传输,TCP也会定期发送探测包以保持连接的活跃状态。如果在多次探测后没有收到响应,则认为对端不可达,连接将被终止。
启用TCP keepalive的影响:
检测到死连接:启用keepalive可以帮助您检测到网络断开或对端崩溃等情况,这些情况下数据传输已经停止但对端没有正确关闭TCP连接。
节省资源:通过检测和关闭无用的连接,可以更有效地利用服务器资源,例如端口和内存。
透明重连: 在某些场合,如果检测到连接断开,应用程序可能希望自动重新建立连接,这在启用了keepalive的情况下更容易实现。
编程处理的差异:
设置了TCP keepalive后,一般不需要在程序中作出特别处理。TCP堆栈自动处理keepalive,包括发送探针和检测死链。程序只需关注正常的I/O操作即可。然而,有两点值得注意:
异常处理:keepalive探测发现连接已死时,TCP堆栈会断开连接。这将导致任何试图通过这个套接字进行读写操作的尝试失败,并产生异常或错误。程序需要能够捕获并妥善处理这些错误。
keepalive参数配置:某些操作系统允许进一步自定义keepalive行为,如探测频率、重试次数和空闲时间等。可以通过类似`setsockopt()`的调用进行更细节的设置。
例如,在Linux上,可以设置更多的keepalive选项:

int keepalive = 1; // 开启keepalive属性
int keepidle = 60; // 若60秒内没有任何数据交互,则进行探测
int keepinterval = 5; // 探测时发探测包的时间间隔为5秒
int keepcount = 3; // 探测尝试的次数。如果第1次探测包就收到响应则后2次的不再发。

setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive , sizeof(keepalive));
setsockopt(sockfd, SOL_TCP, TCP_KEEPIDLE, (void*)&keepidle , sizeof(keepidle));
setsockopt(sockfd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepinterval , sizeof(keepinterval));
setsockopt(sockfd, SOL_TCP, TCP_KEEPCNT, (void *)&keepcount , sizeof(keepcount));

请注意,根据使用的操作系统和网络库,API的确切名称和参数可能会有所不同。另外,不应当滥用keepalive,因为即使是空闲连接,也会增加网络流量并消耗一定的服务器资源。适当使用keepalive能够为网络程序增加一层鲁棒性,但也需要在性能和资源使用之间找到平衡点。

2. 超时检测(Timeout):

   对于连接和读写操作,可以设置超时时间。如果在指定时间内没有数据传输,那么可以假定有错误发生,连接可能已失效,然后关闭连接。
   setsockopt()函数同样可以用来为`recv()`设置超时:

   struct timeval tv;
   tv.tv_sec = 10;  // 10秒超时
   tv.tv_usec = 0;
   setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(struct timeval));

3. 应用层确认消息:

   对于某些协议,可能需要在应用层实现确认(ACK)机制。每次数据传输完毕时,接收方都需要发送一个确认消息给发送方。如果发送方在设定的超时时间内没有收到确认消息,它将重试发送该数据或断开连接。

4. 重试策略:

   如果操作(如连接请求或数据发送)失败,可以采取重试策略,在每次重试之间增加延时,这通常被称为指数退避。

5. 异常检测和状态监控:

   在多线程或异步编程中,可以使用一个监控线程或服务来检测客户端和服务端的异常状态,并响应相应的错误。这可能涉及到周期性地检查套接字的连接状态,或者实现更复杂的状态机制来管理连接。
实现这些机制时,需要考虑到检测频率和网络开销的权衡,以及如何在检测到异常时采取有效的恢复措施。另外,还需要确保任何状态或数据的同步操作在多线程环境中是线程安全的。在多线程或异步编程中,使用一个监控线程或服务来检测客户端和服务端的异常状态是一种常见的设计模式,它可以帮助维护系统的健壮性。以下是一般用于实现这种监控功能的步骤:
1. 定义健康检查API:
   为客户端和服务端实现标准的健康检查API。这些API可以返回简单的状态码或更复杂的健康信息,以便监控服务可以理解被监控服务的状态。
2. 设置监控服务:
   实现一个监控服务或线程,定期向客户端和服务端发出健康检查请求。监控频率可以根据应用场景和系统需求来决定。
3. 检测并记录状态:
   监控服务接收到健康检查的响应后,记录客户端和服务端的状态。如果发现异常状态,应记录详细信息,以备分析和问题排查。
4. 触发报警和响应:
   当检测到异常状态时,监控服务会触发报警。报警可以是日志、电子邮件、短信或者集成企业监控系统的通知。同时,监控服务可以根据设定的策略执行响应动作,比如重启服务、断开客户端连接或者将任务转移到备用服务。
5. 实现异常恢复逻辑:
   当检测到异常时,除了报警之外,可以设置策略和逻辑来试图自动恢复服务。例如,如果是一个临时的网络问题,可能只需要重试连接;如果服务崩溃了,可能需要自动重启服务等。
6.优雅地处理并发问题:
   因为涉及到多线程或异步操作,监控服务自身应当能够优雅地处理并发问题,比如使用线程安全的数据结构,合理地加锁,以及使用并发编程的最佳实践。
一个简单的监控线程示例如下:

import threading
import requests
import time

# 健康检查函数
def health_check(target_url):
    try:
        response = requests.get(target_url)
        if response.status_code == 200:
            print(f"{target_url} is healthy.")
        else:
            print(f"Warning: {target_url} returned status code {response.status_code}")
            # 处理异常情况,例如重启服务、发送报警等
            handle_error(target_url, response.status_code)
    except requests.RequestException as e:
        print(f"Error checking {target_url}: {e}")
        # 处理连接错误
        handle_error(target_url, 'connection error')

# 异常处理函数
def handle_error(target_url, error):
    # 这里添加异常处理逻辑
    pass

# 监控线程主逻辑
def monitor_thread(targets, interval):
    while True:
        for target in targets:
            health_check(target)
        time.sleep(interval)

# 启动监控线程
targets = ['http://service1/api/health', 'http://service2/api/health']
monitoring_interval = 30  # 检查间隔为30秒

# 在后台启动监控线程
monitor_thread = threading.Thread(target=monitor_thread, args=(targets, monitoring_interval))
monitor_thread.daemon = True
monitor_thread.start()

# 主程序可以继续进行其他工作
# ...

上面的Python代码示例创建了一个在后台运行的监控线程,该线程会定期检查一组服务地址的健康状况,并根据服务的状态执行错误处理逻辑。
这只是一个监控机制的简单实现,适用于不需要复杂监控策略的小型系统。在实际应用中,可能需要使用更完善和健壮的监控系统,如Prometheus、Nagios、Zabbix等,以及与之配套的报警系统如Alertmanager。 
在多线程或异步编程中,异常状态通常确实是指应用层面的,而非底层网络或操作系统层面的。应用层的异常状态是指由程序逻辑或程序运行时引起的异常状况,这些异常状况可能需要特殊处理,包括但不限于:
1. 超时错误:当客户端发起请求时,如果服务端在特定的时间内没有作出响应,可能会触发超时异常。
2. 服务不可用:当服务端因为各种原因(如维护、崩溃、过载)无法提供服务时。
3. 资源枯竭:例如,线程池或连接池用完,或者系统内存、CPU资源不足。
4. 功能错误:服务端在处理请求时发生逻辑错误,返回错误的结果。
5. 协议错误:违反应用层协议或错误的数据格式,可能导致无法正确解析请求或响应。
监控线程或服务会在这些异常状态发生时进行以下操作:
- 记录和通知:它会记录异常状态的详细信息,并可能通过邮件、短信或者其他方式通知开发者或系统管理员。
- 尝试恢复:对于一些已知的异常状态(例如,临时的网络问题),监控服务可能尝试重新启动服务或重新发起请求。
- 自动扩容:当服务因为负载过高无法处理额外的请求时,监控服务可能触发自动扩容机制,启动更多的服务实例以处理增加的请求负荷。
- 限流和降级:在系统资源有限或服务不稳定时,监控服务可能实现限流策略,减少服务的访问量,或者执行降级策略,保证核心业务不受影响。
- 故障隔离:当检测到服务异常时,监控服务可能会将异常的服务实例或节点隔离,防止故障扩散。
监控线程或服务是提高系统稳定性和可用性的重要组成部分。通过能够检测和响应应用层的异常状态,可以提前预防潜在的问题,或者在问题发生时迅速做出反应,从而最小化对用户的影响。 

在TCP通信中,当recv()调用返回0时,通常意味着对端的套接字已经正常关闭了其连接,即发送方执行了一个正常的关闭操作(调用了close()或shutdown())。这表明连接被正常地终止,没有遗留的数据未被接收。
如果连接异常关闭,如由于网络故障、对端应用程序崩溃、硬件故障等,那么recv()可能会表现不同。在这些情况下,如果异常发生在recv()之前,recv()可能会:
1. 阻塞,直到超时(如果有设置超时的情况),因为它在等待数据到达,但是由于连接已经失效,数据永远不会到达。
2. 返回一个错误,通常是通过抛出一个异常或返回一个特殊的错误码,这取决于所使用的编程语言和网络库。在Python的socket模块中,这通常会以一个socket.error异常的形式发生。
在某些系统上,当对端非正常关闭连接时(即发送了RST包而非正常的FIN包),recv()将返回一个错误而非0。处理这种情况的确切方式取决于特定的操作系统和网络栈实现。
为了确保网络程序的健壮性,编写网络代码时应当:
- 正确处理recv()返回0的情况,作为连接正常关闭的信号。
- 捕获和处理recv()过程中可能出现的异常或错误。
- 实现适当的心跳包、超时检测和重连策略,以便在连接丢失时能及时发现并采取相应措施。

六、recv函数终止应用程序

在Linux C语言socket编程中,recv函数通常不会直接终止应用程序。它是一个系统调用,用于从一个套接字中接收数据。如果出现错误或异常情况,recv会返回一个错误码,而不是终止应用程序。
然而,有一些情况可能导致应用程序异常结束或被终止,这些通常与recv的错误处理方式有关:
1. 未捕获的信号: 如果在recv调用过程中收到了一个信号,并且该信号没有被捕获或者其处理函数决定退出,那么应用程序可能会终止。常见的例子是SIGINT`(通常是由用户按下Ctrl+C产生)和SIGPIPE(在试图写入一个已关闭的连接时产生)。
2. 未检查的错误码: 如果recv由于某种错误返回了一个负的错误码,而应用程序没有正确检查并处理这个错误,那么接下来的操作可能会基于无效的数据工作并抛出其他错误,这有时会导致应用程序异常结束。
3. 非阻塞套接字: 如果套接字被设置为非阻塞模式,而且在调用recv时没有可读的数据,recv会返回一个错误(通常是EWOULDBLOCK或EAGAIN)。如果不适当地处理这种情况,应用程序可能会进入一个忙循环,可能导致程序使用大量CPU资源、变得不响应,甚至最终因为系统监控或管理员干预而被结束。
4. 程序逻辑错误: 如果在异常处理逻辑中存在错误,或者recv返回值的后续处理代码有问题,这些都可能导致应用程序崩溃。
在编写与recv相关的代码时,应该始终对返回值进行检查,对于任何可能的错误值都要有相应的错误处理逻辑。这样可以避免不必要的程序终止,并且确保在出现错误时能够有序地清理资源并提供适当的错误反馈。此外,理解和处理信号也是确保程序稳定运行的关键部分。 在使用Linux C语言进行socket编程时,处理recv函数的返回值时应当考虑三种主要的情况:
1. 数据成功接收: 当recv函数返回一个正整数时,这表明你成功地从对端接收到了这么多字节的数据。
2. 连接正常关闭: 当recv函数返回0时,这意味着对端已经关闭了连接。在这种情况下,应该结束对这个socket的操作,进行清理工作并关闭自己的socket。
3. 错误发生: 当recv函数返回-1时,这表明发生了一个错误。可以通过检查errno变量来获取错误的具体信息,并采取恰当的错误处理措施。
以下是一个简单的示例代码,展示了如何处理`recv`的不同返回值:

#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#define BUFFER_SIZE 1024

int main() {
    int socket_fd; // 假定这个socket已经被创建并连接到了一个服务端
    char buffer[BUFFER_SIZE];
    ssize_t bytes_received;

    // 接收数据
    bytes_received = recv(socket_fd, buffer, BUFFER_SIZE, 0);

    if (bytes_received > 0) {
        // 成功接收数据
        printf("Received %zd bytes: %s\n", bytes_received, buffer);
    } else if (bytes_received == 0) {
        // 对端关闭连接
        printf("Connection closed by peer.\n");
    } else {
        // 发生错误
        fprintf(stderr, "recv failed: %s\n", strerror(errno));
        if (errno == EAGAIN || errno == EWOULDBLOCK) {
            // 这可能是非阻塞socket的正常情况
        } else {
            // 处理其他错误类型,可能需要关闭socket
        }
    }

    // 关闭socket
    close(socket_fd);

    return 0;
}

在上面的代码中,我们首先检查recv返回的字节数。如果返回的是一个正数,我们打印出接收到的数据。如果返回值是0,我们知道连接已经正常关闭。如果返回值是-1,我们使用strerror函数来打印错误信息。
在实际的网络应用程序中,可能还需要处理网络超时的情况,复杂的错误恢复逻辑,以及可能的非阻塞I/O操作。还需要注意,当在多线程环境中使用sockets时,需要考虑同步和并发问题。
错误处理很大程度上取决于具体的应用逻辑,但应该包括记录错误、尝试重新连接以及合适的时候关闭socket等逻辑。

七、UDP的sendto/recvfrom返回错误与TCP的不同

在Linux中使用C语言编写的UDP套接字编程中,UDP协议是无连接的,这意味着每个数据包独立发送,没有建立持久连接的概念。由于UDP不跟踪连接状态,因此即使对端套接字已关闭,sendto函数通常仍会成功返回而不是报错。
当您使用sendto发送一个UDP数据报时,数据报被发送到指定的目的地址和端口,但是发送方并不知道这个目的地是否有一个活跃的接收者。数据发送后,除非底层网络问题或目的地址错误导致网络栈返回错误,否则sendto通常只是将数据报提交给网络堆栈然后返回。
然而,如果发送到一个不存在的地址或端口上,并且操作系统能够立即确定这一点(例如,没有这样的网络),sendto可能会返回错误并设置errno,例如ENETUNREACH(网络不可达)或EHOSTUNREACH(无法到达主机)。
不过,如果多次向一个关闭的套接字发送数据,该网络协议可能会最终意识到没有接收方,并可能通过ICMP(Internet控制消息协议)消息如“目的地不可达”通知您的系统。但是,由于这个过程是异步的,sendto不会直接因此报错。
在Linux C语言socket编程中,处理UDP通信时,sendto() 和 recvfrom() 的行为与 TCP 有些不同,因为 UDP 是无连接的协议。
1. 如果使用 sendto() 向一个已经关闭的对端发送UDP数据:
由于UDP是无连接的协议,当使用 sendto() 发送数据时,并不会去检查对端是否存在或者已关闭。sendto() 通常会成功地返回发送的字节数,即使对端已关闭。不过,如果对端主机完全不存在或某些特定网络错误发生,可能会收到一个 ICMP 错误消息,但这通常不会通过sendto()本身报告,除非已经设置了socket选项来接收ICMP错误。
2. 如果在对端关闭后使用 recvfrom() 接收数据:
由于UDP是无连接的,所以即使发送方停止发送数据,recvfrom() 通常也不会报告错误,它将阻塞等待直到接收到新的数据报文,或者返回错误如果有本地错误发生(如套接字已被本地关闭)或者遇到超时(如果设置了非阻塞或超时选项)。recvfrom() 不会因为远程端点已关闭就返回错误,因为UDP协议本身根本不跟踪连接状态。
综上所述,由于UDP是无连接的,不能通过 sendto() 和 recvfrom() 来得知对端socket是否已经关闭。如果需要检查对端是否活跃,需要在应用层协议中实现心跳或其他机制来确认对端的状态。 

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

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

相关文章

matlab appdesigner系列-仪器仪表1-圆形仪表、半圆形仪表、90度仪表、线性仪表

圆形仪表、半圆形仪表、90度仪表、线性仪表&#xff0c;是4种仪表的不同样式。 示例&#xff1a;用滑块&#xff0c;来调整仪表 操作步骤&#xff1a; 1&#xff09;将圆形仪表、半圆形仪表、90度仪表、线性仪表、滑块拖拽到画布上 2&#xff09;设置滑块的回调函数 changin…

MySQL 覆盖索引

目录 一、什么是索引 二、索引的有哪些种类&#xff1f; 三、InnoDB的不同的索引组织结构是怎样的呢&#xff1f; 四、什么是覆盖索引 五、如何使用是覆盖索引&#xff1f; 六、如何确定数据库成功使用了覆盖索引呢 总结&#xff1a; 一、什么是索引 索引&#xff08;在 …

JVM篇:垃圾回收算法

标记清除 通过遍历GC Root后得到不再被引用的对象&#xff0c;对没被引用的对象做一个标记处理&#xff0c;然后对其进行清除。 优点&#xff1a;速度快 缺点&#xff1a;会产生内存碎片&#xff0c;可能会导致空闲的内存足够保存对象&#xff0c;但由于不连续而保存失败。 标…

阿里云部署配置幻兽帕鲁Palworld联机服务器详细教程

阿里云作为国内领先的云计算服务提供商&#xff0c;为企业和个人提供了丰富的云服务。本文将为大家详细介绍如何在阿里云上配置幻兽帕鲁Palworld联机服务器&#xff0c;以便与更多玩家共同体验游戏的乐趣。 第一步&#xff1a;登录服务器创建页 1、进入幻兽帕鲁联机服务快速部…

vue3添加pinia

概述&#xff1a;Pinia 是一个专为 Vue.js 开发的状态管理库。Vue.js 是一个流行的 JavaScript 框架&#xff0c;用于构建用户界面。Pinia 旨在提供一个简单、灵活且性能高效的状态管理方案&#xff0c;使开发者能够更容易地管理应用的状态。 以下是 Pinia 的一些特点和概念&a…

技术变革下职业危机

方向一&#xff1a;技术变革 1.人工智能&#xff08;AI&#xff09;&#xff1a;AI技术的快速发展正在改变各个行业。AI在医疗诊断、金融分析、客户服务以及物流管理等方面都有广泛应用&#xff0c;提高了效率和准确性。但同时也引发了一些道德和道德问题&#xff0c;比如隐私…

###C语言程序设计-----C语言学习(4)#

前言&#xff1a;感谢老铁的浏览&#xff0c;希望老铁可以一键三连加个关注&#xff0c;您的支持和鼓励是我前进的动力&#xff0c;后续会分享更多学习编程的内容。现在开始今天的内容&#xff1a; 一. 主干知识的学习 1.字符型数据 &#xff08;1&#xff09;字符型常量 字…

HCS-华为云Stack-FusionSphere

HCS-华为云Stack-FusionSphere FusionSphere是华为面向多行业客户推出的云操作系统解决方案。 FusionSphere基于开放的OpenStack架构&#xff0c;并针对企业云计算数据中心场景进行设计和优化&#xff0c;提供了强大的虚拟化功能和资源池管理能力、丰富的云基础服务组件和工具…

实体关系抽取与属性补全的技术浅析

目录 前言1. 实体关系抽取2 实体关系抽取的方法2.1 基于模板的方法2.2 基于监督学习的关系抽取2.3 基于深度学习的关系抽取2.4 基于预训练语言模型的关系抽取 3 属性补全3.1 属性补全任务简介3.1 抽取式属性补全3.2 生成式属性补全 4 未来发展趋势结语 前言 在信息爆炸时代&am…

[网鼎杯 2018]Fakebook1

join一个用户后&#xff0c;点进去发现是这样的 查看这个页面的源代码&#xff0c;发现一个base64编码后的字串 decode之后就是我们join新用户时填入的blog网址 那我们是不是可以通过填入存储flag的地址&#xff0c;从而回显出来呢&#xff1f;当然&#xff0c;先按照常规sqli…

【Go】深入理解 Go map:赋值和扩容迁移 ①

文章目录 map底层实现hmapbmap map hash冲突了怎么办&#xff1f; map扩容触发扩容时机扩容小结为什么map扩容选择增量&#xff08;渐进式扩容&#xff09;&#xff1f;迁移是逐步进行的。那如果在途中又要扩容了&#xff0c;怎么办&#xff1f; map翻倍扩容原理 map写入数据内…

LC每日一题记录 2861. 最大合金数

题干 思路 所有合金都需要由同一台机器制造&#xff0c;因此我们可以枚举使用哪一台机器来制造合金。 对于每一台机器&#xff0c;我们可以使用二分查找的方法找出最大的整数 xxx&#xff0c;使得我们可以使用这台机器制造 xxx 份合金。找出所有 xxx 中的最大值即为答案。 代…

【HTML 基础】文档结构

文章目录 1.<!DOCTYPE>2. <html>3. <head>4. <body>总结 HTML 文档的结构是构建网页的基础&#xff0c;通过合理使用标签&#xff0c;我们能够清晰地定义文档的各个部分。在这篇博客中&#xff0c;我们将介绍 HTML 文档的基本结构&#xff0c;包括 <…

桥接模式解析

桥接模式 意图 将抽象与其实现相分离&#xff0c;使得两者可以独立变化。 解释 案例&#xff1a;考虑武器有不同的特效&#xff0c;你想让不同的武器可以有不同的特效&#xff0c;你应该怎么做&#xff1f; 为每个武器创建不同的副本使得有不同的特效&#xff0c;还是你只单独…

wifi模块esp8266如何连接路由器访问天气

1.直接上AT指令&#xff0c;测试过可用 注意回车换行&#xff0c;所有指令都有&#xff0c;有一些特殊的不需要&#xff0c;但是基本上都需要。下面的指令都需要回车换行。/r/n [2024-01-27 02:47:23.374] TX&#xff1a;AT[2024-01-27 02:47:23.392] RX&#xff1a;ATOK[2024-…

简单介绍----微服务和Spring Cloud

微服务和SpringCloud 1.什么是微服务&#xff1f; 微服务是将一个大型的、单一的应用程序拆分成多个小型服务&#xff0c;每个服务负责实现特定的业务功能&#xff0c;并且可以通过网络通信与其他服务通信。微服务的优点是开发更灵活&#xff08;不同的微服务可以使用不同的开…

ProRes与YUV有什么关系?ProRes还是H264/H265?

苹果的ProRes是一种视频或图片文件的编码格式。它包含ProRes 4444 XQ、ProRes 4444 、 ProRes 422 HQ、ProRes 422、ProRes 422 LT、ProRes Proxy这6种编码和ProRes RAW这种原始文件的存储格式。 我们知道&#xff0c;1920*1080的24p&#xff0c;8bit位深的RGB或者YUV444视频&…

基于InceptionV2/InceptionV3/Xception不同参数量级模型开发构建中草药图像识别分析系统,实验量化对比不同模型性能

最近正好项目中在做一些识别相关的内容&#xff0c;我也陆陆续续写了一些实验性质的博文用于对自己使用过的模型进行真实数据的评测对比分析&#xff0c;感兴趣的话可以自行移步阅读即可&#xff1a; 《移动端轻量级模型开发谁更胜一筹&#xff0c;efficientnet、mobilenetv2、…

简洁思路推理 KMP 算法——子字符串匹配

例题 28. 找出字符串中第一个匹配项的下标 暴力遍历解法 枚举原串 ss 中的每个字符作为「发起点」&#xff0c;每次从原串的「发起点」和匹配串的「首位」开始尝试匹配&#xff1a; 匹配成功&#xff1a;返回本次匹配的原串「发起点」。 匹配失败&#xff1a;枚举原串的下一个…

PC机组成之主板、芯片组与BIOS

PC机组成 台式PC由主机和外部设备组成&#xff0c;主机由主板、CPU、内存条、显卡、网卡、声卡、插卡和插座、机箱和电源等组成。 主板、芯片组和BIOS 主板 主板也称母版&#xff0c;是微型计算机中最大的一块集成电路板&#xff0c;也是其他部件和设备的连接载体。 CPU、内…