Linux系统编程项目——FTP网盘

目录

一、项目实现的功能

二、项目实现思路

2.1 socket服务器和客户端开发步骤:

2.2 指令的实现思路:

三、项目用到的函数

3.1 判断文件是否存在函数access()原型和头文件:

3.2 字符串输入函数fgets()原型和头文件:

3.3 字符串分割函数strtok()原型和头文件:

3.4 当前工作目录更改为指定的工作目录函数chdir()原型和头文件:

四、FTP项目实现


一、项目实现的功能

  • 在客户端对服务器的操作:

    1. 获取服务器的文件get

    2. 查看服务器当前路径下的所有文件ls

    3. 进入服务器的某个文件夹cd

    4. 上传文件到服务器put

    5. 查看当前服务器路径pwd

    6. 退出quit

  • 在客户端本地的功能实现:

    1. 查看客户端当前路径下有哪些文件lls

    2. 进入客户端的某个文件夹lcd

    3. 查看当前客户端路径lpwd

    4. 退出连接quit

二、项目实现思路

2.1 socket服务器和客户端开发步骤:

  • 服务器server

    1. 创建套接字(socket

    2. 将socket与IP地址和端口绑定(bind

    3. 监听被绑定的端口(listen

    4. 接收连接请求(accept

    5. 当有客户端接入时创建子进程对接并处理客户端发来的请求

  • 客户端client

    1. 创建套接字(socket

    2. 连接指定计算机的端口(connect

    3. 接收输入向socket中写入信息(write

    4. 获取客户端发送的内容像ls,pwd,get指令,需要进行(read)

2.2 指令的实现思路:

ls与pwd:调用popen函数,执行命令,并获取执行命令后的输出内容,将内容发回到客户端

cd:将cd指令发送到服务器,在服务器端调用chdir实现路径的切换

get:客户端获取服务器的某个文件,服务器首先通过access函数判断文件是否存在,存在则将文件的内容发送到客户端,客户端创建文件,并将服务器发送的内容保存至文件中,实现get指令。

put:客户端向服务器发送文件,客户端首先通过access函数判断文件是否存在,存在则将文件的内容发送到服务器,服务器创建文件,并将客户端发送的内容保存至文件中,实现put指令。

lls,lpwd:调用system函数即可。

lcd:在客户端直接调用chdir函数即可

三、项目用到的函数

3.1 判断文件是否存在函数access()原型和头文件:

/*Linux下 man 2 access查看手册
*/
#include <unistd.h>int access(const char *pathname, int mode);int 	函数返回值,返回值如果成功(所有请求的权限都被授予,或者mode为F_OK并且文件存在),返回0。如果出现错误(mode中至少有一个		  位请求的权限被拒绝,或者mode为F_OK而文件不存在,或者发生了其他错误),则返回-1,并适当设置errno。char *pathname	需要检测的文件路径名int mode		需要测试的操作模式
1. R_OK 测试读许可权
2. W_OK 测试写许可权
3. X_OK 测试执行许可权
4. F_OK 测试文件是否存在/*函数说明:判断参数中的文件名是否存在*/

3.2 字符串输入函数fgets()原型和头文件:

/*Linux下 man fgets查看手册
*/
#include <stdio.h>char *fgets(char *s, int size, FILE *stream);char *			函数返回值,fgets()成功时返回s(数组首地址),错误时返回NULL,或者在没有读取任何字符的情况下出现文件结束。char *s			它是一个指向字符数组的指针,用于存储读取的文本行。
int size		表示要读取的最大字符数(包括空字符)
FILE *stream	表示从何种流中读取,可以是标准输入流 stdin,也可以是文件流,即从某个文件中读取/*函数说明:虽然用 gets() 时有空格也可以直接输入,但是 gets() 有一个非常大的缺陷,即它不检查预留存储区是否能够容纳实际输入的数据,换句	 话说,如果输入的字符数目大于数组的长度,gets 无法检测到这个问题,就会发生内存越界,所以编程时建议使用 fgets()
*/

3.3 字符串分割函数strtok()原型和头文件:

/*Linux下 man strtok查看手册
*/
#include <string.h>char *strtok(char *str, const char *delim);	char *		会返回一个指向被分割出的第一个子字符串(即第一个空格之前的部分)的指针。如果没有更多的分割部分,则返回 NULL。char *str	指向要被分割的字符串的指针最初调用时,为需要分割的字符串。后续调用时,为 NULL 以获取下一部分标记。char *delim	指定用来分割字符串的分隔符,比如说空格" "一个包含分隔符的字符串,这些字符用于分割字符串中的标记。/*函数说明:strtok 函数用于将字符串 str 分割成一系列标记 (token),这些标记由参数 delim 中的字符分隔开来。*/使用方式:
strtok 首次调用时传入需要分割的字符串,并传入分隔符字符串。后续调用时,将 str 参数传递为 NULL,以继续检索同一字符串中的下一部分内容
char str[] = "hello world";
char delim[] = " ";
char *token = strtok(str, delim);while (token != NULL) {printf("Token: %s\n", token);token = strtok(NULL, delim);
}

3.4 当前工作目录更改为指定的工作目录函数chdir()原型和头文件:

/*Linux下 man 2 chdir查看手册
*/
#include <unistd.h>int chdir(const char *path);int			函数返回值,通常,当成功时返回 0,失败时返回 -1,并设置 errno 来指示出错原因。char *path	参数是一个字符串指针 path,表示要改变到的目录路径。该路径可以是绝对路径或相对路径。/*函数说明:是一个标准库函数,它的主要功能是改变当前工作目录。它接收一个路径参数并尝试将当前工作目录更改为指定的路径,在成功时返回 0,失败时返回 -1 并设置错误代码。这个功能对于实现像 FTP 服务器这样的程序非常重要,因为它允许服务器根据客户端请求动态更改当前工作目录。
*/

四、FTP项目实现

/*config.h*/
#define LS     0					
#define PWD    1
#define GET    2#define IFGO   3#define CD     4
#define PUT    5
#define LLS    6
#define LCD    7
#define LPWD   8#define QUIT   9
#define DOFILE 10struct MSG
{int type;                                                                        // message type    char cmd[1024];                                                                // message datachar secondBuf[1024];                                                            // second buffer for data  
};

宏定义部分:

  1. #define LS 0 定义一个宏 LS,其值为 0

  2. #define PWD 1 定义一个宏 PWD,其值为 1

  3. #define GET 2 定义一个宏 GET,其值为 2

  4. #define IFGO 3 定义一个宏 IFGO,其值为 3

  5. #define CD 4 定义一个宏 CD,其值为 4

  6. #define PUT 5 定义一个宏 PUT,其值为 5

  7. #define LLS 6 定义一个宏 LLS,其值为 6

  8. #define LCD 7 定义一个宏 LCD,其值为 7

  9. #define LPWD 8 定义一个宏 LPWD,其值为 8

  10. #define QUIT 9 定义一个宏 QUIT,其值为 9

  11. #define DOFILE 10 定义一个宏 DOFILE,其值为 10

这些宏定义为一组命令标识符赋予了整数值,以便在代码中更便捷地使用这些命令。

结构体定义部分:

  1. struct MSG

    定义了一个名为MSG的结构体,其中包含三个成员:

    • int type: 一个整型变量,用于表示消息类型。

    • char cmd[1024]: 一个长度为 1024 的字符数组,用于存储命令数据。

    • char secondBuf[1024]: 一个长度为 1024 的字符数组,用于存储额外的数据。

总结:

这段代码的主要功能是定义了一组文件传输协议(FTP)相关的命令及其对应的整数值,同时定义了一个结构体 MSG 用于存储这些命令及其数据。

  • 命令标识符:通过宏定义,赋予了 LS, PWD, GET 等命令对应的整数值。这些命令表示了常见的 FTP 操作,如列出目录内容、获取当前目录、从服务器获取文件等。

  • 消息结构体MSG 结构体用于传递 FTP 命令及其相关数据。type 成员存储命令类型,cmdsecondBuf 成员分别存储命令数据和额外的数据。

这段代码通过宏和结构体的定义,为实现 FTP 客户端与服务器之间的通信打下了基础。

/*serverFTP.c*/
#include <stdio.h>                                                                 // 包含标准输入输出头文件
#include <stdlib.h>                                                                      // 包含标准库头文件   
#include <sys/types.h>                                                               // 包含系统类型定义头文件
#include <sys/socket.h>                                                                  // 包含套接字编程头文件 
#include <netinet/in.h>                                                                  // 包含网络地址头文件 
#include <arpa/inet.h>                                                                   // 包含网络字节序头文件 
#include <string.h>                                                                      // 包含字符串头文件 
#include <unistd.h>                                                                      // 包含unistd头文件 
#include "config.h"                                                                      // 包含配置文件头文件
#include <sys/stat.h>                                                                     // 包含文件状态头文件
#include <fcntl.h>                                                                        // 包含文件控制头文件char* get_cmd_dir(char *cmd)                                                                //获取目录名函数
{char *p = NULL;p = strtok(cmd," ");                                                                  //分割命令字符串p = strtok(NULL," ");                                                                 //获取目录名return p;                                                                             //返回目录名
}int cmd_type(char *cmd)                                                                    //命令类型判断函数
{if(!strcmp("ls", cmd))     return LS;                                                  //ls命令if(!strcmp("pwd", cmd))    return PWD;                                                 //pwd命令if(!strcmp("quit",cmd))    return QUIT;                                                //quit命令if(strstr(cmd,"cd"))       return CD;                                                  //cd命令if(strstr(cmd,"get"))      return GET;                                                 //get命令if(strstr(cmd,"put"))      return PUT;                                                 //put命令return -1;                                                                             //未知命令
}void msg_handler(int c_fd, struct MSG msg)                                               //消息处理函数    
{int ret;                                                                                //命令类型int fdfile;                                                                             //文件描述符char *dir  = NULL;                                                                      //目录名    char *file = NULL;                                                                       //文件名char dataBuf[1024] = {0};                                                                //数据缓冲区   printf("客户端发送消息:%s\n", msg.cmd);                                               //打印客户端消息ret = cmd_type(msg.cmd);                                       //将客户端命令类型转为int类型switch(ret){                                                             //根据命令类型进行处理case LS:case PWD:msg.type = 0;                                                 //设置消息类型为0,表示命令执行成功FILE *fp = popen(msg.cmd, "r");                                              //打开命令管道fread(msg.cmd,sizeof(msg.cmd),1,fp);                                          //读取命令输出write(c_fd, &msg, sizeof(msg));                                               //发送命令执行结果pclose(fp);                                                                     //关闭命令管道break;case CD:msg.type = 1;                                                               //设置消息类型为1dir = get_cmd_dir(msg.cmd);                                                   //获取目录名chdir(dir);                                                                   //切换目录    break;case GET:file = get_cmd_dir(msg.cmd);                                                    //获取文件名if(access(file, F_OK) == -1){                                                  //判断文件是否存在                                                     strcpy(msg.cmd, "文件不存在!");                   //设置命令执行结果为文件不存在write(c_fd, &msg, sizeof(msg));                              //发送命令执行结果}else{msg.type = DOFILE;                                    //设置消息类型为DOFILE   fdfile = open(file, O_RDWR);                                                  //打开文件 read(fdfile, dataBuf, sizeof(dataBuf));                                       //读取文件内容close(fdfile);                                                              //关闭文件strcpy(msg.secondBuf, dataBuf);                  //设置文件内容到消息结构体的secondBuf                                                       write(c_fd, &msg, sizeof(msg));                                               //发送文件内容}break;case PUT:fdfile = open(get_cmd_dir(msg.cmd), O_RDWR | O_CREAT, 0666); //服务器首先创建并打开要上传的文件write(fdfile,msg.secondBuf,sizeof(msg.secondBuf));//然后将客户端上传给服务器文件里的内容拷贝到新创建的文件中close(fdfile);                                                                   //关闭文件break;case QUIT:printf("客户端断开连接!\n");exit(-1);                                                                       //退出程序  }
}int main(int argc, char **argv)
{int s_fd;                                                                               //socket文件描述符int c_fd;                                                                               //client文件描述符pid_t pid;                                                                              //子进程pidint n_read;                                                                             //读字节数struct MSG msg;                                                                        //消息结构体struct sockaddr_in server_addr;                                                         //服务器地址结构体struct sockaddr_in client_addr;                                                         //客户端地址结构体memset(&server_addr, 0, sizeof(server_addr));                               //清零服务器地址结构体memset(&client_addr, 0, sizeof(client_addr));                             //清零客户端地址结构体if(argc != 3){                                                                          //参数个数错误  printf("参数错误!请按照格式输入:./serverFTP IP地址 端口号");exit(-1);                                                                           //退出程序  }   //int socket(int domain, int type, int protocol);s_fd = socket(AF_INET, SOCK_STREAM, 0);                                               //创建socket套接字  if(s_fd == -1){printf("创建socket失败!");exit(-1);}server_addr.sin_family = AF_INET;     			 // 设置服务器地址结构体的地址族为AF_INET,表示使用IPv4协议server_addr.sin_port = htons(atoi(argv[2]));                                // 设置服务器地址结构体的端口号inet_aton(argv[1], &server_addr.sin_addr);                                    // 设置服务器地址结构体的IP地址//int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);int ret = bind(s_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));            //绑定socketif(ret == -1){printf("绑定socket失败!");exit(-1);}//int listen(int sockfd, int backlog);ret = listen(s_fd, 10);                                                                //监听socket    if(ret == -1){printf("监听失败!\n");}printf("服务器启动成功!\n");int client_addr_len = sizeof(client_addr);                                              //客户端地址长度while(1){//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);c_fd = accept(s_fd,(struct sockaddr *)&client_addr, &client_addr_len);         //接受连接请求                       																			if(c_fd == -1){printf("接受客户端连接请求失败!");exit(-1);}printf("客户端连接成功!\n");										 //打印客户端地址printf("客户端地址:%s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));           //pid_t fork(void);pid = fork();                                                                        //创建子进程if(pid == -1){printf("创建子进程失败!");exit(-1);}else if(pid == 0){while(1){memset(&msg, 0, sizeof(msg));                                                //清零消息结构体//ssize_t read(int fd, void *buf, size_t count);n_read = read(c_fd, &msg, sizeof(msg));                                       //读取客户端消息if(n_read == 0){printf("客户端断开连接!\n");exit(0);                                                                //退出子进程}else if(n_read > 0){printf("客户端发送消息:%s\n", msg.cmd);                                     //打印客户端消息msg_handler(c_fd, msg);                                                   //处理客户端消息}}}    }close(s_fd);                                                                            //关闭socketclose(c_fd);                                                                            //关闭clientreturn 0;
}

这段代码实现了一个基本的FTP服务器功能,主要包括以下几个方面:

  1. 创建和绑定套接字:通过 socket()bind()listen() 等函数完成服务器端的初始化工作。

  2. 处理客户端连接:使用 accept() 函数接收客户端的连接请求,并创建子进程处理每个客户端。

  3. 命令处理:通过 msg_handler() 处理客户端发送的命令,包括 lspwdcdgetputquit 等命令。

  4. 数据传输:根据不同的命令类型进行相应的文件操作,并将结果反馈给客户端。

这个FTP服务器具有基本的文件传输和目录操作功能,适合用于学习和理解FTP服务器的基本实现原理。

/*clientFTP.c*/
#include <stdio.h>               // 包含标准输入输出头文件
#include <stdlib.h>              // 包含标准库函数头文件
#include <sys/types.h>           // 包含数据类型头文件
#include <sys/socket.h>          // 包含Socket通信头文件
#include <netinet/in.h>          // 包含Internet地址族头文件
#include <arpa/inet.h>           // 包含IP地址转换功能头文件
#include <string.h>              // 包含字符串操作函数头文件
#include "config.h"              // 包含配置文件头文件
#include <unistd.h>              // 包含unistd.h头文件
#include <sys/stat.h>            // 包含文件状态头文件
#include <fcntl.h>               // 包含文件控制头文件char* getDir(char *cmd)                                                               //获取文件路径函数
{char *p = NULL;                                                                   //文件路径指针p = strtok(cmd, " ");                                                            //分割命令字p = strtok(NULL," ");                                                             //获取文件路径return p;
}int cmd_type(char *cmd)                                                               //命令类型判断函数
{if(strstr(cmd, "lcd"))          return LCD;                                       //判断是否为LCD命令if(strcmp(cmd, "lpwd") == 0)    return LPWD;                                      //判断是否为LPWD命令if(strcmp(cmd, "lls") == 0)     return LLS;                                       //判断是否为LLS命令if(strcmp(cmd, "ls") == 0)      return LS;                                        //判断是否为LS命令if(strcmp(cmd, "pwd") == 0)     return PWD;                                       //判断是否为PWD命令if(strcmp(cmd, "quit") == 0)    return QUIT;                                      //判断是否为QUIT命令if(strstr(cmd, "cd"))           return CD;                                        //判断是否为CD命令if(strstr(cmd, "get"))          return GET;                                       //判断是否为GET命令if(strstr(cmd, "put"))          return PUT;                                       //判断是否为PUT命令return -1;                                                                        //未知命令类型
}int cmd_handler(int c_fd, struct MSG msg)                                               //命令处理函数
{int ret;                                                                            //返回值char *p = NULL;                                                                   //命令字指针char buf[128] = {0};                                                              //缓冲区int file_fd;                                                                       //文件描述符char *dir = NULL;                                                                  //文件路径指针ret = cmd_type(msg.cmd);                                                          //获取命令类型 switch(ret){case LS:case PWD:msg.type = 0;                                                              //设置消息类型为0write(c_fd, &msg, sizeof(msg));                                            //发送消息   break;case CD:msg.type = 1;                                                              //设置消息类型为1write(c_fd, &msg, sizeof(msg));                                            //发送消息   break;case GET:msg.type = 2;                                                              //设置消息类型为2 write(c_fd, &msg, sizeof(msg));                                            //发送消息 break;case PUT:strcpy(buf, msg.cmd);                                                      //拷贝命令字到缓冲区p = getDir(buf);                                                            //获取文件路径if(access(p, F_OK) == -1){                                                 //判断文件是否存在printf("文件不存在\n");}else{file_fd = open(p, O_RDWR);                                            //可读可写方式打开文件read(file_fd, &msg.secondBuf, sizeof(msg.secondBuf));       //读文件内容到消息结构体的第二个缓冲区close(file_fd);                                                         //关闭文件write(c_fd, &msg, sizeof(msg));                                        //发送消息}break;case LCD:dir = getDir(msg.cmd);                                                      //获取文件路径if(chdir(dir) == -1){                                                     //切换目录printf("目录不存在\n");}break;case LLS:system("ls");                                                               //执行系统命令  break;case LPWD:system("pwd");                                                               //执行系统命令  break;case QUIT:strcpy(msg.cmd, "quit");                                                    //拷贝命令字到消息结构体write(c_fd, &msg, sizeof(msg));                                            //发送消息close(c_fd);                                                                //关闭套接字exit(-1);                                                                   //退出程序break;}   return ret;                                                                        //返回命令类型
}void handler_sever_message(int c_fd, struct MSG msg)                                    //处理服务器消息函数
{int n_read;                                                                        //读取字节数struct MSG getmsg;                                                                //接收消息结构体 char *filename;                                                                   //文件名指针int newfile_fd;                                                                    //新文件描述符n_read = read(c_fd, &getmsg, sizeof(getmsg));                              //接收消息if(n_read == 0){printf("服务器关闭连接\n");exit(-1);}if(getmsg.type == DOFILE){      //当客户端输入的指令是GET时,需要在客户端创建需要获取的文件filename = getDir(msg.cmd);                                                      //获取文件名newfile_fd = open(filename, O_RDWR|O_CREAT, 0666);                              //创建新文件write(newfile_fd, getmsg.secondBuf, sizeof(getmsg.secondBuf));                    //写入文件内容close(newfile_fd);                                                             //关闭文件printf("文件%s上传成功\n", filename);                                            //打印提示信息fflush(stdout);                                                                 //刷新标准输出缓冲区}else{printf("------------------------------\n");printf("服务器消息: %s\n", getmsg.cmd);                                           //打印服务器消息printf("------------------------------\n");}}int main(int argc, char **argv)
{int c_fd;                                                                        //客户端套接字文件描述符int ret;                                                                         //返回值struct sockaddr_in client_addr;                                                   //客户端地址结构体struct MSG msg;                                                                  //消息结构体memset(&client_addr, 0, sizeof(client_addr));                                      //清零客户端地址结构体if(argc != 3){                                                                    //判断参数是否正确printf("参数错误,请按照格式输入: clientFTP IP 端口号\n");exit(-1);}//int socket(int domain, int type, int protocol);c_fd = socket(AF_INET, SOCK_STREAM, 0);                                            //创建客户端套接字if(c_fd == -1){printf("创建客户端套接字失败\n");perror("socket");exit(-1);}client_addr.sin_family = AF_INET;                                     //设置客户端地址结构体的地址族为IPv4client_addr.sin_port = htons(atoi(argv[2]));                                   //设置客户端地址结构体的端口号inet_aton(argv[1], &client_addr.sin_addr);;                                       //设置客户端IP地址   //int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);ret = connect(c_fd, (struct sockaddr *)&client_addr, sizeof(client_addr));           //连接服务器if(ret == -1){printf("连接服务器失败\n");perror("connect");exit(-1);}printf("连接服务器成功\n");while(1){memset(&msg.cmd, 0, sizeof(msg.cmd));                                          //清零消息结构体的命令字printf("请输入命令字: \n");gets(msg.cmd);                                                                   //从标准输入获取命令字printf("------------------------------\n");printf("客户端消息: %s\n", msg.cmd);                                               //打印客户端消息printf("------------------------------\n");ret = cmd_handler(c_fd, msg);                                                  //处理命令字if(ret > IFGO){fflush(stdout);                                                             //刷新标准输出缓冲区continue;                                                                   //继续循环}if(ret == -1){printf("未知命令类型\n");fflush(stdout);                                                             //刷新标准输出缓冲区continue;                                                                   //继续循环}handler_sever_message(c_fd, msg);                                              //处理服务器消息}return 0;
}

这段代码的主要功能是实现一个基本的FTP客户端,能够与FTP服务器建立连接并执行一系列命令,具体包括:

  1. 与服务器建立连接

    • 创建TCP套接字,并通过IP地址和端口号与指定的FTP服务器建立连接。

  2. 处理用户输入的命令

    • 命令包括本地操作(本地切换目录、本地列出目录内容、本地打印当前目录)、远程操作(远程切换目录、远程查询目录内容)、文件传输(上传和下载文件)以及退出命令等。

    • 根据命令类型,通过不同的分支进行处理,并准备好相关的消息发送给服务器。

  3. 与服务器交互

    • 将用户的命令打包成特定格式的消息,通过套接字发送给服务器。

    • 接收服务器的回应消息,并根据消息类型进行处理。

  4. 本地文件和目录操作

    • 在本地执行系统命令,如ls(列出目录内容)和pwd(打印当前目录)。

    • 切换本地目录。

    • 判断本地文件是否存在并进行文件读取,上载(上传)文件内容到服务器。

  5. 处理服务器回应

    • 根据服务器返回的消息类型,执行相应操作。

    • 例如,当从服务器下载文件时,在本地创建相应文件并写入内容。

总结而言,这段代码实现了一个基本的FTP客户端,通过命令行操作与FTP服务器进行交互,实现文件传输、目录操作以及简单的本地系统命令执行。通过本地和远程命令的处理逻辑,客户端能够进行从服务器下载文件到本地、上传本地文件到服务器、查询本地和远程目录等常见功能。

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

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

相关文章

无线麦克风哪个品牌音质最好,一文告诉你无线领夹麦克风怎么挑选

随着直播带货和个人视频日志&#xff08;Vlog&#xff09;文化的兴起&#xff0c;以及自媒体内容创作的蓬勃发展&#xff0c;我们见证了麦克风行业的迅猛发展。在这一浪潮中&#xff0c;无线领夹麦克风以其无与伦比的便携性和操作效率&#xff0c;迅速赢得了广大视频制作者的喜…

前端HTML/CSS知识点系列

1. 什么是块级格式化上下文&#xff1f;【BFC(Block formatting context)】 BFC&#xff08;Block FormattingContext&#xff0c;块级格式化上下文&#xff09;是一个独立的渲染区域&#xff0c;其中的元素的布局不会受到外部元素的影响&#xff0c;反之亦然。BFC的创建有助于…

docker环境部署ruoyi系统前后端分离项目

创建局域网 docker network create net-ry 安装Redis 1 安装 创建两个目录 mkdir -p /data/redis/{conf,data} 上传redis.conf文件到/data/redis/conf文件夹中 cd /data/redis/conf 3.2 配置redis.conf文件 配置redis.conf文件&#xff1a; redis.conf文件配置注意&…

【数据库】四、数据库编程(SQL编程)

四、数据库编程 另一个大纲&#xff1a; 5.1存储过程 5.1.1存储过程基本概念 5.1.2创建存储过程 5.1.3存储过程体 5.1.4调用存储过程 5.1.5删除 5.2存储函数 5.2.1创建存储函数 5.2.2调用存储函数 5.2.3删除存储函数 目录 文章目录 四、数据库编程1.SQL编程基础1.1常量1.2变…

轻松两步,借助向量数据库 VectorDB 与千帆 Appbuilder 构建个性化本地问答知识库

在我们日常的工作和生活中&#xff0c;经常会遇到需要快速获取和管理大量信息的情况。无论是解答客户的问题&#xff0c;还是整理公司内部的资料&#xff0c;一个高效的知识库系统都能帮我们省下大量时间和精力。 为了帮助大家快速构建 RAG 应用&#xff0c;我们之前发布了一个…

虚拟机中VSCode+gcc环境配置

一、安装VSCode 1、在官网下载软件包&#xff1a; 地址&#xff1a;Documentation for Visual Studio Code 2、下载后在放置deb包的文件夹直接打开终端&#xff0c;然后输入sudo dpkg -i code_1.90.2-1718751586_amd64.deb 3、安装成功提示&#xff0c;并显示该图标 二、配…

nginx+keepalived+tomcat集群实验

如遇星河 | nginx+keepalived高可用集群实验 木子87 | Keepalived+Nginx+Tomcat 实现高可用Web集群 环境 192.168.40.204 tomcat-1 192.168.40.138 tomcat-2 安装tomcat [root@bogon local]# vim /etc/profile 添加环境变量 JAVA_HOME=/usr/local/java PATH=$J…

mac 常用工具命令集合

一、vim 快捷键 1、移动光标 h j k l 左 下 上 右 箭头上 上移一行 箭头下 下移一行 0 跳至行首&#xff0c;不管有无缩进&#xff0c;就是跳到第0个字符 ^ 跳至行首的第一个字符 $ 跳至行尾 gg 跳至文首 G 调至文尾 5gg/5G 调至第5行w 跳到下一个字首&#xff0c;按标点或…

Java中如何处理ArrayIndexOutOfBoundsException异常?

Java中如何处理ArrayIndexOutOfBoundsException异常&#xff1f; 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;在Java编程中&#xff0c;ArrayIndexOutOfBoun…

微信小程序学习(十):生命周期

1、应用生命周期 生命周期说明onLaunch监听小程序初始化&#xff0c;全局只会执行 1 次onShow监听小程序启动或切前台onHide监听小程序切后台 &#x1f517;应用生命周期官方文档 App({/*** 当小程序初始化完成时&#xff0c;会触发 onLaunch&#xff08;全局只触发一次&…

固特超声波清洗机怎么样?三大超声波清洗机美的、固特、希亦谁更好?

眼镜是我们日常生活中不可或缺的用具&#xff0c;但随着使用时间的增长&#xff0c;眼镜上的灰尘和污垢也会逐渐积累&#xff0c;传统的清洗方法往往难以彻底清洁。为了解决这一难题&#xff0c;超声波清洗机出现了&#xff01;它利用超声波振动原理&#xff0c;可以轻松、快速…

加载资源文件失败

背景 自己以前装了一个海康的深度学习算法平台&#xff0c;试用期是一个月&#xff0c;过了一个月之后&#xff0c;因为没有有效注册码或者加密狗的支持了导致无法使用&#xff0c;于是打算卸载掉&#xff0c;在卸载一个软件的时候&#xff0c;无论是使用控制面板还是软件自带的…

http 和 https 建立连接的不同

HTTP和HTTPS在建立连接时都需要进行“三次握手”&#xff0c;因为三次握手是TCP协议的一部分&#xff0c;用于建立可靠的传输层连接。 HTTP和HTTPS在建立连接时的过程 HTTP 的三次握手 HTTP协议本身是无状态的应用层协议&#xff0c;它依赖于传输层的TCP协议来建立和管理连接…

AI写文章生成器,这些工具都可以一键智能生成文章

在AI技术快速发展的今天&#xff0c;AI写作生成器成为我们创作内容的重要工具&#xff0c;它可以提高我们的写作效率&#xff0c;节省时间和精力。下面小编就来和大家分享几款优秀的AI写作生成器&#xff0c;帮助你快速生成高质量的文章。 1.专业AI写作工具-文章在线生成器 专…

LeetCode热题100——字母异位词分组

给你一个字符串数组&#xff0c;请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。 字母异位词 是由重新排列源单词的所有字母得到的一个新单词。 示例 1: 输入: strs [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”] 输出: [[“bat”],[“nat”,“tan”…

计算机网络 访问控制列表以及NAT

一、理论知识 1. 单臂路由 单臂路由是一种在路由器上配置多个子接口的方法&#xff0c;每个子接口代表不同的 VLAN&#xff0c;用于在一个物理接口上支持多 VLAN 通信。此方法使得不同 VLAN 之间可以通过路由器进行通信。 2. NAT (网络地址转换) NAT 是一种在私有网络和公共…

步步精科技诚邀您参加2024慕尼黑上海电子展

尊敬的客户&#xff1a; 我们诚挚地邀请您参加即将于2024年7月8日至7月10日在上海新国际博览中心举办的2024慕尼黑上海电子展&#xff08;electronica China&#xff09;。此次展会汇聚了国内外优秀企业&#xff0c;展示从元器件到系统集成方案的完整产品链&#xff0c;为各行…

FragMent嵌套问题

在我们日常项目中应该经常会用到多个fragment嵌套的代码架构&#xff0c;虽然架构简单&#xff0c;但是其中会经常遇到各种不知名和各种奇葩的问题 1.页面显示空白 现象&#xff1a;当我们应用切换到其他应用&#xff0c;然后再重新进入到我们的应用&#xff0c;这时&#xff…

linux 下配置docker mirrors

一、配置mirrors vi /etc/docker/daemon.json {"registry-mirrors": ["https://docker.blfrp.cn"],"log-opts": {"max-size": "10m","max-file": "3"} }#完成配置后重启docker systemctl restart dock…

Python里dict是什么?

在Python中&#xff0c;dict&#xff08;或称为字典&#xff09;是一种可变的数据类型&#xff0c;用于存储键值对&#xff08;key-value pairs&#xff09;。它允许你以任意类型的数据作为键&#xff08;key&#xff09;&#xff0c;并将键与值&#xff08;value&#xff09;关…