网络编程(Day35)

一、学习内容

  1. ip地址的网络字节序转换

    1. 函数原型
      in_addr_t inet_addr(const char *cp);
      1. 返回值
         
        in_addr_t:一个 uint32 数据,该数据是结构体struct in_addr {in_addr_t s_addr;};struct in_addr 是结构体 struct sockaddr_in 中的一个数据
      2. 参数描述
        参数 cp:ip地址
      3. 调用形式

        struct sockaddr_in addr
        addr.sin_addr.s_addr = inet_addr("192.168.1.1")
      4. 功能描述
        将本地字节序的ip地址转换成网络字节序
  2. 套接字

    • 概念

      专门用来进行网络通信的一种文件:该文件中保存了数据接收端的ip地址和port端口号
    • 创建一个套接字

      • 函数原型
        int socket(int domain, int type, int protocol);
        • 参数描述
          • 参数 domain:网络介质

            AF_UNIX, AF_LOCAL:本地通信用的套接字 AF_INET :ipv4协议
          • 参数 type:套接字类型

            SOCK_STREAM:提供一个基于连接的,稳定,双向的,字节流的套接字,TCP协议就是这种套接字
            SOCK_DGRAM:提供一个非连接,不可靠的,要求数据有最大值限制的 数据报套接字,UDP协议就是这种套接字
          • 参数 protocol:协议

            写 0:表示根据套接字类型自动选择协议
        • 调用形式
          int sock = socket(AF_INET,SOCK_STREAM,0) 字节流套接字
          int sock = socket(AF_INET,SOCK_DGRAM,0) 数据报套接字
        • 功能描述
          创建一个套接字文件
  3. 服务器模型

    • 创建一个字节流的套接字

    • 准备一个 互联网地址结构体,用来存放ip和port
       

      互联网地址结构体,tcp使用的结构体类型如下
      struct sockaddr_in {__kernel_sa_family_t  sin_family;   必须写 AF_INET__be16        sin_port;    网络字节序的端口号struct in_addr    sin_addr;  网络字节序的ip,注意是该变量里面一个叫做 s_addr的变量存放网络字节序的ip     unsigned char     __pad[__SOCK_SIZE__ - sizeof(short int) -sizeof(unsigned short int) - sizeof(struct in_addr)];
      };最后一个变量:唯一作用就是让 struct sockaddr_in 
      这个结构体的大小去和 struct sockaddr 这个结构体大小对齐
      为什么要对齐:因为在 下一步绑定里面,需要用到的是 sockaddr 类型
      所以问题又变成了,下一步绑定的时候,为什么要用sockaddr,而不是 sockaddr_in呢:
      因为刚才说过,根据套接字的类型不同,
      使用的地址信息结构体可能是不同的(tcp udp使用的是 sockaddr_in,域套接字使用的是 sockaddr_un),
      所以需要一个通用的地址信息结构体,去接受不同类型的地址信息结构体里面的数据,
      为了能够顺利转换数据,这些不同类型的地址信息结构体,大小必须和通用地址信息结构体保持一致
    • 将ip和port绑定到套接字里面

      • 函数原型
        int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
        • 参数描述
          • 参数 sockfd:等待绑定ip和port的套接字

          • 参数 addr:包含有ip和port的地址信息结构体的地址,详见上一步中的解释

          • 参数 addrlen:参数addr的实际长度

        • 调用形式
          由于存在端口占用的问题,bind函数非常容易出错,
          所以需要判断他的返回值,bind失败返回-1struct sockaddr_in addr;if(bind(套接字,(struct sockaddr*)&addr,sizeof(addr)) == -1){perror("bind")    }
        • 功能描述
          为套接字sockfd,绑定ip地址和端口号port
    • 服务器创建一个监听列表

      • 函数原型
        int listen(int sockfd, int backlog);
        • 解释
          如果监听列表长度为10,然后此时有11个客户端尝试连接,但是这11个客户端的前10个,都没有被服务器接受连接,此时监听列表就会满,第11个客户端就会排队。直到服务器接受一个客户端的连接,让监听列表空出一个位置为止
        • 参数描述
          • 参数 sockfd:谁去监听连接,一般就是服务器

          • 参数 backlog:监听列表的长度

        • 调用形式
          • listen(server,10)

        • 功能描述
          创建监听列表,并监听是否有客户端连接
    • 服务器接受客户端的连接

      • 函数原型
        int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
        • 参数描述
          • 参数 sockfd:服务器描述符

          • 参数 addr:传入一个 struct sockaddr_in 结构体地址,用来存放连接上来的客户端的ip和port

                   如果传0,表示不需要知道客户端的 ip 和 port
          • 参数 addrlen:注意是一个指针,该指针指向参数2所指向的那个结构体的实际长度,我们需要提前准备一下

        • 调用形式
          • 最简单的调用形式

            int client = accept(server,NULL,NULL)
          • 复杂一点的形式

            struct sockaddr_in client_addr = {0};socklen_t client_len = sizeof(client_addr);int client = accept(server,(struct sockaddr*)&client_addr,&client_len)
        • 功能描述

          该函数是一个阻塞型IO,如果没有客户端来连接的话,那么accept将会一直阻塞,直到有客户端连接为止,accept函数将会接受客户端的连接,然后获取客户端的ip和port,并且返回客户端的描述符

          • 为什么要返回客户端描述符
            因为服务器与客户端产生连接之后,服务器与客户端之间的通信,全都依赖客户端套接字
          • 为什么不依赖服务器套接字通信呢
            因为一个服务器允许连接多个客户端,如果使用服务器套接字通信的话,根本不知道会向哪一个客户端进行通信
          • 服务器套接字功能
            仅仅是展示自己的ip和port,让客户端来连接
    • 模型总结

  4. 客户端模型

    • 创建套接字:socket

    • 准备互联网地址信息结构体,并填充信息

    • 连接服务器

      • 函数原型
        int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
        • 调用形式
          直接照抄bind函数的调用形式,再把函数名改成connect就行了
        • 功能描述
          根据地址信息结构体中的ip和port,连接服务器 由于服务器可能没开,所以connect函数的错误率也很高,所以记得判断他的返回值,返回-1表示链接失败
    • 模型总结

  5. 客户端与服务器收发消息

    • 发送消息

      由于套接字本身也是一个描述,使用write发送消息没有任何问题。还有一个专门针对套接字进行消息发送的函数
      • 函数原型
        ssize_t send(int sockfd, const void *buf, size_t len, int flags);
        • 参数描述
          • 参数 sockfd:套接字

          • 参数 buf:存放有准备发送的数据的地址

          • 参数 len:数据的实际长度

          • 参数 flags:写0的时候,send和write一模一样,表示阻塞型IO,还有一个选项 MSG_DONTWAIT:表示非阻塞型IO,当缓存区写满之后,新写入的数据丢失

        • 调用形式
          send(套接字,准备发送的数据的地址,数据的长度,0)
        • 功能描述
          write是unix系统中的一个库函数
          send是 posix标准中的一个标准库函数
          send其实前3个参数和write的用法一模一样,区别仅在于最后一个flags
    • 读取消息
      同理,使用read读取消息也没有任何问题 同理,存在一个专门用于套接字的读取函数
      • 函数原型
        ssize_t recv(int sockfd, void *buf, size_t len, int flags);
        • 注意
          recv和read对比,唯一的优势就是 recv如果想要切换成非阻塞IO很简单,read如果想要切换成非阻塞IO稍微复杂一点
        • 参数描述
          • 参数 sockfd:套接字

          • 参数 buf:存放有准备发送的数据的地址

          • 参数 len:数据的实际长度

          • 参数 flags:写0的时候,recv和read一模一样,表示阻塞型IO,还有一个选项 MSG_DONTWAIT:表示非阻塞型IO,当缓存区写满之后,新写入的数据丢失

        • 调用形式
          recv(套接字,准备发送的数据的地址,数据的长度,0)
        • 功能描述
          接受套接字中的数据
    • read函数如何切换成非阻塞型IO

      • 函数原型
        int fcntl(int fd, int cmd, ... /* arg */ );
        • 参数描述
          • 参数 fd:等待操作的描述符

          • 参数 cmd:决定了 fcntl函数到底是设置flags 还是 获取 flags

                F_GETFL:获取flags的值
                F_SETFL:设置flags的值
          • 参数 ...:根据 cmd的值,决定是否需要传入第3个参数

                如果cmd表示获取flags的值,则不需要传入第3个参数
                如果cmd表示设置flags的值,则需要传入第3个参数,第3个参数传入想要设置的flags的具体的值
        • 返回值
          获取flags,成功获取返回flags,失败获取返回-1
          设置flags,成功返回0,失败返回-1
        • 功能描述
          用来设置一个描述符的flags(open函数的第2个参数,O_RDONLY这种东西) 或者用来获取一个描述符的flags
    • read和recv的特点

      当read 和 recv 是一个阻塞型IO的时候,此时如果客户端断开,则服务器那边的客户端套接字的read就会变成一个非阻塞型IO
      当read变成非阻塞型IO之后,由于没有读取到数据,会返回0
      反过来说,read函数如果从阻塞型IO变成非阻塞型IO(说明客户端断开连接),我们只需要判断返回值是否为0即可
      但是,当read从一开始就是一个非阻塞型IO呢? 没有读取到数据,返回-1,如果客户端断开连接,返回0
  6. 脑图

二、作业

作业

使用搭建好的服务器和客户端,实现一个完整的注册,登录功能
服务器使用链表 + 文件IO的形式去记录账号和密码

代码解答:

服务器:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>// 定义简化的套接字地址类型
typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;      
typedef struct sockaddr_un addr_un_t;// 定义用户数据结构
struct Pack {char name[16];  // 用户名char pswd[16];  // 密码
};// 定义链表节点结构
typedef struct Iceberg {union {struct Pack userdata; // 用于存储用户数据int len;              // 用于存储链表长度};struct Iceberg *next;     // 下一个节点指针
} liink, *Plink;// 创建链表头节点
Plink create() {Plink p = malloc(sizeof(liink));if (p == NULL) {printf("申请头节点失败\n");return NULL;}p->len = 0;p->next = NULL;return p;
}// 从user.txt文件读取用户信息并加载到链表中
void read_usertxt(Plink L) {if (L == NULL) {printf("载入失败\n");return;}int fd = -1;if ((fd = open("./user.txt", O_RDWR)) == -1) {perror("open error");return;}struct Pack pack;Plink t = L;for (int i = 0; i < L->len; i++) {t = t->next;}while (1) {int res = read(fd, &pack, sizeof(pack));if (res == 0) {printf("原有用户已载入完成\n");break;}Plink p = malloc(sizeof(liink));memcpy(p->userdata.name, pack.name, sizeof(pack.name));memcpy(p->userdata.pswd, pack.pswd, sizeof(pack.pswd));p->next = NULL;t->next = p;L->len++;}close(fd);return;
}// 检查用户名是否存在并进行注册
int Plagiarism_detection_reg(struct Pack pack1, Plink L) {int flag = 0;Plink t = L;for (int i = 0; i < L->len; i++) {t = t->next;if (strcmp(t->userdata.name, pack1.name) == 0) {flag = 1;break;}}if (flag == 1) {printf("用户名已存在\n");return 1;} else if (flag == 0) {Plink p = malloc(sizeof(liink));memcpy(p->userdata.name, pack1.name, sizeof(pack1.name));memcpy(p->userdata.pswd, pack1.pswd, sizeof(pack1.pswd));p->next = NULL;t->next = p;L->len++;printf("用户注册成功\n");	return 0;}
}// 用户登录验证
int Plagiarism_detection_login(struct Pack pack2, Plink L) {int flag = 0;                    Plink t = L;for (int i = 0; i < L->len; i++) {t = t->next;if ((strcmp(t->userdata.name, pack2.name) == 0) && (strcmp(t->userdata.pswd, pack2.pswd) == 0)) {flag = 1;break;}}if (flag == 1) {printf("登录成功\n");return 1;} else if (flag == 0) {printf("用户名或密码错误\n");return 0;}
}// 将链表中的用户信息保存到user.txt文件
int save_usertext(Plink L) {int fd = -1;if ((fd = open("./user.txt", O_WRONLY | O_TRUNC)) == -1) {perror("open error");return -1;}Plink t = L->next;while (t != NULL) {if (write(fd, t->userdata.name, sizeof(t->userdata.name)) == -1) {perror("write name error");break;}if (write(fd, t->userdata.pswd, sizeof(t->userdata.pswd)) == -1) {perror("write pswd error");break;}t = t->next;}close(fd);return 0;
}// 销毁链表
int link_destory(Plink L) {if (L == NULL) {printf("销毁失败\n");return -1;}Plink t = L;while (t != NULL) {t = t->next;free(L);L = t;}printf("链表销毁完成\n");
}// 主函数
int main(int argc, const char *argv[]) {if (argc != 2) {printf("请输入端口号\n");return -1;}int port = atoi(argv[1]); // 端口号转换为整数int sever = socket(AF_INET, SOCK_STREAM, 0); // 创建套接字addr_in_t addr = {0}; // 初始化地址结构体addr.sin_family = AF_INET; // 设置地址家族addr.sin_port = htons(port); // 设置端口号addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 设置IP地址if (bind(sever, (addr_t*)&addr, sizeof(addr)) == -1) { // 绑定地址perror("bind error");return -1;}listen(sever, 10); // 监听连接addr_in_t client_addr = {0}; // 初始化客户端地址socklen_t client_len = sizeof(client_addr);int client = accept(sever, (addr_t*)&client_addr, &client_len); // 接受客户端连接printf("客户端连接成功\n");Plink L = create(); // 创建链表头节点read_usertxt(L); // 读取用户信息while (1) {int flag = -1;read(client, &flag, sizeof(flag)); // 读取客户端标志if (flag == 2) { // 注册struct Pack pack1;read(client, &pack1, sizeof(pack1)); // 读取用户信息Plagiarism_detection_reg(pack1, L); // 注册用户save_usertext(L); // 保存用户信息} else if (flag == 1) { // 登录struct Pack pack2;read(client, &pack2, sizeof(pack2)); // 读取登录信息Plagiarism_detection_login(pack2, L); // 验证登录} else { // 退出save_usertext(L); // 保存用户信息link_destory(L); // 销毁链表printf("客户端断开连接\n");close(client); // 关闭客户端连接break;}}return 0;
}
客户端:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>// 定义用户信息结构体
struct Pack {char name[16];  // 用户名char pswd[16];  // 密码
};// 主函数
int main(int argc, const char *argv[]) {// 检查命令行参数,要求提供端口号if (argc != 2) { printf("请输入端口号\n");return -1;}int port = atoi(argv[1]);  // 将端口号字符串转换为整数int client = socket(AF_INET, SOCK_STREAM, 0);  // 创建套接字用于TCP通信// 初始化服务器地址结构体struct sockaddr_in addr = {0};  addr.sin_family = AF_INET;             // 设置地址族为IPv4addr.sin_port = htons(port);           // 设置端口号,转换为网络字节序addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 设置服务器IP地址// 连接服务器,若失败则退出if (connect(client, (struct sockaddr*)&addr, sizeof(addr)) == -1) { perror("connect error");return -1;}// 主循环,提供用户交互菜单while (1) {struct Pack pack; // 定义用于存储用户名和密码的结构体变量// 输出操作菜单printf("\t\t\t1、登录\t\t\t\n"); printf("\t\t\t2、注册\t\t\t\n");printf("\t\t\t0、退出\t\t\t\n");int flag = -1;      // 用户选项标识符scanf("%d", &flag); // 获取用户选择if (flag == 0) {    // 如果用户选择退出write(client, &flag, sizeof(flag)); // 发送退出标志到服务器break;           // 结束循环,退出客户端}write(client, &flag, sizeof(flag)); // 将用户选择的标志发送到服务器// 根据用户选择执行相应操作if (flag == 1) {       // 用户选择登录printf("请输入用户名\n");scanf("%s", pack.name);     // 输入用户名printf("请输入密码\n");scanf("%s", pack.pswd);     // 输入密码write(client, &pack, sizeof(pack)); // 将用户名和密码发送到服务器进行验证} else if (flag == 2) {  // 用户选择注册printf("请输入用户名\n");scanf("%s", pack.name);     // 输入用户名printf("请输入密码\n");scanf("%s", pack.pswd);     // 输入密码write(client, &pack, sizeof(pack)); // 将注册信息发送到服务器进行注册} else {    // 输入其他无效选项printf("无效选择,请重新输入。\n");}}close(client); // 关闭客户端套接字return 0;      // 正常退出
}

成果展现: 

 

三、总结

学习内容概述

        今天的学习内容主要涉及网络编程的基础知识,包括 IP 地址的网络字节序转换、套接字(socket)的概念、套接字的创建与绑定、服务器和客户端的通信流程、数据的发送与接收等。重点掌握了套接字的使用流程,学习了各类 socket 相关函数(如 `socket`、`bind`、`listen`、`accept`、`connect`、`send`、`recv` 等)及其作用。

学习难点

1. 网络字节序转换:

理解 IP 地址的网络字节序和本地字节序之间的转换,对于跨网络传输的数据表示尤为重要。

2. 套接字的多样化使用:

不同的套接字类型(如 SOCK_STREAM 和 SOCK_DGRAM)适用于不同的传输协议(TCP 和 UDP),需要理解不同协议的应用场景。

3. 网络编程模型:

在服务器和客户端模型中,对各函数的调用顺序和作用(如 `bind`、`listen`、`accept` 等)需要深刻理解,否则容易导致程序运行异常。

4. 非阻塞 I/O:

理解阻塞和非阻塞 I/O 的区别,以及如何利用 `fcntl` 函数切换 I/O 模式,尤其是在网络编程中正确处理非阻塞 I/O 的返回值,是一个较难掌握的点。

主要事项

1. IP 地址转换:

`inet_addr` 函数可以将本地字节序的 IP 地址转换为网络字节序,以便在套接字通信中传输。常用于将人类可读的 IP 地址转换为适用于网络传输的数据格式。

2. 套接字创建与绑定:

通过 `socket` 函数创建套接字文件,使用 `bind` 函数将 IP 和端口绑定到套接字上。特别注意的是,`bind` 可能会因为端口占用而失败,需要进行错误处理。

3. 服务器模型:

服务器通过 `listen` 函数创建监听列表,并使用 `accept` 函数接收客户端连接。`accept` 返回的客户端描述符用于后续的通信,这样可以区分不同客户端。

4. 数据传输:

`send` 和 `recv` 函数用于套接字的数据发送和接收,和 `write`、`read` 类似,但有更强的网络适应性。对于需要实现非阻塞 I/O 的场景,可以使用 `fcntl` 设置描述符的 flags。

5. I/O 模式切换:

`fcntl` 函数的 `F_SETFL` 选项可以用于切换描述符的阻塞模式,在网络断开时可判断 `read` 或 `recv` 返回值,识别客户端是否断开连接。

未来学习的重点

1. 深入理解套接字参数和结构体:

如 `struct sockaddr` 和 `struct sockaddr_in` 结构体的区别和联系,了解其各字段的具体含义,以及如何在实际编程中进行正确填充和使用。

2. 网络编程中的错误处理:

套接字编程中常出现的错误,如端口占用、连接失败等,后续学习中可以尝试更多的错误处理方法,提升程序的健壮性。

3. 非阻塞 I/O 和多线程/多进程结合:

深入学习非阻塞 I/O 的使用技巧,特别是在高并发场景下如何配合多线程或多进程处理大量客户端请求,提升程序性能。

4. 协议栈的学习:

进一步理解 TCP/IP 协议栈的各层原理,掌握 TCP 和 UDP 的差异及其具体应用场景,以更好地选择适当的套接字类型和传输协议。

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

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

相关文章

Rust 力扣 - 1652. 拆炸弹

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 我们只需要遍历长度长度为k的窗口&#xff0c;然后把窗口内数字之和填充到结果数组中的对应位置即可 题解代码 impl Solution {pub fn decrypt(code: Vec<i32>, k: i32) -> Vec<i32> {let n c…

【开源免费】基于SpringBoot+Vue.J服装商城系统(JAVA毕业设计)

本文项目编号 T 046 &#xff0c;文末自助获取源码 \color{red}{T046&#xff0c;文末自助获取源码} T046&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 新…

AI虚拟主播实时互动模块的搭建与开发!

AI虚拟主播&#xff0c;作为新兴的数字媒体形式&#xff0c;正在逐步改变我们的娱乐和信息获取方式&#xff0c;它们不仅拥有栩栩如生的外貌&#xff0c;还能通过实时互动模块与用户进行流畅的对话&#xff0c;极大地提升了用户体验。 本文将详细介绍AI虚拟主播实时互动模块的…

STM32之看门狗

STM32有独立看门狗&#xff08;IWDG&#xff09;和窗口看门狗(WWDG)。 采用窗口看门狗&#xff08;WWDG&#xff09;&#xff0c;有一个死前中断&#xff0c;可以用来作一个报警的功能。 独立看门狗超时时间计算公式 假设LSI是32KHz,超时时间等于 预分频系数&#xff08;4&…

uni-app 运行HarmonyOS项目

1. uni-app 运行HarmonyOS项目 文档中心 1.1. HarmonyOS端 1.1.1. 准备工作 &#xff08;1&#xff09;下载DevEco Studio开发工具。   &#xff08;2&#xff09;在 DevEco Studio 中打开任意一个项目&#xff08;也可以新建一个空项目&#xff09;。   &#xff08;3&…

巨好看的登录注册界面源码

展示效果 源码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta http-equiv"X-UA-Compatible" content"IEedge" /><meta name"viewport" content"widthdevic…

阿里云docker安装禅道记录

docker network ls docker network create -d bridge cl_network sudo docker run --name zentao --restart always -p 9982:80 --networkcl_network -v /data/zentao:/data -e MYSQL_INTERNALtrue -d hub.zentao.net/app/zentao:18.5 参考&#xff1a;用docker安装禅道…

c++联合

结构体与联合体的区别 结构体(struct)中所有变量是“共存”的——优点是“有容乃大”&#xff0c;全面&#xff1b;缺点是struct内存空间的分配是粗放的&#xff0c;不管用不用&#xff0c;全分配。 而联合体(union)中是各变量是“互斥”的——缺点就是不够“包容”&#xff…

XSS-libs

靶场下载地址&#xff1a;https://github.com/do0dl3/xss-labs 下载完成后解压到phpstudy的根目录下&#xff0c;访问就可以 第一关 参数name 存在alert函数&#xff0c;插入&#xff1a;<script>alert(1)</script>&#xff0c;会调用该函数&#xff0c;然后就会返…

人脑与机器连接:神经科技的伦理边界探讨

内容概要 在当今科技飞速发展的时代&#xff0c;人脑与机器连接已成为一个引人注目的前沿领域。在这一背景下&#xff0c;神经科技的探索为我们打开了一个全新的世界&#xff0c;从脑机接口到人工智能的飞跃应用&#xff0c;不仅加速了技术的进步&#xff0c;更触动了我们内心…

基于向量检索的RAG大模型

一、什么是向量 向量是一种有大小和方向的数学对象。它可以表示为从一个点到另一个点的有向线段。例如&#xff0c;二维空间中的向量可以表示为 (&#x1d465;,&#x1d466;) &#xff0c;表示从原点 (0,0)到点 (&#x1d465;,&#x1d466;)的有向线段。 1.1、文本向量 1…

数字媒体技术基础:AMF(ACES 元数据文件 )

在现代电影和电视制作中&#xff0c;色彩管理变得越来越重要。ACES&#xff08;Academy Color Encoding System&#xff0c;美国电影艺术与科学学院颜色编码系统&#xff09;是一个广泛采用的色彩管理和交换系统&#xff0c;旨在解决不同设备、软件和工作流程之间的色彩不一致问…

k8s环境下rabbitmq安装社区插件:rabbitmq_delayed_message_exchange

怎么在k8s环境下的rabbitmq安装社区版插件:rabbitmq_delayed_message_exchange 在你的rabbit-value.yaml中加入以下行&#xff0c;然后使用helm重新安装&#xff08;最好把pvc也删了重新安装&#xff08;如果你的密码变化了的话&#xff09; 减少出错概率&#xff09; ## par…

C++ | Leetcode C++题解之第526题优美的排列

题目&#xff1a; 题解&#xff1a; class Solution { public:int countArrangement(int n) {vector<int> f(1 << n);f[0] 1;for (int mask 1; mask < (1 << n); mask) {int num __builtin_popcount(mask);for (int i 0; i < n; i) {if (mask &am…

Blender进阶:贴图与UV

9 UV 9.1 贴图与UV UV&#xff0c;指定每个面顶点在贴图上的坐标 演示&#xff1a; 1、添加物体 2、添加贴图&#xff0c;即图片纹理节点 3、进入UV Edit工作区 4、右边&#xff0c;选择一个面 5、左边&#xff0c;选择一个面&#xff0c;移动这个面 9.2 电子表格 电子…

vue项目安装组件失败解决方法

1.vue项目 npm install 失败 删除node_modules文件夹、package-lock.json 关掉安装对话框 重新打开对话框 npm install

uniapp ,微信小程序,滚动(下滑,上拉)到底部加载下一页内容

前言 小程序的内容基本都是滑动到底部加载下一页&#xff0c;这个一般都没有什么好用的组件来用&#xff0c;我看vant和uniapp的插件里最多只有个分页&#xff0c;没有滚动到底部加载下一页。再次做个记录。 效果预览 下滑到底部若是有下一页&#xff0c;则会自动加载下一页&…

分布式光伏系统管理捷径——借助专业软件

在当前信息化时代&#xff0c;管理软件已经成为了各行各业中不可或缺的工具&#xff0c;光伏行业亦是如此。使用专业管理软件&#xff0c;可以帮助光伏企业实现高效管理、提高工作效率的同时降低成本&#xff0c;进而提升竞争力。本文将以客户反映较好的鹧鸪云光伏光伏与储能管…

C++基础:异常

紧接上一篇错误&#xff0c;看C如何应对这些错误问题&#xff0c;与现代编程语言类型&#xff0c;C也提供了一种错误处理机制&#xff1a;异常。 异常&#xff1a; 为了保证检测到的错误不会被遗漏&#xff0c;异常处理的的基本思想是把错误检测&#xff08;在被调函数中完成&a…

「Qt Widget中文示例指南」如何实现窗口嵌入?

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写&#xff0c;所有平台无差别运行&#xff0c;更提供了几乎所有开发过程中需要用到的工具。如今&#xff0c;Qt已被运用于超过70个行业、数千家企业&#xff0c;支持数百万设备及应用。 本文中的示例主要演…