rust_tokio_kcp

Crates.iorust_tokio_kcp
lib.rsrust_tokio_kcp
version0.2.4
created_at2026-01-23 08:55:47.814764+00
updated_at2026-01-25 14:06:21.724468+00
descriptionA kcp implementation for tokio
homepage
repositoryhttps://github.com/hushuoyouli/rust_tokio_kcp
max_upload_size
id2063879
size332,330
(hushuoyouli)

documentation

README

rust_tokio_kcp

一个基于 Tokio 的 KCP 协议实现,提供高性能、低延迟的可靠 UDP 传输。

简介

rust_tokio_kcp 是一个在 Tokio 异步运行时上实现的 KCP (KCP Protocol) 库。KCP 是一个快速可靠协议,可以比 TCP 浪费 10%-20% 的带宽的代价,换取平均延迟降低 30%-40%,且最大延迟降低三倍的传输效果。

本项目完全兼容 kcp-go,支持加密、FEC 前向纠错等高级特性。

特性

  • 基于 Tokio 异步运行时 - 充分利用异步 I/O 性能
  • 完全兼容 kcp-go - 可与 Go 版本的 KCP 实现互通
  • 多种加密算法支持 - AES、TEA、XTEA、Blowfish、CAST5、3DES、Twofish、Salsa20、SM4 等
  • FEC 前向纠错 - 支持 Reed-Solomon 纠错码,提高丢包环境下的传输可靠性
  • 类似标准库的 API - 提供 KcpStreamKcpListener 接口,类似 TcpStreamTcpListener
  • 高度可配置 - 支持自定义 KCP 参数、加密方式、FEC 配置等
  • ⭐ Custom Mode - 每个连接独立配置 Salt - 每个连接(conv)可以配置独立的 salt,实现连接级别的安全隔离,适用于多租户、不同安全级别等场景
  • 高性能设计 - 共享 UdpSocket,减少资源开销

安装

Cargo.toml 中添加依赖:

[dependencies]
rust_tokio_kcp = "0.2.0"
tokio = { version = "1.37", features = ["net", "sync", "rt", "macros", "time"] }

启用加密功能

默认启用 AES 加密。你也可以启用其他加密算法:

[dependencies]
rust_tokio_kcp = { version = "0.2.0", features = ["aes", "tea", "xtea"] }

支持的加密特性:

  • aes (默认) - AES-128/192/256 加密
  • tea - TEA 加密
  • xtea - XTEA 加密
  • simple_xor - 简单 XOR 加密(需要 pbkdf2 和 sha1)
  • blowfish - Blowfish 加密
  • cast5 - CAST5 加密
  • triple_des - 3DES 加密
  • twofish - Twofish 加密
  • salsa20 - Salsa20 流加密
  • sm4 - SM4 国密算法

快速开始

基本服务器示例

use rust_tokio_kcp::{KcpConfig, KcpListener};
use tokio::io::{AsyncReadExt, AsyncWriteExt};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = KcpConfig::default();
    let mut listener = KcpListener::bind(config, "127.0.0.1:8080").await?;
    
    loop {
        let (mut stream, _) = listener.accept().await?;
        
        tokio::spawn(async move {
            let mut buf = [0; 1024];
            loop {
                let n = stream.read(&mut buf).await.unwrap();
                if n == 0 {
                    break;
                }
                stream.write_all(&buf[..n]).await.unwrap();
            }
        });
    }
}

基本客户端示例

use rust_tokio_kcp::{KcpConfig, KcpStream};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use std::net::SocketAddr;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = KcpConfig::default();
    let server_addr = "127.0.0.1:8080".parse::<SocketAddr>()?;
    let mut stream = KcpStream::connect(&config, server_addr).await?;
    
    stream.write_all(b"Hello, KCP!").await?;
    
    let mut buf = [0; 1024];
    let n = stream.read(&mut buf).await?;
    println!("Received: {}", String::from_utf8_lossy(&buf[..n]));
    
    Ok(())
}

高级配置

启用加密和 FEC

use rust_tokio_kcp::{KcpConfig, KcpNoDelayConfig, Aes128BlockCrypt};
use std::sync::Arc;

// 创建 AES-128 加密器
let key = b"1234567890123456"; // 16 字节密钥
let crypt = Arc::new(Aes128BlockCrypt::new(key)?) as Arc<dyn rust_tokio_kcp::BlockCrypt>;

// 配置 KCP
let mut config = KcpConfig::default();
config.nodelay = KcpNoDelayConfig::fastest(); // 启用最快模式
config.wnd_size = (1024, 1024); // 增大窗口大小
config.flush_write = true; // 立即刷新写入
config.flush_acks_input = true; // 立即刷新 ACK
config.fec_data_shards = 10; // 启用 FEC:10 个数据分片
config.fec_parity_shards = 3; // 3 个校验分片
config.stream = true; // 启用 stream 模式
config.crypt = Some(crypt); // 启用 AES-128 加密

Custom Mode(自定义模式)- ⭐ 每个连接独立配置 Salt

Custom Mode 的核心特性:每个连接(通过 conv ID 标识)可以配置独立的 salt,实现连接级别的安全隔离。

这个特性特别适用于:

  • 多租户场景 - 不同租户使用不同的 salt,实现数据隔离
  • 不同安全级别 - 为不同安全级别的连接配置不同的 salt
  • 动态密钥管理 - 可以为每个连接动态分配和管理 salt

服务器端配置

use rust_tokio_kcp::{KcpConfig, ListenerMode, KcpListener};

// 启用 Custom Mode
let mut config = KcpConfig::default();
config.listener_mode = ListenerMode::Custom;

let mut listener = KcpListener::bind(config, "127.0.0.1:8080").await?;

// 为不同的连接配置独立的 salt
// conv_id = 1 使用 salt1
listener.custom_mode_notify().add(1, b"salt-for-connection-1".to_vec()).await?;

// conv_id = 2 使用 salt2
listener.custom_mode_notify().add(2, b"salt-for-connection-2".to_vec()).await?;

// conv_id = 100 使用 salt3(可以是任意 u32 值)
listener.custom_mode_notify().add(100, b"another-salt-value".to_vec()).await?;

// 动态移除某个连接的 salt 配置
listener.custom_mode_notify().remove(1).await?;

客户端连接

客户端在连接时需要指定对应的 conv ID 和 salt:

use rust_tokio_kcp::{KcpConfig, KcpStream};
use std::net::SocketAddr;

let config = KcpConfig::default();
let server_addr = "127.0.0.1:8080".parse::<SocketAddr>()?;

// 方式 1: 使用 customize_connect_with_conv 指定 conv 和 salt
let conv_id = 1; // 必须与服务器端配置的 conv_id 匹配
let salt = b"salt-for-connection-1".to_vec(); // 必须与服务器端配置的 salt 匹配
let mut stream = KcpStream::customize_connect_with_conv(&config, conv_id, server_addr, salt).await?;

// 方式 2: 使用已有的 UdpSocket 连接
let udp = UdpSocket::bind("0.0.0.0:0").await?;
let mut stream = KcpStream::customize_connect_with_socket(&config, udp, server_addr, salt).await?;

工作原理

  1. 服务器端:通过 conv_map 维护 conv_id 到 salt 的映射关系
  2. 数据包校验:每个数据包的 CRC32 校验会使用对应 conv 的 salt 进行计算
  3. 会话创建:创建新会话时,会根据 conv_id 查找对应的 salt 并传递给会话
  4. 安全隔离:不同 conv 使用不同的 salt,即使知道一个连接的 salt,也无法伪造其他连接的数据包

完整示例

// 服务器端
use rust_tokio_kcp::{KcpConfig, ListenerMode, KcpListener};
use tokio::io::{AsyncReadExt, AsyncWriteExt};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut config = KcpConfig::default();
    config.listener_mode = ListenerMode::Custom;
    
    let mut listener = KcpListener::bind(config, "127.0.0.1:8080").await?;
    
    // 为不同租户配置不同的 salt
    listener.custom_mode_notify().add(1001, b"tenant-a-salt".to_vec()).await?;
    listener.custom_mode_notify().add(1002, b"tenant-b-salt".to_vec()).await?;
    
    loop {
        let (mut stream, addr) = listener.accept().await?;
        tokio::spawn(async move {
            // 每个连接使用其对应的 salt 进行通信
            let mut buf = [0; 1024];
            loop {
                let n = stream.read(&mut buf).await.unwrap();
                if n == 0 { break; }
                stream.write_all(&buf[..n]).await.unwrap();
            }
        });
    }
}

// 客户端(租户 A)
let conv_id = 1001;
let salt = b"tenant-a-salt".to_vec();
let mut stream = KcpStream::customize_connect_with_conv(&config, conv_id, server_addr, salt).await?;

重要提示

  • ✅ 客户端和服务器端的 conv_id 和 salt 必须完全匹配
  • ✅ 如果客户端使用未在服务器端注册的 conv_id,连接将被拒绝
  • ✅ 每个连接都有独立的 salt,实现了连接级别的安全隔离
  • ✅ 可以在运行时动态添加或移除 conv_id 和 salt 的映射关系

性能优化配置

use rust_tokio_kcp::{KcpConfig, KcpNoDelayConfig};

let mut config = KcpConfig::default();

// 使用最快模式(低延迟,适合游戏等实时应用)
config.nodelay = KcpNoDelayConfig::fastest();
// 或使用普通模式(平衡延迟和带宽)
config.nodelay = KcpNoDelayConfig::normal();

// 增大窗口大小以提高吞吐量
config.wnd_size = (1024, 1024);

// 启用立即刷新以提高响应速度
config.flush_write = true;
config.flush_acks_input = true;

架构设计

共享 UdpSocket

本项目采用共享 UdpSocket 的设计:

  • 所有会话共享同一个 UdpSocket - 通过 Arc<UdpSocket> 实现零成本共享
  • 单线程接收,多线程发送 - Listener 主循环负责接收所有数据包,各会话独立处理发送
  • 资源高效 - 避免为每个连接创建独立的 socket,减少系统资源占用

并发模型

  • KcpListener - 单任务循环接收 UDP 数据包,根据 conv 分发到对应会话
  • KcpSession - 每个会话有独立的 IO 任务和更新任务
  • KcpStream - 提供类似标准库的异步读写接口

配置参数说明

KcpConfig

参数 类型 默认值 说明
mtu usize 1400 最大传输单元
nodelay KcpNoDelayConfig normal() 延迟配置
wnd_size (u16, u16) (256, 256) 发送/接收窗口大小
stream bool false 是否启用流模式
fec_data_shards usize 0 FEC 数据分片数(0 表示禁用)
fec_parity_shards usize 0 FEC 校验分片数(0 表示禁用)
crypt Option<Arc<dyn BlockCrypt>> None 加密算法
listener_mode ListenerMode Normal 监听器模式:Normal - 标准 CRC32 校验;Custom - 每个连接独立配置 salt(见 Custom Mode 章节)

KcpNoDelayConfig

参数 类型 说明
nodelay bool 是否启用 NoDelay
interval i32 内部更新间隔(毫秒)
resend i32 快速重传触发阈值
nc bool 是否禁用拥塞控制

预设配置:

  • KcpNoDelayConfig::fastest() - 最快模式:nodelay=true, interval=10ms, resend=2, nc=true
  • KcpNoDelayConfig::normal() - 普通模式:nodelay=false, interval=40ms, resend=0, nc=false

示例

项目提供了多个示例:

  • examples/server.rs - 完整的服务器示例,支持加密和 FEC
  • examples/client.rs - 客户端示例
  • examples/benchmark_client.rs - 性能测试客户端
  • examples/test_client.rs - 测试客户端

运行示例:

# 运行服务器
cargo run --example server --features aes

# 运行客户端
cargo run --example client --features aes

与 kcp-go 兼容性

本项目完全兼容 kcp-go,可以:

  • ✅ 与 Go 版本的 KCP 服务器/客户端互通
  • ✅ 支持相同的加密算法(AES、TEA、XTEA 等)
  • ✅ 支持相同的 FEC 配置
  • ✅ 支持相同的 KCP 参数配置

测试兼容性:

# 启动 Go 服务器(在 kcp-server 目录)
cd kcp-server
go run server.go

# 运行 Rust 客户端
cargo run --example client --features aes

性能注意事项

锁竞争

当前实现中存在以下锁:

  1. SpinMutex (KcpSocket) - 保护 KCP 状态,每个会话独立
  2. Mutex (FecEncoder/FecDecoder) - 保护 FEC 编码器/解码器,每个会话独立
  3. 共享 UdpSocket - Tokio 的 UdpSocket 是线程安全的,内部已处理并发

在高并发场景下,建议:

  • 使用 parking_lot::Mutex 替代标准库的 Mutex(如果性能成为瓶颈)
  • 考虑为每个会话使用独立的 UdpSocket(会增加资源开销)

最佳实践

  1. 合理配置窗口大小 - 根据网络延迟和带宽调整 wnd_size
  2. 启用 FEC 应对丢包 - 在丢包率较高的网络环境中启用 FEC
  3. 使用最快模式降低延迟 - 对于实时应用,使用 KcpNoDelayConfig::fastest()
  4. 启用立即刷新 - 对于低延迟需求,启用 flush_writeflush_acks_input

许可证

MIT License

致谢

本项目基于 tokio_kcp 项目进行修改和扩展,并参考了 kcp-go 的实现。

相关链接

Commit count: 0

cargo fmt