socket网络通信基础

news2025/1/19 10:43:29

目录

一、套接字编程基本流程

二、TCP流式协议及Socket编程的recv()和send()

三、读写无阻塞-完美掌握I/O复用

select()函数详解

poll()函数详解

epoll () 函数详解

一、套接字编程基本流程

原文链接:Socket编程权威指南(一)打通网络通信的任督二脉_seqpacket-CSDN博客

Socket进行编程通常包括以下几个步骤:

    1. 创建Socket
    2. 绑定 Socket(绑定地址信息)
    3. 监听连接请求(TCP服务器)
    4. 接受客户端链接
    5. 发送和接收数据
    6. 关闭Socket

1、创建socket:socket()返回新创建的套接字描述符(sockfd,一个非负整数)

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

创建初始套接字描述符,用于指定通信协议(ipv4..)、套接字类型(tcp/udp..)、以及特定协议(通常默认)

2、绑定套接字地址:bind() 返回0

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

将创建的套接字描述符与服务器的ip+port进行绑定,用于指定服务器的地址将用哪种传输协议进行传输。

sockaddr通常是sockaddr_in 结构。

struct sockaddr_in{
 sa_family_t sin_family;//类型,ipv4
 uint16_t sin_port;
 struct in_addr sin_addr;
 char sin_zero[8];
};

3、服务器监听客户端连接请求:listen()返回0

int listen(int sockfd, int backlog);

sockfd是绑定地址后的套接字描述符(必须在bind()后面);backlog指定内核应该排队的最大未完成连接的数量。这个值应该足够大,以避免在高负载情况下丢失连接请求。

  • 服务器首先创建一个套接字并绑定到一个地址。
  • 调用 listen() 使套接字变为被动监听模式。
  • 服务器随后可以使用 accept() 函数接受客户端的连接请求。

4、服务器接收客户端连接:accept()返回一个新的套接字描述符(clifd),用于与已连接的客户端通信

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

  • sockfd:已经调用过 listen() 的套接字描述符。
  • addr:(可选)指向 sockaddr 结构的指针,用于存储连接客户端的地址信息。如果不需要客户端地址,可以设置为 NULL
  • addrlen:(可选)指向 socklen_t 类型的指针,用于存储 addr 结构的大小。如果 addrNULL,这个参数也会被忽略。

用于接受一个已经建立的连接请求,通常在服务器端使用。当服务器调用 listen() 函数后,它会进入监听状态,等待客户端的连接请求。

5、进行数据传输:read()、write()

6、关闭连接:close(clifd)、close(sockfd)

#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>

int main() {
    // 创建Socket
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    
    // 绑定地址信息
    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(8000);
    bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
    
    // 监听连接
    listen(sockfd, 5);
    
    // 接收客户端连接
    struct sockaddr_in cliaddr;
    socklen_t cliaddrlen = sizeof(cliaddr);
    int clifd = accept(sockfd, (struct sockaddr*)&cliaddr, &cliaddrlen);
    
    // 读取客户端发送数据并回射
    char buffer[1024];
    ssize_t nbytes = read(clifd, buffer, sizeof(buffer));
    write(clifd, buffer, nbytes);
    
    // 关闭连接
    close(clifd);
    close(sockfd);
    
    return 0;
}

二、TCP流式协议及Socket编程的recv()和send()

原文链接:Socket编程权威指南(二)完美掌握TCP流式协议及Socket编程的recv()和send()_enotconn-CSDN博客

TCP 作为流式协议,其设计目标是提供可靠的数据传输服务。它通过多种机制确保数据的正确、有序传输,并通过拥塞控制和流量控制适应不同的网络条件。

拥塞控制是确保可靠数据传输协议有效运作的关键组成部分,因此,在TCP中,发送缓冲区和接收缓冲区成为了必不可少的元素。

在标准的Linux操作系统中,TCP的发送缓冲区和接收缓冲区默认的大小通常被设置为208KB。这意味着,如果进程A没有及时从其接收缓冲区中提取数据,那么传入的数据将继续在缓冲区内积累,直至达到其容量上限。


由于TCP面向字节流传输,因此不同TCP包到达接收缓冲区需要从一连串的字节流中区分出哪个包(粘包问题)。

采用包头+包体的策略可以解决以上问题:

  • Step 1: 首先从接收缓冲区读取固定大小的包头(例如20字节)。
  • Step 2: 解析包头,从中获取数据包的总长度,这里假设包头中包含的数据长度字段名为Header.Length
  • Step 3 : 根据Header.Length的值,确定接下来需要从接收缓冲区读取的数据量。

例如,如果包头之后的数据总长度为1048字节,减去已读取的20字节包头,还需读取1028字节的数据。


对于建立连接后的数据传输,通常使用recv()和send()。

1、recv()

//ssize_t :表示可以存储任意对象大小的有符号整数

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

  • sockfd:套接字描述符,表示要从中读取数据的 TCP 套接字。
  • buf:指向一个缓冲区的指针,用于存储接收到的数据。
  • len:缓冲区的大小,即 buf 可以存储的最大字节数。
  • flags:用来修改recv()行为的选项。常用的值包括:
    • 0:正常接收数据。
    • MSG_PEEK:窥视接收的数据,不从接收缓冲区中移除数据。
    • MSG_WAITALL:等待直到接收到 len 个字节的数据,或者出现错误。

返回值:成功时,返回接收到的字节数,该值通常小于或等于 len

(1)使用场景

  • 主要用于 TCP 套接字上的数据接收。对于 UDP 套接字,通常使用 recvfrom() 函数。

(2)阻塞和非阻塞行为

  • 默认情况下,recv() 是阻塞的,它会等待直到至少接收到一个字节的数据。
  • 对于非阻塞套接字,如果接收缓冲区中没有数据,recv() 会立即返回,返回值为 0。

(3)与 read() 的区别

  • read() 是一个通用的系统调用,用于读取文件描述符,而 recv() 专门用于套接字。
  • recv() 可以处理套接字选项和状态,而 read() 不能。

2、send()

本质上是向发送缓冲区中写入数据,内核在发送 TCP 数据时,通常会使用 Nagle 算法把多个小的数据包合并成一个发送给另一端,以提高效率。

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

  • sockfd:套接字描述符,表示要从中发送数据的套接字。
  • buf:指向要发送数据缓冲区的指针。
  • len:要发送数据的长度,单位为字节。
  • flags:用来修改发送行为的选项。常用的值包括:
    • 0:正常发送数据。
    • MSG_DONTWAIT:使 send() 调用非阻塞。
    • MSG_MORE:暗示更多的数据要发送,可以用于优化传输效率。

返回值:成功时,返回已发送的字节数,该值通常小于或等于 len

(1)使用场景

主要用于已连接的 TCP 套接字上的数据发送。对于 UDP 套接字,通常使用 sendto() 函数。

(2)阻塞和非阻塞行为

  • 默认情况下,send() 是阻塞的,它会等待直到数据被发送。

对于非阻塞套接字,如果数据不能立即发送,send() 会返回 -1 并设置 errnoEAGAINEWOULDBLOCK

(3)与 write() 的区别

  • write() 是一个通用的系统调用,用于写文件描述符,而 send() 专门用于套接字。
  • send() 可以处理套接字选项和状态,而 write() 不能。

为了实现更为复杂的非阻塞操作,recv()和send()可以结合select()和poll()。

三、读写无阻塞-完美掌握I/O复用


问题描述:

tcp流式协议以及recv()和send()这两个关键函数,用于从套接字中读取和发送数据。不过,仅依赖这两个函数存在一个明显的缺陷:如果一个套接字阻塞了,整个进程将无法处理其他套接字,效率低下。


问题解决思路:

为了解决这个问题,I/O复用模型应运而生,它使用单个线程高效地监视多个文件描述符。

I/O 复用模型的工作原理:

    • 基本概念: I/O 复用模型通过将 I/O 操作与特定的事件关联起来,使得进程或线程可以在数据准备好时才进行操作,而不是不断地轮询。
    • 使用系统调用: I/O 复用通常依赖于特定的系统调用,如 select()poll(), 和 epoll()(在 Linux 上)。这些调用允许进程监控多个 I/O 描述符的状态。
    • 监控 I/O 描述符: 进程提供一个 I/O 描述符的列表给 I/O 复用系统调用,请求监控这些描述符上特定的事件,例如可读、可写或异常状态。
    • 阻塞等待: I/O 复用调用本身可能是阻塞的,直到以下情况发生:
      • 至少有一个 I/O 描述符准备好了 I/O 操作。
      • 超时时间到达,即使没有 I/O 描述符准备好。
    • 事件通知: 当 I/O 复用系统调用返回时,它会通知进程哪些 I/O 描述符已经准备好了 I/O 操作,进程可以据此执行相应的操作。
    • 提高效率: 与为每个 I/O 流创建线程或进程相比,I/O 复用可以显著减少并发处理的开销,因为它通过单个系统调用管理多个 I/O 流。
  • select() 函数select() 是最基本的 I/O 复用机制,它允许进程监控多个描述符的 I/O 状态,但它有一些限制,如描述符数量的限制和性能问题。
  • poll() 函数poll() 提供与 select() 类似的功能,但没有描述符数量的限制,但仍然存在性能问题,尤其是在大量描述符时。
  • epoll() 函数epoll() 是 Linux 特有的 I/O 复用机制,它比 select()poll() 更高效,因为它使用事件通知机制,并且可以处理大量描述符。
  • 水平触发与边缘触发: I/O 复用可以工作在两种模式下:
    • 水平触发(Level-triggered):只要条件满足,每次调用都会返回。
    • 边缘触发(Edge-triggered):只有在状态变化时才返回,可以提高性能,但编程模型更复杂。

select()函数详解

#include <sys/select.h>

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

    • nfds:监视的文件描述符集合中最大的描述符加一(描述符集合的数量)
    • readfds:指向需要监视读状态的文件描述符集合的指针。
    • writefds:指向需要监视写状态的文件描述符集合的指针。
    • exceptfds:指向需要监视异常状态的文件描述符集合的指针。
    • timeout:指向超时时间的指针,可以是 NULL 表示无限期等待。

select() 被调用时,它会阻塞直到以下情况之一发生:

    • 至少有一个文件描述符准备好了 I/O 操作。
    • 发生了异常。
    • 超时时间到达。

select() 会更新传入的集合参数,以反映哪些文件描述符已经准备好 I/O 操作

返回值:成功时,返回准备好的文件描述符的数量。

宏功能的说明:FD_ZERO()FD_SET()FD_CLR(), 和 FD_ISSET() 是与 select() 函数一起使用的宏,它们用于操作文件描述符集合fd_set)。

(1)、FD_ZERO()
作用:将 fd_set 结构初始化为零,即清空集合中的所有文件描述符。

用法:FD_ZERO(&fdset); 其中 fdset 是 fd_set 类型的变量。


(2)、FD_SET()
作用:将指定的文件描述符添加到 fd_set 结构中。

用法:FD_SET(fd, &fdset); 其中 fd 是要添加的文件描述符,fdset 是 fd_set 类型的变量。

注意:如果文件描述符已经在集合中,再次调用 FD_SET() 不会有任何效果。


(3)、FD_CLR()
作用:从 fd_set 结构中删除指定的文件描述符。

用法:FD_CLR(fd, &fdset); 其中 fd 是要删除的文件描述符,fdset 是 fd_set 类型的变量。

注意:如果文件描述符不在集合中,调用 FD_CLR() 没有效果。


(4)、FD_ISSET()
作用:检查指定的文件描述符是否在 fd_set 结构中。

用法:if (FD_ISSET(fd, &fdset)) { ... } 其中 fd 是要检查的文件描述符,fdset 是 fd_set 类型的变量。

返回值:如果文件描述符在集合中,返回非零值(通常是 1);如果不在集合中,返回 0。

select()配合宏功能的使用步骤:

  • 使用 FD_ZERO() 初始化 fd_set 结构。
  • 使用 FD_SET() 将需要监视的文件描述符添加到集合中。
  • 调用 select() 函数,传入 fd_set 结构。
  • 调用 select() 后,使用 FD_ISSET() 检查哪些文件描述符已经准备好 I/O 操作。
  • 使用 FD_CLR() 从集合中删除已经处理过的文件描述符,以便在下一次 select() 调用中不再监视它们。

注意事项:

  • select() 有文件描述符数量的限制(通常是 1024),对于大量并发连接,可能需要使用 poll()epoll() 等更高级的 I/O 复用技术。
  • 在调用 select() 之前,需要使用 FD_ZERO()FD_SET()FD_CLR()FD_ISSET() 等宏来初始化和操作文件描述符集合。
// 在这个例子中,我们首先将监听套接字加入masterfds集合。
//然后在每次循环中,将masterfds复制到readfds,并调用select()进行监视。
//如果监听套接字就绪,则接受新的连接并将数据套接字加入masterfds。
//如果数据套接字就绪,则可以对其进行读写操作。
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>

int main() {
    //创建服务器端socket套接字
    int listensock = socket(AF_INET, SOCK_STREAM, 0);
    
    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(8000);
    //绑定套接字与服务器地址端口号
    bind(listensock, (struct sockaddr*)&servaddr, sizeof(servaddr));
    //监听该套接字
    listen(listensock, 5);
    //定义文件描述符集合
    fd_set readfds, masterfds;
    //初始化
    FD_ZERO(&masterfds);
    //将服务器读状态套接字放入master文件描述符集合(其实就是表示是否本套接字有连接情况)
    FD_SET(listensock, &masterfds);
    
    while (true) {
        //循环将监听套接字集合更新到read集合中
        readfds = masterfds;
        //select阻塞监视监听套接字集合,会更新传入的集合,只留下已经准备好IO操作的文件描述符
        int nfds = select(listensock + 1, &readfds, NULL, NULL, NULL);
        //如果监听套接字就绪,与客户端套接字建立连接
        if (FD_ISSET(listensock, &readfds)) {
            struct sockaddr_in cliaddr;
            socklen_t cliaddrlen = sizeof(cliaddr);
            //建立连接
            int datafds = accept(listensock, (struct sockaddr*)&cliaddr, &cliaddrlen);
            //把客户端套接字(数据套接字)加入到master,用于后续更新
            FD_SET(datafds, &masterfds);
        }
        //实现一个监听套接字监听多个数据套接字的IO操作
        for (int datafds = 0; datafds < nfds; datafds++) {
            //如果数据套接字就绪进行数据传输操作
            if (FD_ISSET(datafds, &readfds)) {
                // Handle data from datafds
            }
        }
    }
    
    close(listensock);
    return 0;
}

select()函数的限制:文件描述符数量限制。

具体原因:

select使用的文件描述符集合的数据结构为fd_set:

typedef struct {
    unsigned long fds_bits[FD_SETSIZE / (8 * sizeof(unsigned long))];
} fd_set;

这是一个固定大小的数组,FD_SETSIZE 是一个常量,定义了 fd_set 可以表示的最大文件描述符数量。通常这个值是 1024。


poll()函数详解

select()使用描述符集来监视描述符,主要存在两方面缺陷:

(1)采用fd_set结构,导致存在最大文件描述符数量的限制,无法适应大量并发IO操作

(2)存在复制描述符集的开销,主要原因是每次调用select()时,它会修改传入的集合fd_set,只保留已经准备好进行 I/O 操作的描述符,因此需要在进入循环开始先重置描述符集合,存在复制开销。

    • fd_set 的修改机制
      • 设置准备好的文件描述符:

在调用 select() 之前,你会用 FD_SET 宏将感兴趣的文件描述符添加到 fd_set 中。

select() 返回时,它会修改传入的 fd_set只保留那些已经准备好进行 I/O 操作的文件描述符。

      • 清除未准备好的文件描述符:

select()清除(unset)那些没有准备好的文件描述符。这意味着,如果一个文件描述符在调用 select() 时没有准备好,它将不再存在于返回的 fd_set 中。


poll() 是一种 I/O 多路复用系统调用,它提供了一种机制来监视多个文件描述符(file descriptors)的状态,类似于 select() 函数。poll()函数克服了select()的部分缺陷,它采用pollfd结构数组(std::vector<struct pollfd> fds;)来监视,而不是描述符集,避免了每次调用时复制描述符集的开销。

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

    • fds:指向 struct pollfd 数组的指针,数组中的每个元素都包含了要监视的文件描述符和相关的事件类型。
    • nfds:数组 fds 中元素的数量。
    • timeout:等待时间,单位为毫秒。如果设置为 -1,表示无限期等待;设置为 0 表示非阻塞调用,立即返回。

struct pollfd 结构:

struct pollfd {
    int   fd;         /* 文件描述符 */
    short events;     /* 需要监视的事件类型 */
    short revents;    /* 事件发生后的状态 */
};
    • fd:需要监视的文件描述符。
    • events:需要监视的事件类型,可以是以下宏的组合:
      • POLLIN:有数据可读。
      • POLLOUT:写入不会阻塞。
      • POLLPRI:有紧急数据可读。
      • POLLERR:发生错误。
      • POLLHUP:对端关闭连接。
      • POLLNVAL:文件描述符不是有效的监视对象。
    • revents:实际发生的事件,函数返回后由系统填充。

返回值:成功时,返回准备好的文件描述符的数量。

工作原理:

    • 初始化 pollfd 数组:为每个需要监视的文件描述符设置一个 pollfd 结构,并指定需要监视的事件类型。
    • 调用 poll():传入 pollfd 数组、数组的大小和超时时间。
    • 等待事件poll() 函数会阻塞,直到以下情况之一发生:
      • 至少有一个文件描述符准备好了 I/O 操作。
      • 超时时间到达。
    • 处理结果poll() 函数返回后,检查 pollfd 数组中的 revents 字段,以确定哪些事件发生了。

 

epoll () 函数详解

select()和poll()虽然调用的是IO复用的机制,但是前者存在最大描述符数量限制以及描述符集合复制开销;后者存在每次调用poll时加入全部描述符,因此这两种方法都无法应对大量的并发IO操作。

epoll 在处理大量并发连接时具有明显的优势,因为它使用基于事件的模型,可以减少 CPU 和内存的使用。


1、核心概念

  • epoll 实例:使用 epoll_create() 创建,代表一个监视的集合理解为一个监视多个文件描述符事件的对象
  • 事件:可以是读、写、错误等。
  • 文件描述符:需要被监视的 I/O 对象。
  • 回调机制:当文件描述符上的事件发生时,epoll 会通知应用程序。

2、函数原型

  • 创建 epoll 实例
#include <sys/epoll.h>
//自从Linux2.6.8版本以后,size值其实是没什么用的,不过要大于0,因为内核可以动态的分配大小,
//所以不需要size这个提示了。
int epoll_create(int size);

size:建议的初始大小,实际上创建的实例大小由内核决定。

  • 添加/修改文件描述符
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
    • epfd:epoll 实例的文件描述符,监视多个文件描述符的对象,由epoll_create创建。
    • op:操作类型,可以是 EPOLL_CTL_ADD(添加)、EPOLL_CTL_MOD(修改)或 EPOLL_CTL_DEL(删除)。
    • fd:需要监视的文件描述符。
    • event:指向 epoll_event 结构的指针,指定了要监视的事件和相关的回调数据。
  • 等待事件
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
    • epfd:epoll 实例的文件描述符。
    • events:用于存储发生的事件的数组
    • maxevents:数组 events 的最大容量。
    • timeout:等待时间,单位为毫秒。如果设置为 -1,表示无限期等待。

epoll_event 结构:

struct epoll_event {
    uint32_t events;     /* Epoll events */
    epoll_data_t data;   /* User data variable */
};
  • events:事件掩码,可以是以下宏的组合:
    • EPOLLIN:有数据可读。
    • EPOLLOUT:写入不会阻塞。
    • EPOLLPRI:有紧急数据可读。
    • EPOLLERR:发生错误。
    • EPOLLHUP:对端关闭连接。
  • data:用户自定义的数据,可以是任何类型的指针,用于在事件发生时传递额外信息。

3、工作原理

  • 第一步,创建 epoll 实例:使用 epoll_create() 创建一个 epoll 实例。
  • 第二步,添加文件描述符:使用 epoll_ctl() 将需要监视的文件描述符添加到 epoll 实例中,并设置要监视的事件。
  • 第三步,等待事件:调用 epoll_wait() 等待事件发生。与 select()poll() 不同,epoll_wait() 只返回已经发生的事件,减少了不必要的轮询。
  • 第四步,处理事件:遍历 epoll_wait() 返回的事件数组,处理每个发生的事件。
#include <sys/epoll.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    
    int epfd = epoll_create(1); // 创建 epoll 实例
    //event定义监视的事件类型;events数组保存实际发生的事件
    struct epoll_event event, events[10];

    if (epfd == -1) {
        perror("epoll_create");
        return 1;
    }

    // 初始化事件
    event.data.fd = STDIN_FILENO; // 监视标准输入
    event.events = EPOLLIN;

    // 添加文件描述符到 epoll 实例
    if (epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &event) == -1) {
        perror("epoll_ctl");
        return 1;
    }

    // 等待事件,把发生的事件放入events数组中,并返回已经发生的事件的数量
    int nfds = epoll_wait(epfd, events, 10, -1);
    if (nfds == -1) {
        perror("epoll_wait");
        return 1;
    }

    // 处理事件
    for (int i = 0; i < nfds; i++) {
        if (events[i].events & EPOLLIN) {
            printf("Data is available to read on fd %d\n", events[i].data.fd);
        }
    }

    close(epfd);
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

接口防篡改+防重放攻击

接口防止重放攻击&#xff1a;重放攻击是指攻击者截获了一次有效请求(如交易请求),并在之后的时间里多次发送相同的请求&#xff0c;从而达到欺骗系统的目的。为了防止重放攻击&#xff0c;通常需要在系统中引入一种机制&#xff0c;使得每个请求都有一个唯一的标识符(如时间戳…

庄小焱——2024年博文总结与展望

摘要 大家好&#xff0c;我是庄小焱。岁末回首&#xff0c;2024 年是我在个人成长、博客创作以及生活平衡方面收获颇丰的一年。这一年的经历如同璀璨星辰&#xff0c;照亮了我前行的道路&#xff0c;也为未来的发展奠定了坚实基础。 1. 个人成长与突破 在 2024 年&#xff0c…

在线base64转码工具

在线base64转码工具&#xff0c;无需登录&#xff0c;无需费用&#xff0c;用完就走。 官网地址&#xff1a; https://base64.openai2025.com 效果&#xff1a;

鸿蒙学习构建视图的基本语法(二)

一、层叠布局 // 图片 本地图片和在线图片 Image(https://developer.huawei.com/allianceCmsResource/resource/HUAWEI_Developer_VUE/images/080662.png) Entry Component//自适应伸缩 设置layoutWeight属性的子元素与兄弟元素 会按照权重进行分配主轴的空间// Position s…

OA-CNN:用于 3D 语义分割的全自适应稀疏 CNN

大家读完觉得有帮助记得及时关注和点赞&#xff01;&#xff01;&#xff01; 1介绍 2相关工作 基于点的学习。 基于 CNN 的学习。 动态卷积。 3全能自适应 3D 稀疏 CNN 3.1空间适应性感受野 赋予动机。 体素网格。 金字塔网格分区。 Adaptive 聚合器。 3.2自适应关…

利用 LNMP 实现 WordPress 站点搭建

部署MySQL数据库 在主机192.168.138.139主机部署数据库服务 包安装数据库 apt-get install mysql-server 创建wordpress数据库和用户并授权 mysql> create database wordpress;#MySQL8.0要求指定插件 mysql> create user wordpress192.168.138.% identified with mys…

Vue2.0的安装

1.首先查看是否已经安装了node.js 选择以管理员方式打开命令提示符&#xff08;权限较高&#xff09;&#xff0c;或者通过cmd的方式打开 打开后输入node -v 查看自己电脑是否安装node&#xff0c;以及版本号 node -v 如果没有的话&#xff0c;请查看Node.js的安装 2.Vue和脚…

OpenEuler学习笔记(一):常见命令

OpenEuler是一个开源操作系统&#xff0c;有许多命令可以用于系统管理、软件安装、文件操作等诸多方面。以下是一些常见的命令&#xff1a; 一、系统信息查看命令 uname 用途&#xff1a;用于打印当前系统相关信息&#xff0c;如内核名称、主机名、内核版本等。示例&#xff…

无纸化同屏解决方案探究和技术展望

好多开发者&#xff0c;在了解到我们在无纸化同屏、智慧教育场景的碾压式行业积累后&#xff0c;希望我们做些无纸化同屏相关的技术探讨&#xff0c;实际上这块方案并不复杂&#xff0c;很容易做到实际使用场景契合的方案&#xff0c;主要是如何达到客户期望的功能和体验。 无…

nss刷题3

[SWPUCTF 2022 新生赛]webdog1__start level1&#xff1a; 打开环境后什么也&#xff0c;没有&#xff0c;查看源码&#xff0c;看到第一关是MD5值&#xff0c;要get传参web&#xff0c;然后web的值的MD5和它原来值相等&#xff0c;0e开头的字符在php中都是0&#xff0c;传入…

深入了解计算机网络中的路由协议与性能优化

在计算机网络中&#xff0c;路由协议是决定数据如何从源节点到达目标节点的关键组成部分。不同的路由协议各有特点&#xff0c;如何根据实际需求选择合适的协议&#xff0c;并对网络性能进行优化&#xff0c;是每个网络管理员需要面临的重要课题。 本篇文章将深入探讨计算机网…

通过视觉语言模型蒸馏进行 3D 形状零件分割

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01;对应英文要求比较高&#xff0c;特此说明&#xff01; Abstract This paper proposes a cross-modal distillation framework, PartDistill, which transfers 2D knowledge from vision-language models …

Apple Vision Pro 距离视网膜显示还有多远

本文介绍了视网膜屏幕的概念和人眼视敏度极限,以及头戴显示设备在视场角和角分辨率之间的权衡设计。文章还提到了苹果公司的新产品Apple Vision Pro的设计规范和视觉效果。 Retina display 是苹果公司针对其高分辨率屏幕技术的一种营销术语。这个术语最早由乔布斯在 2010 年 6…

微服务学习-快速搭建

1. 速通版 1.1. git clone 拉取项目代码&#xff0c;导入 idea 中 git clone icoolkj-microservices-code: 致力于搭建微服务架构平台 1.2. git checkout v1.0.1版本 链接地址&#xff1a;icoolkj-microservices-code 标签 - Gitee.com 2. 项目服务结构 3. 实现重点步骤 …

美最高法维持TikTok禁令,不卖就禁或有转机,TikTok直播专线助力企业在挑战中前行

一、TikTok 面临的危机与转机 最近&#xff0c;TikTok 在美国的命运可谓是波谲云诡。当地时间 1 月 17 日&#xff0c;美国联邦最高法院裁定 TikTok “不卖就禁” 的法律不违宪&#xff0c;这就意味着该法案将于 1 月 19 日生效 &#xff0c;TikTok 似乎已被逼至悬崖边缘。然而…

编写Wireshark的Lua脚本详解及示例解析

编写Wireshark的Lua脚本详解及示例解析 编写Wireshark Lua脚本的基本步骤SMGP.lua脚本解析脚本解析要点总结Wireshark是一个强大的网络协议分析工具,支持通过Lua脚本扩展其功能,以解析自定义或复杂的协议。下面将详细介绍如何编写Wireshark的Lua脚本,并通过解析一个具体的SM…

【20】Word:小许-质量管理-论文❗

目录 题目​ NO1.2.3.4.5 NO6.7 NO8 NO9 NO10.11 题目 NO1.2.3.4.5 另存为“Word.docx”文件在考生文件夹下&#xff0c;F12Fn是另存为的作用布局→页面设置对话框→纸张&#xff1a;大小A4→页边距&#xff1a;上下左右不连续ctrl选择除表格外的所有内容→开始→字体对…

【软件开发过程管理规范】需求管理,需求分析,设计开发管理,测试管理(Word)

一、需求管理规程 1 简介 2 过程总体描述 2.1 过程概述 2.2 过程流程图 3 过程元素描述 3.1 准备阶段 3.2 需求调研 3.3 需求分析 软件开发人员及用户往往容易忽略信息沟通&#xff0c;这导致软件开发出来后不能很好地满足用户的需要&#xff0c;从而造成返工。而返工不仅在技术…

RabbitMQ-消息可靠性以及延迟消息

目录 消息丢失 一、发送者的可靠性 1.1 生产者重试机制 1.2 生产者确认机制 1.3 实现生产者确认 &#xff08;1&#xff09;开启生产者确认 &#xff08;2&#xff09;定义ReturnCallback &#xff08;3&#xff09;定义ConfirmCallback 二、MQ的持久化 2.1 数据持久…

了解 .mgJSON 文件

.mgJSON &#xff08;Motion Graphics JSON&#xff09;是一个基于标准 JSON 格式的文件扩展名&#xff0c;专门用于存储和交换与动态图形、动画和多媒体应用相关的数据。该格式支持静态和动态数据流&#xff0c;能够精确描述动画、物体变换、图形效果等。 .mgJSON 文件通过层级…