Linux小项目:在线词典开发

news2024/11/18 3:37:09

在线词典介绍

  • 流程图如下:
    在这里插入图片描述
  • 项目的功能介绍
    • 在线英英词典
    • 项目功能描述
    • 用户注册和登录验证
    • 服务器端将用户信息和历史记录保存在数据中。客户端输入用户和密码,服务器端在数据库中查找、匹配,返回结果
    • 单词在线翻译
    • 根据客户端输入输入的单词在字典文件中搜索
    • 历史记录查询
    • 项目分析
  • 项目流程
    • 定义数据库中表的结构
    • 定义消息结构体
    • 分析服务端和客户端流程
    • 编码实现
  • 客户端流程图分析
    • 服务器和客户端通信的结构体
      在这里插入图片描述
    • 客户端流程图如下:
      在这里插入图片描述
    • 服务器端流程图如下: ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/64b2312c006e4298a3f382597ff62558.png

在线词典的代码编写

  • 客户端代码的编写:
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <string.h>
    
    #define   N  32
    
    #define  R  1   // user - register
    #define  L  2   // user - login
    #define  Q  3   // user - query
    #define  H  4   // user - history
    
    // 定义通信双方的信息结构体
    typedef struct {
    	int type;
    	char name[N];
    	char data[256];
    }MSG;
    
    
    int  do_register(int sockfd, MSG *msg)
    {
    	msg->type = R;
    
    	printf("Input name:");
    	scanf("%s", msg->name);
    	getchar();
    
    	printf("Input passwd:");
    	scanf("%s", msg->data);
    
    	if(send(sockfd, msg, sizeof(MSG),0) < 0)
    	{
    		printf("fail to send.\n");
    		return -1;
    	}
    
    	if(recv(sockfd, msg, sizeof(MSG), 0) < 0)
    	{
    		printf("Fail to recv.\n");
    		return -1;
    	}
    
    	// ok !  or  usr alread exist.
    	printf("%s\n", msg->data);
    
    	return 0;
    }
    
    int do_login(int sockfd, MSG *msg)
    {
    	msg->type = L;
    
    	printf("Input name:");
    	scanf("%s", msg->name);
    	getchar();
    
    	printf("Input passwd:");
    	scanf("%s", msg->data);
    
    	if(send(sockfd, msg, sizeof(MSG),0) < 0)
    	{
    		printf("fail to send.\n");
    		return -1;
    	}
    
    	if(recv(sockfd, msg, sizeof(MSG), 0) < 0)
    	{
    		printf("Fail to recv.\n");
    		return -1;
    	}
    
    	if(strncmp(msg->data, "OK", 3) == 0)
    	{
    		printf("Login ok!\n");
    		return 1;
    	}
    	else 
    	{
    		printf("%s\n", msg->data);
    	}
    
    	return 0;
    }
    
    int do_query(int sockfd, MSG *msg)
    {
    	msg->type = Q;
    	puts("--------------");
    
    	while(1)
    	{
    		printf("Input word:");
    		scanf("%s", msg->data);
    		getchar();
    
    		//客户端,输入#号,返回到上一级菜单
    		if(strncmp(msg->data, "#", 1) == 0)
    			break;
    
    		//将要查询的单词发送给服务器
    		if(send(sockfd,msg, sizeof(MSG), 0) < 0)
    		{
    			printf("Fail to send.\n");
    			return -1;
    		}
    
    		// 等待接受服务器,传递回来的单词的注释信息
    		if(recv(sockfd, msg,sizeof(MSG), 0) < 0)
    		{
    			printf("Fail to recv.\n");
    			return -1;
    		}
    		printf("%s\n", msg->data);
    	}
    		
    	return 0;
    }
    
    int do_history(int sockfd, MSG *msg)
    {
    
    	msg->type = H;
    
    	send(sockfd, msg, sizeof(MSG), 0);
    	
    	// 接受服务器,传递回来的历史记录信息
    	while(1)
    	{
    		recv(sockfd, msg, sizeof(MSG), 0);
    
    		if(msg->data[0] == '\0')
    			break;
    
    		//输出历史记录信息
    		printf("%s\n", msg->data);
    	}
    
    	return 0;
    }
    
    // ./server  192.168.3.196  10000
    int main(int argc, const char *argv[])
    {
    
    	int sockfd;
    	struct sockaddr_in  serveraddr;
    	int n;
    	MSG  msg;
    
    	if(argc != 3)
    	{
    		printf("Usage:%s serverip  port.\n", argv[0]);
    		return -1;
    	}
    
    	if((sockfd = socket(AF_INET, SOCK_STREAM,0)) < 0)
    	{
    		perror("fail to socket.\n");
    		return -1;
    	}
    
    	bzero(&serveraddr, sizeof(serveraddr));
    	serveraddr.sin_family = AF_INET;
    	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    	serveraddr.sin_port = htons(atoi(argv[2]));
    
    	if(connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    	{
    		perror("fail to connect");
    		return -1;
    	}
    
    	while(1)
    	{
    		printf("*****************************************************************\n");
    		printf("* 1.register          2.login              3.quit               *\n");
    		printf("*****************************************************************\n");
    		printf("Please choose:");
    
    		scanf("%d", &n);
    		getchar();
    
    		switch(n)
    		{
    		case 1:
    			do_register(sockfd, &msg);
    			break;
    		case 2:
    			if(do_login(sockfd, &msg) == 1)
    			{
    				goto next;
    			}
    			break;
    		case 3:
    			close(sockfd);
    			exit(0);
    			break;
    		default:
    			printf("Invalid data cmd.\n");
    		}
    
    	}
    
    next:
    	while(1)
    	{
    		printf("*****************************************************\n");
    		printf("* 1.query_word   2.history_record   3.quit          *\n");
    		printf("*****************************************************\n");
    		printf("Please choose:");
    		scanf("%d", &n);
    		getchar();
    
    		switch(n)
    		{
    			case 1:
    				do_query(sockfd, &msg);
    				break;
    			case 2:
    				do_history(sockfd, &msg);
    				break;
    			case 3:
    				close(sockfd);
    				exit(0);
    				break;
    			default :
    				printf("Invalid data cmd.\n");
    		}
    
    	}
    	
    	return 0;
    

}
```

  • 服务器端代码编写如下:
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <string.h>
    #include <unistd.h>
    #include <sqlite3.h>
    #include <signal.h>
    #include <time.h>
    
    
    #define   N  32
    
    #define  R  1   // user - register
    #define  L  2   // user - login
    #define  Q  3   // user - query
    #define  H  4   // user - history
    
    #define  DATABASE  "my.db"
    
    // 定义通信双方的信息结构体
    typedef struct {
    	int type;
    	char name[N];
    	char data[256];
    }MSG;
    
    
    
    int do_client(int acceptfd, sqlite3 *db);
    void do_register(int acceptfd, MSG *msg, sqlite3 *db);
    int do_login(int acceptfd, MSG *msg, sqlite3 *db);
    int do_query(int acceptfd, MSG *msg, sqlite3 *db);
    int do_history(int acceptfd, MSG *msg, sqlite3 *db);
    int history_callback(void* arg,int f_num,char** f_value,char** f_name);
    int do_searchword(int acceptfd, MSG *msg, char word[]);
    int get_date(char *date);
    
    // ./server  192.168.3.196  10000
    int main(int argc, const char *argv[])
    {
    
    	int sockfd;
    	struct sockaddr_in  serveraddr;
    	int n;
    	MSG  msg;
    	sqlite3 *db;
    	int acceptfd;
    	pid_t pid;
    
    	if(argc != 3)
    	{
    		printf("Usage:%s serverip  port.\n", argv[0]);
    		return -1;
    	}
    
    	//打开数据库
    	if(sqlite3_open(DATABASE, &db) != SQLITE_OK)
    	{
    		printf("%s\n", sqlite3_errmsg(db));
    		return -1;
    	}
    	else
    	{
    		printf("open DATABASE success.\n");
    	}
    
    	if((sockfd = socket(AF_INET, SOCK_STREAM,0)) < 0)
    	{
    		perror("fail to socket.\n");
    		return -1;
    	}
    
    	bzero(&serveraddr, sizeof(serveraddr));
    	serveraddr.sin_family = AF_INET;
    	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    	serveraddr.sin_port = htons(atoi(argv[2]));
    
    	if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    	{
    		perror("fail to bind.\n");
    		return -1;
    	}
    
    	// 将套接字设为监听模式
    	if(listen(sockfd, 5) < 0)
    	{
    		printf("fail to listen.\n");
    		return -1;
    	}
    
    	//处理僵尸进程
    	signal(SIGCHLD, SIG_IGN);
    
    	while(1)
    	{
    		if((acceptfd = accept(sockfd, NULL, NULL)) < 0)
    		{
    			perror("fail to accept");
    			return -1;
    		}
    
    		if((pid = fork()) < 0)
    		{
    			perror("fail to fork");
    			return -1;
    		}
    		else if(pid == 0)  // 儿子进程
    		{
    			//处理客户端具体的消息
    			close(sockfd);
    			do_client(acceptfd, db);
    
    		}
    		else  // 父亲进程,用来接受客户端的请求的
    		{
    			close(acceptfd);
    		}
    	}
    	
    	return 0;
    }
    
    
    int do_client(int acceptfd, sqlite3 *db)
    {
    	MSG msg;
    	while(recv(acceptfd, &msg, sizeof(msg), 0) > 0)
    	{
    	  printf("type:%d\n", msg.type);
    	   switch(msg.type)
    	   {
    	  	 case R:
    			 do_register(acceptfd, &msg, db);
    			 break;
    		 case L:
    			 do_login(acceptfd, &msg, db);
    			 break;
    		 case Q:
    			 do_query(acceptfd, &msg, db);
    			 break;
    		 case H:
    			 do_history(acceptfd, &msg, db);
    			 break;
    		 default:
    			 printf("Invalid data msg.\n");
    	   }
    
    	}
    
    	printf("client exit.\n");
    	close(acceptfd);
    	exit(0);
    
    	return 0;
    }
    
    void do_register(int acceptfd, MSG *msg, sqlite3 *db)
    {
    	char * errmsg;
    	char sql[128];
    
    	sprintf(sql, "insert into usr values('%s', %s);", msg->name, msg->data);
    	printf("%s\n", sql);
    
    	if(sqlite3_exec(db,sql, NULL, NULL, &errmsg) != SQLITE_OK)
    	{
    		printf("%s\n", errmsg);
    		strcpy(msg->data, "usr name already exist.");
    	}
    	else
    	{
    		printf("client  register ok!\n");
    		strcpy(msg->data, "OK!");
    	}
    
    	if(send(acceptfd, msg, sizeof(MSG), 0) < 0)
    	{
    		perror("fail to send");
    		return ;
    	}
    
    	return ;
    }
    
    int do_login(int acceptfd, MSG *msg , sqlite3 *db)
    {
    	char sql[128] = {};
    	char *errmsg;
    	int nrow;
    	int ncloumn;
    	char **resultp;
    
    	sprintf(sql, "select * from usr where name = '%s' and pass = '%s';", msg->name, msg->data);
    	printf("%s\n", sql);
    
    	if(sqlite3_get_table(db, sql, &resultp, &nrow, &ncloumn, &errmsg)!= SQLITE_OK)
    	{
    		printf("%s\n", errmsg);
    		return -1;
    	}
    	else
    	{
    		printf("get_table ok!\n");
    	}
    
    	// 查询成功,数据库中拥有此用户
    	if(nrow == 1)
    	{
    		strcpy(msg->data, "OK");
    		send(acceptfd, msg, sizeof(MSG), 0);
    		return 1;
    	}
    
    	if(nrow == 0) // 密码或者用户名错误
    	{
    		strcpy(msg->data,"usr/passwd wrong.");
    		send(acceptfd, msg, sizeof(MSG), 0);}
    
    	return 0;
    }
    
    int do_searchword(int acceptfd, MSG *msg, char word[])
    {
    	FILE * fp;
    	int len = 0;
    	char temp[512] = {};
    	int result;
    	char *p;
    
    
    	//打开文件,读取文件,进行比对
    	
    	if((fp = fopen("dict.txt", "r")) == NULL)
    	{
    		perror("fail to fopen.\n");
    		strcpy(msg->data, "Failed to open dict.txt");
    		send(acceptfd, msg, sizeof(MSG), 0);
    		return -1;
    	}
    
    	//打印出,客户端要查询的单词
    	len = strlen(word);
    	printf("%s , len = %d\n", word, len);
    
    	//读文件,来查询单词
    	while(fgets(temp, 512, fp) != NULL)
    	{
    
    	//	printf("temp:%s\n", temp);
    
    		// abandon  ab
    		result = strncmp(temp,word,len);
    
    		if(result < 0)
    		{
    			continue;
    		}
    		if(result > 0 || ((result == 0) && (temp[len]!=' ')))
    		{
    			break;
    		}
    
    		// 表示找到了,查询的单词
    		p = temp + len; //  abandon   v.akdsf dafsjkj 
    	//	printf("found word:%s\n", p);
    		while(*p == ' ')
    		{
    			p++;
    		}
    
    		// 找到了注释,跳跃过所有的空格
    		strcpy(msg->data, p);
    		printf("found word:%s\n", msg->data);
    
    		// 注释拷贝完毕之后,应该关闭文件
    		fclose(fp);
    		return 1;
    	}
    
    	fclose(fp);
    
    	return 0;
    }
    
    int get_date(char *date)
    {
    	time_t t;
    	struct tm *tp;
    
    	time(&t);
    
    	//进行时间格式转换
    	tp = localtime(&t);
    
    	sprintf(date, "%d-%d-%d %d:%d:%d", tp->tm_year + 1900, tp->tm_mon+1, tp->tm_mday, 
    			tp->tm_hour, tp->tm_min , tp->tm_sec);
    	printf("get date:%s\n", date);
    
    	return 0;
    }
    
    int do_query(int acceptfd, MSG *msg , sqlite3 *db)
    {
    	char word[64];
    	int found = 0;
    	char date[128] = {};
    	char sql[128] = {};
    	char *errmsg;
    
    	//拿出msg结构体中,要查询的单词
    	strcpy(word, msg->data);
    
    	found = do_searchword(acceptfd, msg, word);
    	printf("查询一个单词完毕.\n");
    
    	// 表示找到了单词,那么此时应该将 用户名,时间,单词,插入到历史记录表中去。
    	if(found == 1)
    	{
    		// 需要获取系统时间
    		get_date(date);
    
            sprintf(sql, "insert into record values('%s', '%s', '%s')", msg->name, date, word);
    
    		if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
    		{
    			printf("%s\n", errmsg);
    			return -1;
    		}
    		else
    		{
    			printf("Insert record done.\n");
    		}
    
    	}
    	else  //表示没有找到
    	{
    		strcpy(msg->data, "Not found!");
    	}
    
    	// 将查询的结果,发送给客户端
    	send(acceptfd, msg, sizeof(MSG), 0);
    
    	return 0;
    }
    
    // 得到查询结果,并且需要将历史记录发送给客户端
    int history_callback(void* arg,int f_num,char** f_value,char** f_name)
    {
    	// record  , name  , date  , word 
    	int acceptfd;
    	MSG msg;
    
    	acceptfd = *((int *)arg);
    
    	sprintf(msg.data, "%s , %s", f_value[1], f_value[2]);
    
    	send(acceptfd, &msg, sizeof(MSG), 0);
    
    	return 0;
    }
    
    
    int do_history(int acceptfd, MSG *msg, sqlite3 *db)
    {
    	char sql[128] = {};
    	char *errmsg;
    
    	sprintf(sql, "select * from record where name = '%s'", msg->name);
    
    	//查询数据库
    	if(sqlite3_exec(db, sql, history_callback,(void *)&acceptfd, &errmsg)!= SQLITE_OK)
    	{
    		printf("%s\n", errmsg);
    	}
    	else
    	{
    		printf("Query record done.\n");
    	}
    
    	// 所有的记录查询发送完毕之后,给客户端发出一个结束信息
    	msg->data[0] = '\0';
    
    	send(acceptfd, msg, sizeof(MSG), 0);
    
    	return 0;
    }
    
    

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

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

相关文章

李宏毅机器学习入门笔记——第四节

自注意力机制&#xff08;常见的神经网络结构&#xff09; 上节课我们已经讲述过 CNN 卷积神经网络 和 spatial transformer 网络。这次讲述一个其他的常用神经网络自注意力机制神经网络。 对于输入的变量长度不一的时候&#xff0c;采用frame的形式&#xff0c;进行裁剪设计。…

Astra Pro点云代码

github上找到的python读取点云的代码 import timeimport cv2 as cv import numpy as np import open3d from openni import _openni2 from openni import openni2SAVE_POINTCLOUDS True # 是否保存点云数据def get_rgbd(color_capture, depth_stream, depth_scale1000, dept…

猫头虎分享已解决Bug || 依赖问题:DependencyNotFoundException: Module ‘xyz‘ was not found

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

VirtualBox虚拟机安装 Linux 系统

要想学习各种计算机技术&#xff0c;自然离不开Linux系统。并且目前大多数生产系统都是安装在Linux系统上。常用的Linux系统有 Redhat&#xff0c;Centos&#xff0c;OracleLinux 三种。 三者的区别简单说明如下&#xff1a; Red Hat Enterprise Linux (RHEL): RHEL 是由美国…

QEMU之内存虚拟化

内存虚拟化方案 最直观的方案&#xff0c;将QEMU进程的虚拟地址空间的一部分作为虚拟机的物理地址。但该方案有一个问题&#xff1a; 在物理机上&#xff0c;CPU对内存的访问在保护模式下是通过分段分页实现的&#xff0c;在该模式下&#xff0c;CPU访问时使用的是虚拟地址&am…

算法--时空复杂度分析以及各个数据量对应的可使用的算法(C++;1s内)

这里写目录标题 由数据范围反推算法时间复杂度以及算法内容一级目录二级目录二级目录二级目录 一级目录二级目录二级目录二级目录 一级目录二级目录二级目录二级目录 一级目录二级目录二级目录二级目录 由数据范围反推算法时间复杂度以及算法内容 一级目录 二级目录 二级目录…

龙蜥 Anolis OS8.4 设置IP

1、配置文件路径 /etc/sysconfig/network-scripts/ [rootlocalhost ~]# cd /etc/sysconfig/network-scripts/ [rootlocalhost network-scripts]# ls ifcfg-ens32 进入配置文件路径后&#xff0c;展示。ifcfg-ens32这个不同的服务器不一样&#xff0c;本次虚拟机所对应的是ens3…

labelme 使用笔记

下载和安装 labelme官网地址 在Anaconda环境下 conda create -n labelme python3.6 conda activate labelme # go https://anaconda.org/ to find pkg conda install conda-forge/label/cf202003::labelme安装好了&#xff0c;查看版本和使用帮助 labelme -V labelme -h用l…

【机器人最短路径规划问题(栅格地图)】基于蚁群算法求解

基于蚁群算法求解机器人最短路径规划问题的仿真结果 仿真结果 收敛曲线变化趋势 蚁群算法求解最优解的机器人运动路径 各代蚂蚁求解机器人最短路径的运动轨迹

pandas/geopandas 笔记:逐record的轨迹dataFrame转成逐traj_id的轨迹dataFrame

我们现在有这样的一个dataframe&#xff0c;名字为dart 我们需要这样一个DataFrame&#xff0c;每一行有两列&#xff0c;一列是new_installation_id&#xff0c;表示这个轨迹的id&#xff1b;另一列就是这个new_installation_id的轨迹 dart_new dart[[new_installation_id]]…

使用HTML5画布(Canvas)模拟图层(Layers)效果

使用HTML5画布&#xff08;Canvas&#xff09;模拟图层&#xff08;Layers&#xff09;效果 在图形处理和计算机图形学中&#xff0c;图层&#xff08;Layers&#xff09;是指将图像分成不同的可独立编辑、组合和控制的部分的技术或概念。每个图层都可以包含不同的图形元素、效…

你真的了解C语言的枚举和联合吗~

目录 1. 枚举1.1 枚举类型的定义1.2 枚举的优点1.3 枚举的使用 2. 联合&#xff08;共用体&#xff09;2.1 联合类型的定义2.2 联合的特点2.3 使用联合体判断当前机器的大小端2.4 联合大小的计算 1. 枚举 枚举顾名思义就是一一列举。 把可能的取值一一列举。 比如我们现实生活…

华为云磁盘挂载

华为云磁盘挂载 磁盘挂载情况 fdisk -l 2. 查看当前分区情况 df -h 3.给新硬盘添加新分区 fdisk /dev/vdb 4.分区完成&#xff0c;查询所有设备的文件系统类型 blkid 发现新分区并没有文件系统类型&#xff08;type为文件系统具体类型&#xff0c;有ext3,ext4,xfs,iso9660等…

如何一步一步地优化LVGL的丝滑度

经过一番周折将LVGL移植到了STM32F407单片机上&#xff0c;底层驱动的LCD是st7789&#xff0c;移植时的条件和环境如下&#xff1a; ●LVGL用的是单缓冲&#xff0c;一次刷新10行&#xff1b; ●刷新函数用的是最原始的一个一个打点的方式&#xff1b; ●ST7789底层发送数据用的…

寒假开学在即,怎么寄行李才能便宜省钱呢?

在度过了一个充实愉快的假期之后&#xff0c;小伙伴们就要踏上新的征程了&#xff0c;来面对新学期的到来&#xff0c;可是&#xff0c;面对这么多不知道怎么安排的行李可就把人给愁死了&#xff0c;如果通过驿站寄行李的话&#xff0c;又要花费一大笔快递费了&#xff0c;可是…

IAudioManager.cpp源码解读

IAudioManager.cpp源码如下&#xff1a; 源码路径&#xff1a;https://cs.android.com/android/platform/superproject/main//main:frameworks/native/services/audiomanager/IAudioManager.cpp;drc84410fbd18148d422d3581201c67f1a72a6658c4;l147?hlzh-cn /** Copyright (C)…

基于springboot实现线上阅读系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现线上阅读系统演示 摘要 随着社会发展速度的愈来愈快&#xff0c;以及社会压力变化的越来越快速&#xff0c;致使很多人采取各种不同的方法进行解压。大多数人的稀释压力的方法&#xff0c;是捧一本书籍&#xff0c;心情地让自己沉浸在情节里面&#xff0c;以…

【Linux】TCP应用与相关API守护进程

需要云服务器等云产品来学习Linux的同学可以移步/–>腾讯云<–/官网&#xff0c;轻量型云服务器低至112元/年&#xff0c;优惠多多。&#xff08;联系我有折扣哦&#xff09; 文章目录 1. 相关使用接口2. 代码实现2.1 日志组件2.2 Server端2.3 Client端2.3 bug解决 3. 守…

动态规划|【斐波那契数列模型 】|面试题08.01三步问题

目录 题目 思路 普通思路 动态规划思路 1.状态表示 2.状态转移方程 3.初始化 4.填表顺序 5.返回值 代码 空间优化 题目 题目链接 面试题 08.01. 三步问题https://leetcode.cn/problems/three-steps-problem-lcci/ 三步问题。有个小孩正在上楼梯&#xff0c;楼梯有n…

【JAVA日志】关于日志系统的架构讨论

目录 1.日志系统概述 2.环境搭建 3.应用如何推日志到MQ 4.logstash如何去MQ中取日志 5.如何兼顾分布式链路追踪 1.日志系统概述 关于日志系统&#xff0c;其要支撑的核心能力无非是日志的存储以及查看&#xff0c;最好的查看方式当然是实现可视化。目前市面上有成熟的解决…