本地配置同网段,优先从低IP传输数据
比如,对端是192.168.12.77/16
本地配置192.168.1.7/16与192.168.12.177/16两个IP,无论是否配置到同一网卡,在通信时,本地只会从192.168.1.7/16发包
在不做任何特殊网络转发配置前提下,必然ping不通对端
本地配置同网段,优先从低IP传输数据
比如,对端是192.168.12.77/16
本地配置192.168.1.7/16与192.168.12.177/16两个IP,无论是否配置到同一网卡,在通信时,本地只会从192.168.1.7/16发包
在不做任何特殊网络转发配置前提下,必然ping不通对端
https://docs.ceph.com/en/latest/rbd/rbd-openstack/
rbd已被引入到Linux内核,在内核视角看,RBD镜像是由远程分布式存储提供的一块线性块设备,实现细节都被内核模块
rbd.ko隐藏RBD 镜像的数据最终就是其所在池里的一批对象
内核挂载过程:
获取rbd镜像列表
1 | rbd list |
使用 rbd 将镜像名称映射到内核模块。
指定镜像名称、池名称和用户名。如果 RBD 内核模块尚未加载,rbd 将代您加载。
1 | sudo rbd device map {pool-name}/{image-name} --id {user-name} |
如果您使用 cephx 身份验证,则还必须指定一个密钥。密钥可能来自密钥环或包含密钥的文件。
1 | sudo rbd device map rbd/myimage --id admin --keyring /path/to/keyring |
查看使用 rbd 映射到内核模块的块设备映像
1 | rbd device list |
解除映射
1 | sudo rbd device unmap /dev/rbd/{poolname}/{imagename} |
内核模块 rbd.ko 通过librados连接到集群,把指定的 pool/image 打开
模块为该image申请一个 rbd_device 结构体的示例,并注册一个块设备
make_request_fn 入口内核只缓存页数据,元数据有用户态的rbd或librbd维护,内核只在需要时通过 rbd_header 对象读取最小必要信息
Ceph 块设备最常见的用例是向虚拟机提供块设备映像。例如,用户可以创建具有理想配置的操作系统和任何相关软件的“golden”映像。然后,用户创建该镜像的快照(snapshot)。最后,用户克隆该快照(可能多次)。制作快照的写时复制(copy-on-write)能力意味着 Ceph 可以快速地向虚拟机提供块设备映像,因为客户端不必在每次启动新的虚拟机时下载整个映像
Ceph 块设备连接到 QEMU 虚拟机,如下图所示

略
有关 QEMU 文档,请参阅 QEMU Manual.。有关安装详细信息,请参阅 Installation。
QEMU 命令行要求您指定 Ceph 池和镜像名称。您还可以指定快照。
QEMU 将假定 Ceph 配置位于默认位置(例如,/etc/ceph/$cluster.conf),并且您以默认 client.admin 用户身份执行命令
1 | qemu-img {command} [options] rbd:{pool-name}/{image-name}[@snapshot-name][:option1=value1][:option2=value2...] |
可以指定另一个 Ceph 配置文件路径或其他用户。例如,指定 id 和 conf 选项可能如下所示:
1 | qemu-img {command} [options] rbd:glance-pool/maipo:id=glance:conf=/etc/ceph/ceph.conf |
您应该将管理员用户的密钥或使用 :id={user} 选项指定的另一个用户的密钥放在密钥环文件中,该文件存储在默认路径中(即 /etc/ceph 或具有适当文件所有权和权限的本地目录)。
详情请参阅User Management - User。
1 | qemu-img create -f raw rbd:{pool-name}/{image-name} {size} |
raw 数据格式实际上是 RBD 中 format 选项唯一合理的选项。从技术上讲,您可以使用其他 QEMU 支持的格式(例如 qcow2 或 vmdk)。但这样做会增加额外的开销,并且在启用缓存(见下文)时也会导致卷对于虚拟机实时迁移不安全。
1 | qemu-img resize rbd:{pool-name}/{image-name} {size} |
1 | qemu-img info rbd:{pool-name}/{image-name} |
QEMU 可以将块设备从主机传递到客户机,但从 QEMU 0.15 开始,无需将images 映射为主机上的块设备。相反,QEMU 会直接通过 librbd 将镜像附加为虚拟块设备。
您可以使用 qemu-img 将现有的虚拟机镜像转换为 Ceph 块设备镜像。
1 | # 将现有的qcow2镜像转换为ceph块镜像 |
要运行从该image启动的虚拟机,您可以运行:
1 | qemu -m 1024 -drive format=raw,file=rbd:data/squeeze |
RBD 缓存可以显著提升性能。
自 QEMU 1.2 起,QEMU 的缓存选项控制 librbd 缓存:
1 | qemu -m 1024 -drive format=rbd,file=rbd:data/squeeze,cache=writeback |
如果您有旧版本的 QEMU,则可以将 librbd 缓存配置(与任何 Ceph 配置选项一样)设置为“文件”参数的一部分:
1 | qemu -m 1024 -drive format=raw,file=rbd:data/squeeze:rbd_cache=true,cache=writeback |
如果设置了 rbd_cache=true,则必须设置 cache=writeback,否则可能会丢失数据。因为,不设置writeback,QEMU 将不会向 librbd 发送刷新请求。如果 QEMU 在此配置下不干净退出,则 rbd 上的文件系统可能会损坏。
从 Ceph 0.46 版和 QEMU 1.1 版开始,Ceph 块设备支持discard操作。即客户机可以发送 TRIM 请求,让 Ceph 块设备回收未使用的空间。
为了使客户机可以使用此功能,必须为块设备显式启用此功能。为此,必须指定与驱动器关联的 discard_granularity:
1 | qemu -m 1024 -drive format=raw,file=rbd:data/squeeze,id=drive1,if=none \ |
如果使用 libvirt,请使用 virsh edit 编辑 libvirt 域的配置文件,以添加 xmlns:qemu 值。然后,添加 qemu:commandline 块作为该域的子块。以下示例显示如何将两个 qemu id= 的设备设置为不同的 discard_granularity 值。
1 | <domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'> |
QEMU 的缓存选项对应于以下 Ceph RBD 缓存设置。
Writeback:
1 | rbd_cache = true |
Writethrough:
1 | rbd_cache = true |
None:
1 | rbd_cache = false |
QEMU 的缓存设置覆盖 Ceph 的缓存设置(包括在 Ceph 配置文件中明确设置的设置)。
libvrit 库在虚拟机管理程序接口和调用这些API的应用层软件之间,创建了一个抽象层
借助libvirt,开发者和系统管理员可以专注于通用管理框架、API和通用Shell接口,可用于多种虚拟机管理程序:
可将 Ceph 块设备与可与 libvirt 交互的软件配合使用。下面的堆栈图说明了 libvirt 和 QEMU 如何通过 librbd 使用 Ceph 块设备。

最常见的 libvirt 用例涉及向 OpenStack、OpenNebula 或 CloudStack 等云解决方案提供 Ceph 块设备。还可以将 Ceph 块设备与 libvirt、virsh 和 libvirt API 结合使用。
要创建使用 Ceph 块设备的虚拟机,请使用以下部分中的步骤。
使用Ceph块设备
SSH客户端 可连接运行了 SSH服务器 的远程主机
SSH协议:专为远程登录会话和其他网络服务提供安全性的协议。建立在应用层和传输层基础上的安全协议
通过SSH协议防止远程登录过程的信息泄露
| 命令 | 对应英文 | 作用 |
| :————————————: | :—————: | :—————- |
| ssh 用户名@ip | secure shell | 远程bash |
| scp 用户名@ip:文件名或录用 | secure copy | 远程复制文件 |
1 | ssh [-p port] user@remote Ip |
Windows若想ssh连接到Linux,则需要安装相应的客户端
Linux默认采用的编码格式是UTF-8,Windows默认采用的编码格式是ANSI(GB2312、GBK),所以需要设置一下相应编码:

对所有传输的数据进行加密,防止DNS与IP欺骗
| 选项 | 含义 |
| :—: | :—————————————————————————————- |
| -r | 给出的源文件是目录文件,将递归赋值该目下的所有子目录和文件,目标文件必须为一目录名 |
| -P | 若远程SSH服务器的端口不是22,需要用大写字母 P 来指定端口 |
1 | # 将本地当前目录下的 01.py 复制到远程家目录下的 Desktop/01.py |

在本地 .ssh 文件夹下登录远程服务器,保存SSH相关的配置信息
生成本地当前用户公钥
ssh-keygen 即可生成SSH公钥,全部回车id_rsa.pub :即本机公钥id_rsa :即本机私钥将公钥上传到服务器
ssh-copy-id -p port user@remote ,让远程服务器记住公钥
工作原理:非对称加密算法
使用 公钥 加密的数据,需要私钥解密
使用 私钥 加密的数据,需要公钥解密
输入 ssh username@ipaddress 很繁琐
在 ~/.ssh/config 里追加以下内容
1 | Host newName |
保存之后,就可使用别名登录

http.tar.gz 是用C语言编程的一个简单版webserver
1 | # 将http.tar.gz拷贝到服务端,并解压 |

修改输出内容
1 | root@tzj-virtual-machine:/opt/http# vim hello.c |

中文乱码 :Linux默认采用的编码格式是UTF-8,浏览器显示默认采用的编码格式是GBK

直接修改浏览器编码
在输头部信息中指定编码格式,
1 | root@tzj-virtual-machine:/opt/http# vim ceshi.c |

安卓调试工具(Android Debug Bridge,ADB),PC端与安卓手机的通道,管理手机设备或模拟器的状态
1 | # 将abd.exe添加到环境变量 |
将Android设备通过USB连接到PC
1 | # 将Android设备通过USB连接到PC,安装相应驱动 |

1 | C:\Users\tian_zj>adb devices |
[TOC]
服务端配置
1 | [root@node205]# yum install -y targetcli |
1 | rpm -i scst-4.19.90-52.23.v2207.gfb08.ky10.aarch64-3.7.0-1.ky10.src.rpm |
1 | iscsiadm -m node -T iqn.2025-09.local:ramdisk.target -p 10.152.1.205 --logout |
关闭防火墙
关闭SELinux
关闭NetworkManager
1 | systemctl stop NetworkManager |
ssh配置
1 | ``` |
1 | yum install -y nfs-utils rpcbind |
1 | mount -t nfs 192.168.100.191:/home/nfs /mnt -o proto=tcp -o nolock |
https://luyuhuang.tech/2020/12/09/kcp.html
https://zhuanlan.zhihu.com/p/10153049382
调用 ikcp_send 发送数据,创建报文段实例,加入 snd_queue 中
ikcp_update 会在合适的时刻调用 ikcp_flush。
在 ikcp_flush 中:
snd_queue 移入 发送缓冲snd_buf 中snd_buf 中满足条件的报文段发送出去:snd_buf 中,从未被发送过的报文对端收到报文后,icp_input 会被调用,解析收到的数据
所有的报文都有 una 字段,根据una将相应的报文标为送达
如果是 ACK 报文, 就将相应的报文标记为已送达
如果是数据报文, 就将它放入 rcv_buf, 然后将 rcv_buf 中顺序正确的报文移入 rcv_queue
接着将相关信息插入 ACK 列表, 在稍后的 ikcp_flush 调用中会发送相应的 ACK;
如果是窗口探测报文, 就标记 “需要发送窗口通知”. 在稍后的 ikcp_flush 调用中会发送窗口通知报文;
包括窗口通知报文在内的所有报文都有 wnd 字段, 据此更新 rmt_wnd;(对端窗口大小)
根据 ACK 失序情况决定快速重传
计算cwnd
调用 ikcp_recv 接收数据,从 rcv_queue 中读取数据
对端发送ACK报文给发送方
发送方在 ikcp_input 中解析到ACK,将相应报文标为已送达
队列中的报文都是有序的(发送队列与接收队列都是)
缓冲区与滑动窗口相关
snd_buf 是一个链表,链表中的报文段编号始终递增
两个指针
snd_una 指针指向第一个已经发出但未收到ACK的报文段
小于该una的报文,均已收到ACK,
在发送缓冲区 snd_buf 中,已经收到ACK的报文段会被删除
snd_nxt 指向缓冲区的下一个插入位置
发送窗口 cwnd 的起始位置是 snd_una 指针指向的位置,大小为 cwnd
flush时,会从 snd_queue 中取出报文,并从 snd_nxt 指向的位置开始插入 snd_buf ,然后 snd_nxt 向右移动
当 snd_nxt - snd_una >= cwnd 时,不允许新的报文加入 snd_buf ,
发送窗口中未确认到达的报文何时重传,取决于这个报文的RTO,报文在一个RTO时间内仍未被确认,则重传
rx_rto在 KCP 的实现中, rcv_buf 是一个报文段链表,链表中报文段编号始终递增。当收到新的报文时,会根据编号插入到链表中相应的位置中。顺序正确的报文会从链表头部弹出, 移动到 rcv_queue 中。
nrcv_que 为接收队列的长度,rcv_buf 中在 rcv_nxt 之前的报文段都将移入 rcv_queue 中nrcv_que 个报文,读取后删除该报文,接收窗口向右滑动指针 rcv_nxt :指向编号最小的未收到报文应插入的位置,即已收到最小编号的报文之前
rcv_buf 对应的位置。然后,检查 rcv_nxt 是否可向右移动,仅当报文顺序正确且连续才能移动KCP会通知发送方,剩余的接收窗口大小 rcv_wnd-nrcv_queue
如果收到的数据报文编号大于 rcv_nxt+rcv_wnd ,远超接收窗口,这个报文会被丢弃
报文中,会携带报文发送者的接收缓冲区 rcv_buf 中还未收到的最小报文编号——rcv_nxt
rcv_nxt具有ACK+UNA的双重确认机制
t1:发送una=1的报文,该报文被加入发送缓冲区
snd_una 指向1,snd_nxt 指向2t2-t3:依次发送2至3号报文,snd_nxt 依次后移
t4:对端收到3号报文,放入 rcv_buf 中,发送3号报文的ACK报文
t5:对端收到2号报文,放入rcv_buf 中,发送2号报文的ACK报文
t7:收到2号报文的ACK报文,将2号报文标为已送达,送 snd_buf 中移除
ACK报文的una为1,标识rcv_nxt指向1,对端还未收到una=1的报文,因此发送方知道需要重发
snd_una 仍为1
t8:重发1号报文,
t9:1号报文到达对端,对端发送1号报文的ACK
t10:发送方收到1号报文的ACK报文,将1号报文标为发送成功,同时una=4,所以也将3号报文标为已收到,snd_una 移动到4
每个报文的RTO会随着重传次数的增加而增加
初始值保存在 rx_rto 中,
KCP计算RTO初始值的方法是TCP的标准方法,在RFC 6298中
计算所依赖的两个中间量,$srtt$ (对一段时间内RTT均值的估计)与 $rttvar$ (对 RTT 平均偏差的估计)
每收到一个ACK都能得到一个RTT,然后更新中间变量,得出RTO
其中,$g=\frac{1}{8}$ 与 $h=\frac{1}{4}$ 为常数,$interval$ 为计时器粒度(调用 ikcp_flush 的时间间隔)
RTO至少为一个时钟间隔
初始值,测得第一个RTT时对srtt和rttvar初始化
发送方不能无限制地发送数据,否则必然会导致网络与缓冲区无法容纳发送的数据,导致大量丢包
拥塞控制,实际上是动态计算发送窗口大小的过程
慢启动:理想情况下, 发送数据的速率正好等于网路的容量, 然而我们并不知道网路的实际容量. 我们的做法是先将 cwnd 设为 1, 随后平均每经过一个 RTT 时间 cwnd 都翻一倍, 以探测到何时会出现丢包. 这种方法便是慢启动.
在慢启动中 cwnd 会指数增长, 这个过程显然不能一直持续下去. 当 cwnd 超过一个阈值之后, 便会进入拥塞避免阶段. 这个阈值我们称为慢启动阈值(Slow Start Threshold), 通常记作 ssthresh
在拥塞避免阶段, cwnd 会近似于线性增长.
随着 cwnd 的增长, 网路容量会被逐步填满
网络容量填满之后, 就会不可避免地出现丢包. 这就意味着我们的发送速率过大, cwnd 应该减小了. KCP 称之为丢包退让。此时有两种策略:
一种是将 ssthresh 置为当前 cwnd 的一半, 然后 cwnd 置为 1, 重新执行慢启动
将 ssthresh 置为当前 cwnd 的一半, 但 cwnd 置为比 ssthresh 稍高的值, 然后进入快速恢复阶段
快速恢复阶段中, cwnd 会以与拥塞避免相同的方式线性增长
KCP 采用的策略是, 如果发生超时重传, 就进入慢启动; 如果发生快速重传, 就进入快速恢复.
rmt_wnd.慢启动时, 平均每经过一个 RTT 时间 cwnd 都翻一倍
在拥塞避免阶段, 差不多要等当前发送窗口发的报文都确认到达之后, cwnd 才增加 1.
在 KCP 的实现中, 拥塞避免阶段的 cwnd 仍然会在每收到一个 ACK 的时候增长, 只不过不是增加 1, 而是增加零点几.
KCP 的做法是维护一个中间变量 incr,计算方式为诶
当 incr 累计增加的值超过一个 mss 时, cwnd 增加 1

实际上相当于每收到一个 ACK, cwnd 都自增 $\frac{1}{cwnd}$ ,即要等当前发送窗口发的报文都确认到达之后, cwnd 才增加 1.
去掉 $\frac{mss}{16}$ 即为TCP拥塞避免阶段cwnd的增长方式
| 特性类别 | 协议 | 描述 |
|---|---|---|
| 拥塞控制机制 | TCP | 固定算法(慢启动、拥塞避免等),保守的调整策略(指数和线性增长) |
| KCP | 灵活算法,动态调整策略,快速调整窗口大小 | |
| 重传机制的延迟 | TCP | 固定重传间隔(RTO),多次确认触发重传,需要主动开启选择性重传(SACK) |
| KCP | 快速重传,选择性重传,减少重传延迟 | |
| 流量控制 | TCP | 固定流量控制(依赖接收窗口和发送窗口),通用性设计 |
| KCP | 自适应流量控制,应用层反馈调整发送窗口和重传策略 | |
| 应用场景 | TCP | 广泛应用于各种网络环境,标准化要求高 |
| KCP | 优化特定场景(如高丢包率和高延迟网络),灵活实现 |
TCP 作为一种成熟且广泛使用的传输协议,在设计上注重可靠性和通用性,因此在拥塞控制和流量控制方面相对保守,以确保在各种网络条件下都能稳定运行。
拥塞控制机制
重传机制的延迟
应用场景的差异
TCP
广泛应用:TCP 设计用于广泛的网络环境,包括稳定的有线网络和不稳定的无线网络,因此其机制必须足够通用和保守,保证在各种情况下的可靠性。
标准化要求:作为互联网的基础协议,TCP 的各项机制经过严格标准化,任何修改都需要广泛测试和验证,以确保不会影响现有网络的稳定性。
KCP
特定优化:KCP 设计初衷是优化特定场景下的传输性能,特别是高丢包率和高延迟网络,因此在设计上更加灵活,能够根据实时网络状况进行调整。
灵活实现:KCP 可以根据具体应用需求进行优化,例如在实时通信和在线游戏等场景中,灵活的流量控制和快速重传机制显著提升了传输效率。
可以利用这个函数构建高效的事件驱动网络程序
1 | // 示例:管理多个RUDP连接的调度器 |
[TOC]
[TOC]