欢迎光临
我们一直在努力

CatShare原理实战篇(3):在Android里内嵌Ktor+Netty HTTPS服务器,WebSocket协议与ZIP流式传输

CatShare原理实战篇(3):在Android里内嵌Ktor+Netty HTTPS服务器,WebSocket协议与ZIP流式传输

【免费下载链接】CatShare 类原生 & 海外设备,现已加入互传联盟。 【免费下载链接】CatShare 项目地址: https://gitcode.com/gh_mirrors/ca/CatShare

CatShare 是一款开源的 Android 互传应用,模仿各大品牌手机间的"互传"体验:用蓝牙发现附近设备,通过 Wi-Fi Direct 点对点直连,发送和接收文件与文本。本文是"原理实战"系列第 3 篇,带你拆解它的传输层核心:如何在 Android App 内部嵌入一个 Ktor + Netty 的 HTTPS 服务器,用 WebSocket 协议完成信令握手,并借助 ZIP 流式传输实现满速文件互传。

🏗️ 为什么要在 App 里跑一个"服务器"?

CatShare 的互传链路由三个通道分工协作:

通道角色带宽
蓝牙 LE(BLE) 发现设备、交换公钥、传递 Wi-Fi Direct 连接信息
Wi-Fi Direct 免路由器组网,相当于两台设备间"拉网线"
HTTPS + WebSocket 真正的数据通道,文件从这里流过

它的设计哲学是:发送方临时充当服务器,接收方临时充当客户端。整个过程中,只有发送方运行 Ktor 服务器,接收方只运行一个 Ktor HttpClient,传输结束后服务器立即关闭。

  • 发送方(服务器端):P2pSenderService.kt
  • 接收方(客户端):P2pReceiverService.kt

🔐 搭建内嵌 HTTPS 服务器:三个关键点

关键点 1:一行 embeddedServer 启动 Netty 引擎

Ktor 的 embeddedServer(Netty) 把一个完整的 HTTP 服务器直接嵌入 App 进程,无需独立部署:

val httpServer = embeddedServer(Netty, configure = {
val keyStore = buildKeyStore {
certificate("sampleAlias") {
password = "foobar"
domains = listOf("127.0.0.1", "0.0.0.0", "localhost")
}
}
sslConnector(keyStore = keyStore, keyAlias = "sampleAlias", …) {
port = 0
}
enableHttp2 = false
}) {
install(WebSockets)
// 路由:/websocket 与 /download
}

关键点 2:自签名证书 + 随机端口,用完即弃

  • buildKeyStore 由 ktor-network-tls-certificates 在运行时自动生成自签名证书,无需在打包时携带任何证书文件,每次传输都是一份全新证书;
  • sslConnector 设置 port = 0,让系统分配一个空闲端口,启动后通过 resolvedConnectors().first().port 取出实际端口(P2pSenderService.kt),这个端口会随连接信息一起交给接收方。

关键点 3:接收方为什么敢"跳过证书校验"?

因为证书是发送方随机生成的自签名证书,接收方无法提前预装。接收端通过 FakeTrustManager.kt 信任一切:

class FakeTrustManager : X509TrustManager {
override fun checkClientTrusted(…) { } // 空实现
override fun checkServerTrusted(…) { }
override fun getAcceptedIssuers() = emptyArray()
}

这并非裸奔:整个 Wi-Fi Direct 网络是隔离的临时组网,接入凭证(SSID/PSK/端口)只能通过加密的 BLE 通道获得,外部设备根本无法路由到该服务器,"安全"由链路隔离而非 CA 体系保证。

📡 WebSocket 信令:一条连接上跑 4 类消息

文件走 HTTP 下载,控制指令则全部复用同一条 WSS 连接(wss://<组网IP>:<端口>/websocket)。消息格式极简,一行文本:

type:id:name?payload
// 例如:action:1:sendRequest?{"taskId":"42","fileName":"cat.png","fileCount":1,"totalSize":1024}

解析正则定义在 WebSocketMessage.kt。完整握手时序:

  • versionNegotiation(发送方 → 接收方):声明协议版本(当前为 1);
  • ack(双向):对每条消息的确认,接收方回 ack 表示"我收到了";
  • sendRequest(发送方 → 接收方):携带任务 ID、发送方名称、首个文件名、文件数、总大小,接收方据此弹出"是否接收"通知;
  • status(接收方 → 发送方):1 表示传输成功,3/user refuse 表示用户点了"拒绝",发送方收到后优雅终止(WsUtils.kt)。
  • 发送方还设置了层层超时:等待 WS 连接 10 秒、握手 5 秒、开始传输 30 秒(P2pSenderService.kt),任何一步卡住都会给出明确报错,不会无声挂起。

    📦 ZIP 流式传输:为什么不用多个 HTTP 请求逐文件发?

    发送端:边读边压,不落盘

    /download 路由用 respondOutputStream 把响应体直接当流用,ZipOutputStream 逐个文件写入:

    call.respondOutputStream(ContentType.Application.Zip) {
    ZipOutputStream(this).use { zo ->
    for ((i, rf) in task.files.withIndex()) {
    cr.openInputStream(rf.uri)!!.use { ist ->
    zo.putNextEntry(ZipEntry("$i/${rf.name}"))
    // 4MB 缓冲,读完写入,累计进度
    }
    }
    }
    }

    两个工程细节值得借鉴(P2pSenderService.kt):

    • 4MB 缓冲区:大块读写摊薄系统调用开销;
    • 进度节流:累计 1 秒才刷新一次通知,避免通知栏被高频更新卡死。

    接收端:边收边解,直写 MediaStore

    接收方发起 GET /download?taskId=xxx,把响应字节通道直接包成 ZipInputStream(P2pReceiverService.kt),saveArchive 逐个 entry 读文件、直写 Downloads/CatShare/,全程不需要把 ZIP 整体落盘——内存占用恒定,进度条天然连续。

    在 Android 14+ 上还注册了 ZipPathValidatorCallback 来绕过对"无意义相对路径"的严格校验(P2pReceiverService.kt、ZipPathValidatorCallback.kt)。

    多文件 = 1 个 HTTP 请求,配合协程取消即可随时中断传输,这是逐文件方案做不到的。

    🔄 一次传输的完整生命周期(8 步)

  • 接收方开启接收模式,BLE 广播携带公钥的设备信息(GattServerService.kt);
  • 发送方蓝牙发现目标设备,BLE 连接并读取设备信息,协商会话密钥;
  • 发送方启动 Ktor HTTPS 服务器(随机端口);
  • 发送方创建 Wi-Fi Direct 组网,把 SSID/PSK/MAC/端口打包成 P2pInfo,敏感字段加密后写入接收方的 BLE 特征值(P2pInfo.kt);
  • 接收方读取 P2pInfo,连入 Wi-Fi Direct 网络;
  • 接收方建立 wss://<组网IP>:<端口>/websocket 连接;
  • 完成 versionNegotiation → sendRequest → 用户点"接受";
  • 接收方流式下载并解压落盘,回执 status ok,发送方 httpServer.stop() 并解散 Wi-Fi Direct 组,一切归位。
  • 📚 源码阅读清单

    想学什么从哪读起
    Ktor 服务器搭建与 WebSocket 路由 P2pSenderService.kt#L200-L294
    ZIP 流式写出 + 进度节流 P2pSenderService.kt#L296-L359
    客户端信任配置(FakeTrustManager) P2pReceiverService.kt#L383-L399
    流式解压直写 MediaStore P2pReceiverService.kt#L606-L674
    信令消息格式与解析 WebSocketMessage.kt#L27-L48
    依赖版本(Ktor 3.0.3) libs.versions.toml#L8-L31

    💡 一个容易踩的坑:release 包混淆时,记得在 proguard-rules.pro 保留 io.ktor.**,否则 Ktor 依赖反射的类被裁剪后服务器会起不来。

    下一篇,我们将聊聊 BLE 侧的公钥加密握手(BleSecurity),看看 CatShare 如何在不依赖证书体系的前提下完成"设备间信任"。

    【免费下载链接】CatShare 类原生 & 海外设备,现已加入互传联盟。 【免费下载链接】CatShare 项目地址: https://gitcode.com/gh_mirrors/ca/CatShare

    创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

    赞(0)
    未经允许不得转载:171主机测评 » CatShare原理实战篇(3):在Android里内嵌Ktor+Netty HTTPS服务器,WebSocket协议与ZIP流式传输
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址