UDP实现文件的发送、UDP实现全双工的聊天、TCP通信协议
我要成为嵌入式高手之3月7日Linux高编第十七天!!
————————————————————————————
回顾
重要程序
1、UDP实现文件的发送
发端:
#include "head.h"int main(void)
{int sockfd = 0;struct sockaddr_in recvaddr;ssize_t nsize = 0;int ret = 0;int fd = 0;char pathname[1024] = {0};char tmpbuff[1024] = {0};ssize_t nret;sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sockfd){perror("fail to socket");return -1;}recvaddr.sin_family = AF_INET;recvaddr.sin_port = htons(33333);recvaddr.sin_addr.s_addr = inet_addr("192.168.1.167");memset(pathname, 0, sizeof(pathname));gets(pathname);nsize = sendto(sockfd, pathname, strlen(pathname), 0, (struct sockaddr *)&recvaddr, sizeof(recvaddr));if (nsize == -1){perror("fail to sendto");return -1;}fd = open(pathname, O_RDONLY);if (fd == -1){perror("fail to open");return -1;}printf("pathname: %s\n", pathname);while (1){usleep(10);nret = read(fd, tmpbuff, sizeof(tmpbuff));nsize = sendto(sockfd, tmpbuff, nret, 0, (struct sockaddr *)&recvaddr, sizeof(recvaddr));if (nsize == -1){perror("fail to sendto tmpbuff");return -1;}if (nret <= 0){break;}}sprintf(tmpbuff, "|__quit__|");nsize = sendto(sockfd, tmpbuff, strlen(tmpbuff)+1, 0, (struct sockaddr *)&recvaddr, sizeof(recvaddr));if (nsize == -1){perror("fail to sendto tmpbuff");return -1;}printf("成功发送 %s 文件\n", pathname);close(fd);close(sockfd);return 0;
}
收端:
#include "head.h"int main(void)
{int ret = 0;int sockfd = 0;char tmpbuff[4096] = {0};struct sockaddr_in recvaddr;ssize_t nsize = 0;struct sockaddr_in sendaddr;socklen_t len = sizeof(sendaddr);sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd == -1){perror("fail to socket");return -1;}recvaddr.sin_family = AF_INET;recvaddr.sin_port = htons(33333);recvaddr.sin_addr.s_addr = inet_addr("192.168.1.167");ret = bind(sockfd, (struct sockaddr *)&recvaddr, sizeof(recvaddr));if (ret == -1){perror("fail to bind");return -1;}nsize = recvfrom(sockfd, tmpbuff, sizeof(tmpbuff), 0, (struct sockaddr *)&sendaddr, &len);if (nsize == -1){perror("fail to recvfrom");return -1;}// printf("%s:%d>>接受到%ld个字节:%s\n",inet_ntoa(sendaddr.sin_addr),ntohs(sendaddr.sin_port),nsize,tmpbuff);int fd = 0;char ch[1024]= {0};fd = open(tmpbuff,O_WRONLY | O_CREAT | O_TRUNC, 0664);if (fd == -1){perror("fail to fopen");return -1;}while(nsize != 0){memset(ch, 0, sizeof(ch));nsize = recvfrom(sockfd, ch, sizeof(ch), 0, (struct sockaddr *)&sendaddr, &len);if (nsize == -1){perror("fail to recvfrom");return -1;}if (!strcmp(ch,"1")){break;}write(fd,ch,nsize);printf("写入%ld字节\n",nsize);}printf("接受完成!\n");close(fd);close(sockfd);return 0;
}
2、UDP实现全双工的聊天
发送端
#include "head.h"char name[32] = {0};
int sockfd = 0;
struct sockaddr_in recvaddr;
pthread_t tid_send;
pthread_t tid_recv;void *sendfun(void *arg)
{ssize_t nsize = 0;struct msgbuf sendmsg;while(1){memset(&sendmsg, 0, sizeof(sendmsg));sendmsg.type = MSG_TYPE_CHAT;sprintf(sendmsg.name, "%s", name);gets(sendmsg.text); if (!strcmp(sendmsg.text, ".quit")){sendmsg.type = MSG_TYPE_END;}nsize = sendto(sockfd, &sendmsg, sizeof(sendmsg), 0, (struct sockaddr *)&recvaddr, sizeof(recvaddr));if (nsize == -1){perror("fail to sendto");return NULL;}if (sendmsg.type == MSG_TYPE_END){break;}}pthread_cancel(tid_recv);return NULL;
}void *recvfun(void *arg)
{struct msgbuf recvmsg;ssize_t nsize = 0;while(1){memset(&recvmsg, 0, sizeof(recvmsg));nsize = recvfrom(sockfd, &recvmsg, sizeof(recvmsg), 0, NULL, NULL);if (-1 == nsize){perror("fail to recvfrom");return NULL;}if (recvmsg.type == MSG_TYPE_CHAT){printf("%s(%s:%d)>%s\n", recvmsg.name, RECV_ADDR, RECV_PORT, recvmsg.text);}else if (recvmsg.type == MSG_TYPE_END){break;}}pthread_cancel(tid_send);return NULL;
}int main(void)
{ssize_t nsize = 0;struct msgbuf sendmsg;/*设置接收方信息*/recvaddr.sin_family = AF_INET;recvaddr.sin_port = htons(RECV_PORT);recvaddr.sin_addr.s_addr = inet_addr(RECV_ADDR);/*输入昵称*/printf("请输入您的昵称:\n");gets(name);/*创建套接字*/sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd == -1){perror("fail to sockfd");return -1;}/*设置要发送的信息*/memset(&sendmsg, 0, sizeof(sendmsg));sendmsg.type = MSG_TYPE_START;sprintf(sendmsg.name, "%s", name);/*发送信息*/nsize = sendto(sockfd, &sendmsg, sizeof(sendmsg), 0, (struct sockaddr *)&recvaddr, sizeof(recvaddr));if (nsize == -1){perror("fail to sendto");return -1;}pthread_create(&tid_send, NULL, sendfun, NULL);pthread_create(&tid_recv, NULL, recvfun, NULL);pthread_join(tid_send, NULL);pthread_join(tid_recv, NULL);close(sockfd);return 0;
}
接收端
#include "head.h"char name[32] = {0};
int sockfd = 0;
struct sockaddr_in recvaddr;
struct sockaddr_in sendaddr;
pthread_t tid_send;
pthread_t tid_recv;void *sendfun(void *arg)
{ssize_t nsize = 0;struct msgbuf sendmsg;while(1){memset(&sendmsg, 0, sizeof(sendmsg));sendmsg.type = MSG_TYPE_CHAT;sprintf(sendmsg.name, "%s", name);gets(sendmsg.text); if (!strcmp(sendmsg.text, ".quit")){sendmsg.type = MSG_TYPE_END;}nsize = sendto(sockfd, &sendmsg, sizeof(sendmsg), 0, (struct sockaddr *)&sendaddr, sizeof(sendaddr));if (nsize == -1){perror("fail to sendto");return NULL;}if (sendmsg.type == MSG_TYPE_END){break;}}pthread_cancel(tid_recv);return NULL;
}void *recvfun(void *arg)
{struct msgbuf recvmsg;ssize_t nsize = 0;while(1){memset(&recvmsg, 0, sizeof(recvmsg));nsize = recvfrom(sockfd, &recvmsg, sizeof(recvmsg), 0, NULL, NULL);if (-1 == nsize){perror("fail to recvfrom");return NULL;}if (recvmsg.type == MSG_TYPE_CHAT){printf("%s(%s:%d)>%s\n", recvmsg.name, inet_ntoa(sendaddr.sin_addr), ntohs(sendaddr.sin_port), recvmsg.text);}else if (recvmsg.type == MSG_TYPE_END){break;}}pthread_cancel(tid_send);return NULL;
}int main(void)
{char tmpbuff[1024] = {0};struct msgbuf recvmsg;int ret = 0;socklen_t addrlen = sizeof(sendaddr);/*设置接收方信息*/recvaddr.sin_family = AF_INET;recvaddr.sin_port = htons(RECV_PORT);recvaddr.sin_addr.s_addr = inet_addr(RECV_ADDR);/*输入昵称*/printf("请输入您的昵称:\n");gets(name);/*创建套接字*/sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd == -1){perror("fail to sockfd");return -1;}ret = bind(sockfd, (struct sockaddr *)&recvaddr, sizeof(recvaddr));if (-1 == ret){perror("fail to bind");return -1;}/*设置要发送的信息*/memset(&recvmsg, 0, sizeof(recvmsg));/*接收信息*/ret = recvfrom(sockfd, &recvmsg, sizeof(recvmsg), 0, (struct sockaddr *)&sendaddr, &addrlen);if (ret == -1){perror("fail to recvfrom");return -1;}pthread_create(&tid_send, NULL, sendfun, NULL);pthread_create(&tid_recv, NULL, recvfun, NULL);pthread_join(tid_recv, NULL);pthread_join(tid_send, NULL);close(sockfd);return 0;
}
head.h
#ifndef _HEAD_H
#define _HEAD_H#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <time.h>
#include <pwd.h>
#include <stdlib.h>
#include <grp.h>
#include <sys/wait.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <arpa/inet.h>struct msgbuf
{int type; //消息类型char name[32]; //昵称char text[32]; //消息
};#define MSG_TYPE_START 100 //开始聊天消息类型
#define MSG_TYPE_END 200 //退出聊天消息类型
#define MSG_TYPE_CHAT 300 //聊天消息类型
#define RECV_ADDR "192.168.1.172" //接收端地址
#define RECV_PORT 50000 //接收端端口#endif
学习笔记
TCP通信
TCP发端(客户端)
socket -> connect -> send / recv-> close
TCP收端(服务端)
socket -> bind -> listen -> accept -> send / recv -> close
1、connect
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:发起链接请求
参数:
sockfd:套接字的描述符
addr:目的地址存放空间首地址
addrlen:IP地址的大小
返回值:成功0,失败-1;
2、send
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
功能:发送数据
参数:
sockfd:套接字描述符
buf:发送数据空间的首地址
len:发送数据的长度
flags:属性默认为0
返回值:
成功返回实际发送字节数
失败返回-1;
3、recv
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
功能:接收数据
参数:
sockfd:套接字描述符
buf:存放数据空间首地址
len:最大接收数据的长度
flags:属性默认0
返回值:
成功返回实际接收字节数,失败返回-1;
若对方退出,返回0
客户端:
#include "head.h"int main(void)
{int ret = 0;int sockfd = 0;struct sockaddr_in serveaddr;char tmpbuff[1024] = {0};ssize_t nsize = 0;sockfd = socket(AF_INET, SOCK_STREAM, 0);if (-1 == sockfd){perror("fail to socket");return -1;}serveaddr.sin_family = AF_INET;serveaddr.sin_port = htons(50000);serveaddr.sin_addr.s_addr = inet_addr("192.168.1.101");ret = connect(sockfd, (struct sockaddr *)&serveaddr, sizeof(serveaddr));if (ret == -1){perror("fail to connect");return -1;}gets(tmpbuff);nsize = send(sockfd, tmpbuff, strlen(tmpbuff), 0);if (nsize == -1){perror("fail to send");return -1;}memset(tmpbuff, 0, sizeof(tmpbuff));nsize = recv(sockfd, tmpbuff, sizeof(tmpbuff), 0);if (nsize == -1){perror("fail to recv");return -1;}printf("RECV: %s\n", tmpbuff);close(sockfd);return 0;
}

4、listen
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);
功能:监听客户端发送的链接请求,不会阻塞
参数:
sockfd:套接字描述符
backlog:允许等待的尚未被处理的三次握手请求的最大个数
返回值:成功0,失败-1;
5、accept
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能:
处理等待链接队列中的第一个链接请求,若没有人发送链接请求会阻塞等待,直到有连接请求
参数:
sockfd:套接字描述符
addr:存放IP地址的空间首地址
addrlen:存放IP地址大小空间首地址
返回值:
成功返回一个新的文件描述符
失败返回-1;
服务端:
#include "head.h"int main(void)
{int sockfd = 0;struct sockaddr_in serveaddr;int ret = 0;int confd = 0;ssize_t nsize = 0;char tmpbuff[1024] = {0};serveaddr.sin_family = AF_INET;serveaddr.sin_port = htons(50000);serveaddr.sin_addr.s_addr = INADDR_ANY;sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd == -1){perror("fail to socket");return -1;}ret = bind(sockfd, (struct sockaddr *)&serveaddr, sizeof(serveaddr));if (ret == -1){perror("fail to bind");return -1;}ret = listen(sockfd, 10);if (-1 == ret){perror("fail to listen");return -1;}confd = accept(sockfd, NULL, NULL);if (-1 == confd){perror("fail to accept");return -1;}nsize = recv(confd, tmpbuff, sizeof(tmpbuff), 0);if (-1 == nsize){perror("fail to recv");return -1;}printf("RECV: %s\n", tmpbuff);memset(tmpbuff, 0, sizeof(tmpbuff));gets(tmpbuff);nsize = send(confd, tmpbuff, strlen(tmpbuff), 0);if (-1 == nsize){perror("fail to send");return -1;}close(confd);close(sockfd);return 0;
}
相关文章:
UDP实现文件的发送、UDP实现全双工的聊天、TCP通信协议
我要成为嵌入式高手之3月7日Linux高编第十七天!! ———————————————————————————— 回顾 重要程序 1、UDP实现文件的发送 发端: #include "head.h"int main(void) {int sockfd 0;struct sockaddr_i…...
Yocto - Project Quick Build
欢迎光临! 这篇简短的文档将向您介绍使用 Yocto 项目构建典型镜像的过程。本文还介绍了如何为特定硬件配置构建。您将使用 Yocto Project 构建一个名为 Poky 的参考嵌入式操作系统。 Welcome! This short document steps you through the process for a typical i…...
深入探讨C++中的可变参数列表(Variadic Templates)
文章目录 导言可变参数列表的基本用法使用std::initializer_list应用场景 导言 在C编程中,处理可变数量参数的能力是一种非常有用的功能。通过可变参数列表,你可以编写更加通用和灵活的函数,从而提高代码的可读性和重用性。本文将详细介绍C中…...
MS2548 国产自动方向控制、半双工 RS-485 收发器 替代MAX13487
MS2548 国产自动方向控制、半双工 RS-485 收发器 替代MAX13487 北京冠宇铭通科技有限公司 肖小姐 产品简述 MS2548 是一个 5V 供电、半双工 RS-485 收发器。 芯片具有自动换向控制功能,可用于隔离485 端口,驱动器输入与使能信号一起配合控制芯片的状态&…...
数据库大师之路:Oracle在线学习平台全指南!
介绍数据库是由甲骨文公司开发的一款关系数据库管理系统(RDBMS),在数据库领域具有领先地位,并且以其系统可移植性而闻名。以下是对Oracle数据库的详细介绍: 市场地位:Oracle数据库是目前世界上流行的关系数…...
如何在Windows系统部署Jellyfin Server并实现公网访问内网影音文件
文章目录 1. 前言2. Jellyfin服务网站搭建2.1. Jellyfin下载和安装2.2. Jellyfin网页测试 3.本地网页发布3.1 cpolar的安装和注册3.2 Cpolar云端设置3.3 Cpolar本地设置 4.公网访问测试5. 结语 1. 前言 随着移动智能设备的普及,各种各样的使用需求也被开发出来&…...
华为北向网管NCE开发教程(3)CORBA协议开发
华为北向网管NCE开发教程(1)闭坑选接口协议 华为北向网管NCE开发教程(2)REST接口开发 华为北向网管NCE开发教程(3)CORBA协议开发 如果你真的还有选择的余地,能用REST,尽量用REST&…...
【算法训练营】最长公共子序列,倒水问题,奶牛吃草(Python实现)
最长公共子序列 时间限制:1 sec 空间限制:256 MB 问题描述 给定两个 1 到 n 的排列 A,B (即长度为 n 的序列,其中 [1,n] 之间的所有数都出现了恰好一次)。 求它们的最长公共子序列长度。 输入格式 第一行一个整数 n &a…...
Armadillo:矩阵类、向量类、Cube类和泛型类
文章目录 矩阵类、向量类、Cube类和泛型类Mat<type>matcx_matCol<type>veccx_vecRow<type>rowveccx_rowvecCube<type>cubecx_cubefield<object_type>SpMat<type>sp_matsp_cx_mat运算符: − * % / ! < > <…...
【守护健康】小脑萎缩患者必备营养指南
当生活给予我们挑战,我们选择用科学和关爱予以回应。面对小脑萎缩这一难题,正确的营养补充不仅是一剂强心针,更是患者康复之路上的坚实伙伴。今天,让我们一起了解那些能够助力小脑萎缩患者的神奇维生素! 1. 维生素B群…...
lvs集群中NAT模式
群集的含义 由多台主机构成,但对外表现为一个整体,只提供一个访问入口,相当于一台大型的计算机。 横向发展:放更多的服务器,有调度分配的问题。 垂直发展:升级单机的硬件设备,提高单个服务器自身功能。 …...
FPGA——三速自适应以太网设计(2)GMII与RGMII接口
FPGA——以太网设计(2)GMII与RGMII 基础知识(1)GMII(2)RGMII(3)IDDR GMII设计转RGMII接口跨时钟传输模块 基础知识 (1)GMII GMII:发送端时钟由MAC端提供 下…...
【校园导航小程序】2.0版本 静态/云开发项目 升级日志
演示视频 【校园导航小程序】2.0版本 静态/云开发项目 演示 首页 重做了首页,界面更加高效和美观 校园指南页 新增了 “校园指南” 功能,可以搜索和浏览校园生活指南 地图页 ①弃用路线规划插件,改用SDK开发包。可以无阻通过审核并发布…...
深入揭秘Lucene:全面解析其原理与应用场景(二)
本系列文章简介: 本系列文章将深入揭秘Lucene,全面解析其原理与应用场景。我们将从Lucene的基本概念和核心组件开始,逐步介绍Lucene的索引原理、搜索算法以及性能优化策略。通过阅读本文,读者将会对Lucene的工作原理有更深入的了解…...
Java中synchronized关键字、ReentrantLock、volatile关键字是如何实现线程同步的。
在Java中,synchronized关键字、ReentrantLock和volatile关键字这三个是编程中常用于实现线程同步的机制,下面结合代码详细说明一下这三个关键字的用法。 1. synchronized关键字: synchronized关键字是Java语言提供的内置锁机制,…...
路由拦截器
路由拦截可以分为几种不同的类型,每种类型都有其特定的作用和适用场景。以下是常见的几种路由拦截类型及其用途: 身份验证拦截器: 作用: 检查用户是否已经登录或具有有效的身份认证,并根据认证状态决定是否允许用户访问…...
Springboot+vue的物业管理系统(有报告)。Javaee项目,springboot vue前后端分离项目。
演示视频: Springbootvue的物业管理系统(有报告)。Javaee项目,springboot vue前后端分离项目。 项目介绍: 本文设计了一个基于Springbootvue的物业管理系统,采用M(model)Vÿ…...
STM32的启动流程分析 和 一些底层控制的原理
阅读引言: 阅读本文之后, 你将对单片机, 甚至是嵌入式系统, 或者是传统的PC机系统的启动流程有一个大致的了解, 本文更加偏向于单片机的启动流程分析。 目录 一、基础知识 1.STM32系列的微控制器(mcu&…...
C#面:几种注释类型
三种常见的注释类型:单行注释、多行注释和 XML 注释。 单行注释: 以双斜线 // 开头,用于在一行中注释单个语句或代码块。单行注释会被编译器忽略,不会对程序的执行产生任何影响。 例如: // 这是一个单行注释 int a…...
#onenet网络请求http(GET,POST)
参考博文: POST: https://blog.csdn.net/qq_43350239/article/details/104361153 POST请求(用串口助手测试): POST /devices/1105985351/datapoints HTTP/1.1 api-key:AdbrV5kCRsKsRCfjboYOCVcF9FY Host:api.heclouds.com Con…...
告别全局update!手把手教你写一个安全的UVM寄存器批量更新函数
告别全局update!手把手教你写一个安全的UVM寄存器批量更新函数 在SoC验证环境中,寄存器配置是最基础却最频繁的操作之一。每次看到验证工程师手动逐个调用set()和update()时,我总会想起自己刚入行时那段"复制粘贴到怀疑人生"的日子…...
Android Automotive(八) 实战调试工具链全解析
1. Android Automotive调试工具链全景概览 开发Android Automotive应用就像组装一辆汽车,你需要各种专用工具来调试不同部件。在实际项目中,我发现很多开发者面对车载系统调试时容易陷入两个极端:要么只会用ADB基础命令,要么被复杂…...
别再乱用QStatusBar了!PyQt5状态栏addPermanentWidget和addWidget混用踩坑实录
PyQt5状态栏深度避坑指南:永久控件与临时消息的黄金分割法则 在桌面应用开发中,状态栏作为用户界面的"信息中枢",承担着版本展示、操作反馈、状态提示等多重职责。许多PyQt5开发者都遇到过这样的困境:精心设计的版本号标…...
Cesium 3D可视化实战:给你的地理围栏加上‘跑马灯’特效(基于MaterialProperty自定义材质)
Cesium 3D可视化实战:地理围栏动态光效的深度实现 在三维地理信息系统中,动态视觉效果往往能带来更直观的数据感知体验。想象一下,当监控区域边界以流动光带形式呈现,不仅美观更能突出警示作用——这正是我们今天要探讨的Cesium高…...
游戏模组管理革命:XXMI Launcher如何让6款热门游戏一键安装模组?
游戏模组管理革命:XXMI Launcher如何让6款热门游戏一键安装模组? 【免费下载链接】XXMI-Launcher Modding platform for GI, HSR, WW and ZZZ 项目地址: https://gitcode.com/gh_mirrors/xx/XXMI-Launcher 还在为不同游戏安装多个模组管理器而烦恼…...
Dify金融问答合规配置实战指南:从0到1通过银保监AI问答备案的7个关键配置项
第一章:Dify金融问答合规配置的监管背景与备案逻辑近年来,金融领域人工智能应用加速落地,监管框架同步趋严。《生成式人工智能服务管理暂行办法》《金融行业大模型应用安全指引(试行)》及《银行保险机构数据安全管理办…...
Rhino 7 + Grasshopper 新手避坑指南:这5个隐藏设置不打开,效率直接减半
Rhino 7 Grasshopper 新手避坑指南:这5个隐藏设置不打开,效率直接减半 刚接触Rhino和Grasshopper的新手设计师们,往往会被默认界面中那些看似无害实则拖累效率的"隐形陷阱"困扰。当你在深夜赶项目时,是否经历过反复切…...
从LED到激光器:一文搞懂半导体光电子器件的核心原理与设计差异
从LED到激光器:半导体光电子器件的核心原理与设计差异解析 当我们在夜晚点亮一盏LED台灯,或是使用光纤网络高速下载文件时,背后是两类截然不同却又紧密相关的半导体光电器件在发挥作用。LED(发光二极管)和半导体激光器…...
Maxwell Simplorer Simulink 永磁同步电机矢量控制联合仿真
maxwell simplorer simulink 永磁同步电机矢量控制联合仿真,电机为分数槽绕组,使用pi控制SVPWM调制,修改文件路径后可使用,软件版本matlab 2017b, Maxwell electronics 2021b 共包含两个文件, Maxwell和Simplorer联合仿…...
Claude+Obsidian 5小时速成新领域
别只抄工具!Claude+Obsidian 5小时速成新领域 目录 别只抄工具!Claude+Obsidian 5小时速成新领域 一、一步步复现:原作者的5小时知识框架搭建法 步骤1:理解核心问题 步骤2:列出已知条件 步骤3:逐步推理(以"本体论"为例) 步骤4:原方法的核心结论 二、深度反…...
