自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(178)
  • 收藏
  • 关注

原创 Protobuf 的快速使用(四)

搭建 Httplib 库Protobuf 还常⽤于通讯协议、服务端数据交换场景。那么在这个⽰例中,我们将实现⼀个⽹络版本的通讯录,模拟实现客⼾端与服务端的交互,通过 Protobuf 来实现各端之间的协议序列化。需求如下:客⼾端可以选择对通讯录进⾏以下操作:新增⼀个联系⼈删除⼀个联系⼈查询通讯录列表查询⼀个联系⼈的详细信息为了将代码简化,我们只具体实现新增联系人的功能,服务端则相应提供增删查能⼒,并需要持久化通讯录(为了将代码简化,

2025-04-05 11:37:24 64

原创 Protobuf 的快速使用(三)

升级通讯录 2.1 版本在(三)中,我们将新增联系⼈属性,共包括:姓名、年龄、性别、电话信息、地址、其他联系⽅式、备注。enum 类型语法⽀持我们定义枚举类型并使⽤。在.proto⽂件中枚举类型的书写规范为:枚举类型名称:使⽤驼峰命名法,⾸字⺟⼤写。例如: MyEnum常量值名称:全⼤写字⺟,多个字⺟之间⽤ _连接。例如:要注意枚举类型的定义有以下⼏种规则:0 值常量必须存在,且要作为第⼀个元素。这是为了与 proto2 的语义兼容:第⼀个元素作为默认值,且值为 0。

2025-03-31 15:26:44 806

原创 Protobuf 的快速使用(二)

创建通讯录 2.0 版本这个部分会对通讯录进⾏多次升级,使⽤ 2.x 表⽰升级的版本,最终将会升级如下内容:不再打印联系⼈的序列化结果,⽽是将通讯录序列化后并写⼊⽂件中。从⽂件中将通讯录解析出来,并进⾏打印。新增联系⼈属性,共包括:姓名、年龄、性别、电话信息、地址、其他联系⽅式、备注。字段规则消息的字段可以⽤下⾯⼏种规则来修饰:singular :消息中可以包含该字段零次或⼀次(不超过⼀次)。proto3 语法中,字段默认使⽤该规则。

2025-03-29 13:47:48 376

原创 Protobuf 的快速上手(一)

序列化概念序列化概念序列化:把对象转换为字节序列的过程称为对象的序列化。反序列化:把字节序列恢复为对象的过程称为对象的反序列化。什么情况下需要序列化存储数据:当你想把的内存中的对象状态保存到⼀个⽂件中或者存到数据库中时。⽹络传输:⽹络直接传输数据,但是⽆法直接传输对象,所以要在传输前序列化,传输完成后反序列化成对象。例如我们之前学习过 socket 编程中发送与接收数据。我们可以通过等方式实现序列化,接下来,我们来介绍protobuf。

2025-03-29 12:03:29 969

原创 Linux网络 多路复用epoll && Reactor 反应堆模式

epoll 是 Linux 内核为处理大批量文件描述符而设计的 I/O 事件通知机制,它提供了一种高效的方式,能够同时监控多个文件描述符的事件(如读、写等),并且回调那些就绪的文件描述符。相比于传统的 select 和 poll,epoll 在处理大量并发连接时具有更高的效率,特别适用于高并发服务器的开发。功能:创建一个 epoll 实例,返回一个文件描述符。size 参数已弃用,只需要大于 0 即可。成功:返回epoll文件描述符。失败:返回-1,并设置errno。

2025-03-19 15:21:50 647

原创 C++11 异步操作

std::future 和 std::shared_future 都是C++11标准库中的⼀个模板类,它表⽰⼀个异步操作的结果。当我们在多线程编程中使⽤异步任务时,其可以帮助我们在需要的时候获取任务的执⾏结果。其的⼀个重要特性是能够阻塞当前线程,直到异步操作完成,从⽽确保我们在获取结果时不会遇到未完成的操作。应用场景异步任务: 当我们需要在后台执⾏⼀些耗时操作时,如⽹络请求或计算密集型任务等,std::future可以⽤来表⽰这些异步任务的结果。通过将任务与主线程分离,我们可以实现任务的并⾏处理,从。

2025-03-17 15:34:29 644

原创 Muduo库的简介与使用

初始化:创建EventLoop,根据需要创建TcpServer或TcpClient,设置回调函数。事件循环:调用EventLoop的 loop() 方法,进入事件循环,等待事件发生。事件检测与分发:通过I/O多路复用检测事件,将事件分发给对应的Channel。网络事件处理连接建立/断开:触发连接回调,处理连接相关逻辑。数据收发:触发消息回调,处理数据接收和发送逻辑。多线程协作(如适用):在多线程环境下,主线程和子线程协同处理不同的连接事件。程序退出。

2025-03-14 19:25:04 1092

原创 Linux网络 五种 IO 模型

在计算机网络和操作系统中,I/O(输入/输出)模型描述了程序如何与外部设备(如磁盘、网络接口等)进行数据交互,不同的I/O模型在处理I/O操作时的行为和性能表现各有不同。以下是五种常见的I/O模型及其特点。

2025-03-11 23:49:10 656

原创 Linux网络 多路复用 select && poll

select 函数是 Unix 和类 Unix 系统中用于实现多路复用输入/输出(I/O)的一种系统调用。它允许程序同时监视多个文件描述符(file descriptors),以确定它们是否准备好进行读取、写入或异常条件检测,程序会停在 select等待,直到被监视的文件描述符有一个或多个发生了状态改变。它同时检查多个文件描述符是否准备好进行 I/O 操作,从而避免程序阻塞在单个 I/O 操作上,这使得程序能够同时处理多个 I/O 事件,例如同时监听多个网络连接或文件输入。

2025-03-06 18:24:07 832

原创 Linux网络 NAT、代理服务、内网穿透

IPv4协议中存在IP地址数量不充足的问题,而 NAT 技术是当前解决IP地址不够用的主要手段, 是路由器的一个重要功能。NAT 能够将私有 IP 对外通信时转为全局 IP,也就是就是一种将私有 IP 和全局 IP 相互转化的技术方法。这可以让很多学校,家庭,公司内部采用每个终端设置私有 IP,而在路由器或必要的服务器上设置全局 IP。全局 IP 要求唯一,但是私有 IP 不需要,在不同的局域网中出现相同的私有 IP 是完全不影响的。

2025-03-03 19:30:45 1051

原创 Linux网络 DNS

TCP/IP中使用IP地址和端口号来确定网络上的一台主机的一个程序但是IP地址不方便记忆。于是人们发明了一种叫主机名的东西,是一个字符串并且使用hosts文件来描述主机名和 IP地址的关系。最初通过互连网信息中心(SRI-NIC)来管理这个hosts文件的如果一个新计算机要接入网络,或者某个计算机IP变更都需要到信息中心申请变更 hosts文件其他计算机也需要定期下载更新新版本的 hosts文件才能正确上网这样就太麻烦了于是产生了DNS系统。

2025-03-02 14:29:22 978

原创 Linux网络 TCP全连接队列与tcpdump抓包

在 Linux 网络中,TCP 全连接队列(也称为 Accept 队列)是一个重要的概念,用于管理已经完成三次握手,即已经处于 established 状态但尚未被应用程序通过 accept( ) 函数处理的 TCP 连接,避免因为应用程序处理不及时而导致连接丢失。

2025-03-01 16:13:07 840

原创 Linux网络 数据链路层

在Linux网络中,数据链路层位于物理层之上,网络层之下,其主要职责是将网络层的IP数据包封装成帧,并通过物理链路发送到目标设备。以太网是当前应用最广泛的局域网技术,和以太网并列的还有令牌环网,无线LAN等。MTU(Maximum Transmission Unit,最大传输单元)是指网络中能够传输的单个数据包的最大字节数,它是数据链路层的概念,用于限制链路层一次性转发的数据帧的最大尺寸。所以在以太网中,设备需要知道目标设备的MAC地址才能传输数据,而ARP协议通过广播机制实现IP地址到MAC地址的映射。

2025-02-28 22:20:24 883

原创 Redis 分布式锁

我们引⼊⼀组 Redis 节点. 其中每⼀组 Redis 节点都包含⼀个主节点和若⼲从节点. 并且组和组之间存储的数据都是⼀致的, 相互之间是 "备份" 关系. 加锁的时候, 按照⼀定的顺序, 写多个 master 节点. 在写锁的时候需要设定操作的 "超时时间". ⽐如 50ms. 即如果 setnx 操作超过了 50ms 还没有成功, 就视为加锁失败.同理, 释放锁的时候, 也需要把所有节点都进⾏解锁操作. (即使是之前超时的节点, 也要尝试解锁, 尽量保证逻辑严密).

2025-02-27 15:20:29 988

原创 Redis 集群

表⽰的. 表⽰ 16384 (16k) 个 slots, 需要的位图⼤⼩是 2KB. 如果给定的 slots 数更多了, ⽐如 65536 个了, 此时就需要消耗更多的空间, 8 KB 位图表⽰了. 8 KB, 对于内存来说不算什么, 但是在频繁的⽹络⼼跳包中, 还是⼀个不⼩的开销的.围绕这个问题, 业界有三种⽐较主流的实现⽅式.缺点: ⼀旦需要进⾏扩容, N 改变了, 原有的映射规则被破坏, 就需要让节点之间的数据相互传输, 重新排列, 以满⾜新的映射规则. 此时需要搬运的数据量是⽐较多的, 开销较⼤.

2025-02-26 22:06:24 1052 2

原创 Redis 缓存

因此就需要提前把热点数据准备好, 直接写⼊到 Redis 中. 使 Redis 可以尽快为 MySQL 撑起保护伞. 热点数据可以基于之前介绍的统计的⽅式⽣成即可. 这份热点数据不⼀定⾮得那么 "准确", 只要能帮助 MySQL 抵挡⼤部分请求即可. 随着程序运⾏的推移, 缓存的热点数据会逐渐⾃动调整, 来更适应当前情况.如何让数据库能够承担更⼤的并发量呢?针对要查询的参数进⾏严格的合法性校验. ⽐如要查询的 key 是⽤⼾的⼿机号, 那么就需要校验当前 key 是否满⾜⼀个合法的⼿机号的格式.

2025-02-26 22:06:15 815

原创 Linux网络 网络层

4位版本号(version):指定IP协议的版本对于IPv4来说就是4.4位头部长度头部的长度是多少个32bit,也就是4 字节,4bit表示最大的数字是15,因此IP头部最大长度是60字节8 位服务类型位优先权字段已经弃用), 4位TOS字段和 1 位保留字段必须置为0). 4位TOS分别表示最小延时最大吞吐量最高可靠性, 最小成本.这四者相互冲突只能选择一个对于ssh/telnet这样的应用程序。

2025-02-24 17:33:37 1397

原创 C++ 互斥锁的使用

std::mutex 是C++标准库中用于线程同步的互斥锁机制,主要用于保护共享资源,避免多个线程同时访问导致的竞态条件。lock:阻塞当前线程,直到获取锁。unlock:释放锁,允许其他线程获取锁。try_lock:尝试获取锁,如果锁已被占用则立即返回。

2025-02-22 17:07:50 880

原创 C++ thread库的使用

在C++中,std::thread 是C++11引入的线程库的一部分,用于创建和管理线程。它提供了一个简单而强大的方式来实现多线程编程。thread库底层是对各个系统的线程库进⾏封装,如Linux下的pthread库和Windows下Thread库等,所以C++11 thread库的第⼀个特点是可以跨平台,第⼆个特点是Linux和Windows下提供的线程库都是⾯向过程的,C++11 thread是库⾯向对象的,并且融合了⼀些C++11语⾔特点,如右值引⽤的移动语义,可变模板参数等,⽤起来会更好⽤⼀些。

2025-02-22 15:18:15 424

原创 Linux System V - 消息队列与责任链模式

消息队列是一种以消息为单位的进程间通信机制,允许一个或多个进程向队列中发送消息,同时允许一个或多个进程从队列中接收消息。:发送方和接收方不需要同时运行,消息会存储在队列中。:支持消息的优先级排序。:每条消息都有一个类型,可以根据类型选择性地接收。:即使发送方或接收方意外退出,消息仍然保留在队列中。内核结构在其中是 System V IPC(进程间通信)机制中用于描述 IPC 对象(如消息队列、信号量集或共享内存段)权限和所有者信息的数据结构。

2025-02-21 00:16:44 1049

原创 Redis 客户端C++使用

安装 redis-plus-plus安装 redis-plus-plus在C++中使用Redis,通常需要借助第三方库来实现与Redis服务器的交互。目前比较流行的库有和。是基于实现的,是⼀个 C 语⾔实现的 redis 客⼾端,因此需要先安装直接使⽤包管理器安装即可。UbuntuCentos之后通过源码编译安装构建成功后, 会在。

2025-02-19 20:54:40 369

原创 Redis 哨兵

Redis 的主从复制模式下,⼀旦主节点由于故障不能提供服务,需要⼈⼯进⾏主从切换,同时⼤量的客⼾端需要被通知切换到新的主节点上,对于上了⼀定规模的应⽤来说,这种⽅案是⽆法接受的,于是 Redis 从 2.8 开始提供了 Redis Sentinel(哨兵)来解决这个问题。Redis Sentinel 是⼀个分布式架构,其包含若⼲个 Sentinel 节点和 Redis 数据节点,每个Sentinel 节点会对数据节点和其余 Sentinel 节点进⾏监控,当它发现节点不可达时,会对节点做下线表⽰。

2025-02-18 17:43:48 880

原创 Redis 主从复制

在分布式系统中为了解决单点问题,通常会把数据复制多个副本部署到其他服务器,满⾜故障恢复和负载均衡等需求。Redis 也是如此,它提供了复制的功能,实现了相同数据的多个 Redis 副本,通过一个主节点(master)和多个从节点(replica)来实现数据的冗余备份和读写分离。以下为主从复制的作用:从节点可以复制主节点的数据,当主节点发生故障时,从节点可以接管服务,保证系统的高可用性。:主节点负责写操作,从节点负责读操作,通过分摊读请求,可以提高系统的读写性能。

2025-02-17 17:22:36 935

原创 Redis 事物

Redis 事务是一种机制,允许用户将多个命令打包在一起,然后一次性、顺序地执行这些命令。它类似于关系型数据库中的事务,但 Redis 的事务更轻量级且功能相对简单。以下是关于Redis 事务和 MySQL 事务的区别:弱化的原⼦性: redis 没有 "回滚机制". 只能做到这些操作 "批量执⾏",不能做到 "⼀个失败就恢复到初始状态".不保证⼀致性: 不涉及 "约束",也没有回滚。MySQL 的⼀致性体现的是运⾏事务前和运⾏后 , 结果都是合理有效的, 不会出现中间⾮法状态.

2025-02-16 13:43:25 363

原创 Redis 持久化

RDBRedis 提供了多种持久化机制,主要包括 RDB(Redis Database)、AOF(Append Only File)以及混合持久化机制,持久化功能有效地避免因进程退出造成数据丢失问题,当下次重启时利⽤之前持久化的⽂件即可实现数据恢复。RDBRDB 持久化过程为,在指定的时间间隔内 Redis 将 内存中的数据保存到磁盘上的一个 RDB 文件(通常是 .rdb 文件)。这个文件是一个二进制文件,包含了当前 Redis 数据库的完整数据快照。

2025-02-15 22:58:54 764

原创 Redis 数据类型 Zset 有序集合

有序集合相对于字符串、列表、哈希、集合来说会有⼀些陌⽣。它保留了集合不能有重复成员的特点,但与集合不同的是,有序集合中的每个元素都有⼀个唯⼀的浮点类型的分数(score)与之关联,着使得有序集合中的元素是可以维护有序性的,但这个有序不是⽤下标作为排序依据⽽是⽤这个分数。有序集合中的元素是不能重复的,但分数允许重复。类⽐于⼀次考试之后,每个⼈⼀定有⼀个唯⼀的分数,但分数允许相同。列表、集合、有序集合三者的异同点数据结构是否允许重复元素是否有序有序依据应⽤场景列表是是索引下标。

2025-02-14 16:14:00 672

原创 Redis 数据类型 Set 集合

集合类型也是保存多个字符串类型的元素的,但和列表类型不同的是,集合中元素之间是⽆序的并且元素不允许重复,如下图所⽰。⼀个集合中最多可以存储 2^32 - 1 个元素。Redis 除了⽀持集合内的增删查改操作,同时还⽀持多个集合取交集、并集、差集,合理地使⽤好集合类型,能在实际开发中解决很多问题。:集合中的元素是无序的,不能通过索引访问元素。:集合中的元素是唯一的,不允许重复。:集合内部使用哈希表实现,因此添加、删除和查找操作的时间复杂度接近于 O(1)。

2025-02-12 12:13:41 732

原创 Redis 数据类型 List 列表

列表类型是⽤来存储多个有序的字符串,如下图所⽰,a、b、c、d、e 五个元素从左到右组成了⼀个有序的列表,列表中的每个字符串称为元素(element),⼀个列表最多可以存储 2^32 - 1个元素。在 Redis 中,可以对列表两端插⼊(push)和弹出(pop),还可以获取指定范围的元素列表、获取指定索引下标的元素等。列表是⼀种⽐较灵活的数据结构,它可以充当栈和队列的⻆⾊,在实际开发上有很多应⽤场景。:List 是一个有序的字符串集合,元素的顺序由插入顺序决定。

2025-02-11 22:43:46 1079

原创 Redis 数据类型 Hash 哈希

在 Redis 中,哈希类型是指值本⾝⼜是⼀个键值对结构,形如 key = "key",value = { { field1, value1 }, ..., {fieldN, valueN } },Redis String 和 Hash 类型⼆者的关系可以⽤下图来表⽰。哈希类型是稀疏的,⽽关系型数据库是完全结构化的,例如哈希类型每个键可以有不同的 field,⽽。下图为关系型数据表记录的两条⽤⼾信息,⽤⼾的属性表现为表的列,每条⽤⼾信息表现为⾏。:键和值都是二进制安全的,可以包含任何数据,包括二进制数据。

2025-02-11 21:41:31 709

原创 Redis 数据类型 String 字符串

⼀个分布式 Web 服务将⽤⼾的 Session 信息(例如⽤⼾登录信息)保存在各⾃的服务器中,但这样会造成⼀个问题:出于负载均衡的考虑,分布式服务会将⽤⼾的访问请求均衡到不同的服务器上,并且通常⽆法保证⽤⼾每次请求都会被均衡到同⼀台服务器上,这样当⽤⼾刷新⼀次访问是可能会发现需要重新登录,这个问题是⽤⼾⽆法容忍的。由于 Redis 内部存储字符串完全是按照⼆进制流的形式保存的,所以 Redis 是不处理字符集编码问题的,客⼾端传⼊的命令中使⽤的是什么字符集编码,就存储什么字符集编码。

2025-02-10 16:44:46 848

原创 Redis 基本全局命令和单线程架构

KEYSRedis 提供了许多命令来管理和操作数据,以下是一些常用的全局命令,这些命令可以帮助你管理和监控 Redis 服务器的状态、配置和数据。KEYS语法:时间复杂度:O(N)返回所有满⾜样式(pattern)的 key。⽀持如下统配样式。h?llo ,其中?可以替代任一字符,可以匹配如hellohallo和hxlloh*llo ,其中 * 可以替代任一段字符,可以匹配如 hllo 和heeeello。

2025-02-10 13:46:44 998

原创 Linux 传输层协议 UDP 和 TCP

16位UDP长度表示整个数据报(UDP首部+UDP数据的最大长度如果校验和出错,就会直接丢弃UDP传输的过程类似于寄信无连接:知道对端的IP和端口号就直接进行传输不需要建立连接不可靠:没有确认机制没有重传机制;如果因为网络故障该段无法发到对方,UDP 协议层也不会给应用层返回任何错误信息面向数据报: 不能够灵活的控制读写数据的次数和数量,即应用层交给 UDP 多长的报文,UDP原样发送,既不会拆分,也不会合并UDP 没有真正意义上的调用sendto。

2025-02-04 22:33:11 1281

原创 Linux网络 HTTPS 协议原理

MD5、SHA1、SHA256、SHA512 等,算法把无限的映射成有限,因此可能会有碰撞(两个不同的信息,算出的摘要相同,但是概率非常低),摘要特征:和加密算法的区别是,摘要严格意义不是加密,因为没有解密,只不过从摘要很难反推原信息,通常用来进行数据对比。如果服务器用它的私钥加密数据传给浏览器,那么浏览器用公钥可以解密它,而这个公钥是一开始从服务器通过明文传输给浏览器的,若这个公钥被中间人劫持到了,那他也能用该公钥解密服务器传来的信息了。对数据加密,再发送,只能由服务器解密,因为只有服务器有私钥 S'

2025-02-02 16:38:05 1009 1

原创 Linux网络 HTTP cookie 与 session

当用户第一次访问网站时,会话 Cookie:在浏览器关闭时失效;持久 Cookie:带有明确的过期日期或持续时间,可以跨多个浏览器会话存在。如果 cookie是一个持久性的cookie,那么它其实就是浏览器相关的,特定目录下的一个文件。但直接查看这些文件可能会看到乱码或无法读取的内容,因为cookie文件通常以二进制或sqlite格式存储。一般我们查看,直接在浏览器对应的选项中直接查看即可。HTTP 存在一个报头选项:Set-Cookie可以用来进行给浏览器设置。

2025-02-01 21:11:47 1046

原创 Linux网络 应用层协议 HTTP

无论是HTTP 301还是HTTP 302重定向,都需要依赖Location选项来指定资源的新位置。这个 Location选项是一个标准的HTTP响应头部,用于告诉浏览器应该将请求重定向到哪个新的 URL地址。

2025-01-27 19:41:08 1856

原创 Linux网络 进程间关系与守护进程

每一个进程除了有一个进程ID(PID)之外还属于一个进程组。

2025-01-20 23:25:34 884

原创 Linux网络 序列化与反序列化

序列化(Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。

2025-01-20 12:49:06 776

原创 Linux网络 TCP socket

socket()功能:创建一个新的套接字。参数说明:domain:指定协议族,如AF_INET(IPv4)或AF_INET6(IPv6)。type:指定套接字类型,如(TCP)或SOCK_DGRAM(UDP),由于使用TCP协议,所以使用SOCK_STREAM,表示面向流的传输协议。protocol:指定协议类型,通常为0,表示使用默认协议。返回值:成功时返回一个新的套接字描述符,失败时返回-1。该套接字描述符本质与文件描述符一样,应用程序可以像读写文件一样用read/write。

2025-01-17 17:31:58 1341

原创 MySQL 架构

求需要处理时,先从线程池中查找是否有可⽤的线程,如果没有则新创建⼀个,当连接结束时,如果线程池没有满,则把当前线程放⼊线程池,主要的作⽤是提⾼线程的复⽤,减少创建线程造成的系统开销从⽽提⾼效率。通过语法校验的SQL语句将进⼊查询优化器处理阶段,查询优化器会将解析树转化为查询计划,⼀般情况下,⼀条查询可以有很多种执⾏⽅案,查询优化器会根据执⾏计划匹配合适的索引,选择最。连接管理器线程在接收到每个客⼾端连接后,把请求转发到真正的执⾏线程,每个请求都对应⼀个执⾏线程,该线程处理连接的⾝份验证和具体请求。

2025-01-09 13:26:48 1026

原创 MySQL 存储引擎

MyISAM 的三种存储格式中,静态格式是最简单和最安全的(最不容易损坏),同时也是最快的磁盘格式,因为每⾏的⻓度固定,根据索引中的⾏号乘以⾏⻓度就可以计算出⾏位置,此外,每次读取固定数量的⾏也⾮常的⾼效。存储引擎维护了⼀个⾃⼰的缓冲池,访问数据时在内存中缓存表和索引数据,对于经常使⽤的数据直接从内存中处理,⼤幅提升了效率。使⽤MyISAM存储引擎的表占⽤空间很⼩,但是由于使⽤表级锁定,所以限制了读/写操作的性能,通常⽤于中⼩型的Web应⽤和数据仓库配置中的只读或主要是读的场景。

2025-01-09 13:25:29 1068

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除