c++怎么在Windows下利用命名管道实现高性能跨进程的文件传输【进阶】

冬辰姑娘_1003

冬辰姑娘_1003

2026-03-25

134人浏览

原创

命名管道传大文件卡在writefile是因为字节流模式+64kb缓冲区限制导致阻塞;应改用消息模式分块、异步i/o、边写边读,并用transmitfile零拷贝优化,配合协议头与校验确保完整性。

c++怎么在windows下利用命名管道实现高性能跨进程的文件传输【进阶】

命名管道传输大文件时为什么卡在 WriteFile 不返回?

因为默认管道模式是字节流(PIPE_TYPE_BYTE),且缓冲区有限(通常 64KB),WriteFile 会阻塞直到对方调用 ReadFile 消费数据,或者写满内核缓冲区。跨进程传 GB 级文件时,若接收端没及时读、或网络/磁盘慢,发送端就卡住。

解决办法是:改用消息模式 + 合理分块 + 异步 I/O:

  • 创建管道时指定 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE,避免粘包,但注意:消息模式单条上限 64KB,超长会被截断
  • 发送端拆成 ≤64KB 的 std::vector<char></char> 分块,每块单独调用 WriteFile
  • 务必用 FILE_FLAG_OVERLAPPED 创建句柄,配合 GetOverlappedResult 或 IOCP,否则阻塞式写在大文件下极易拖垮整个线程
  • 接收端不能等“全部写完再读”,必须边写边读——启动独立读线程,持续调用 ReadFile 并写入目标文件

CreateNamedPipe 的 nMaxInstances 设成 1 还是 255?

这不是并发连接数的自由选择,而是和你的通信模型强绑定。设成 1 表示“严格一对一”,第二个客户端连上来会直接被 ConnectNamedPipe 拒绝;设成 255 是为了支持“服务端复用同一管道名,轮流服务多个客户端”,但前提是每次服务完必须调用 DisconnectNamedPipe,再重新 ConnectNamedPipe 等待下一个。

文件传输场景下,几乎总是选 1:

  • 传输过程需要稳定上下文(如已协商的压缩算法、校验方式),多实例共享同一管道名会导致状态混乱
  • Windows 内核对每个实例分配独立缓冲区,设太高反而浪费内存,且无实际并发收益
  • 如果真需要一对多分发文件,应该由主服务进程创建多个独立管道(如 \.pipe ile_xfer_001、\.pipe ile_xfer_002),而不是靠 nMaxInstances 混用

为什么用 TransmitFile 替代循环 ReadFile+WriteFile?

因为绕过用户态内存拷贝。传统方式要把文件内容从磁盘读到用户缓冲区,再从缓冲区写进管道内核缓冲区,两次 memcpy;而 TransmitFile 让内核直接把文件映射页链表接续到管道发送队列,零拷贝,实测 1GB 文件快 30%–50%。

但要注意限制条件:

C++14
C++14

C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。

下载
  • 只支持同步句柄(不能带 FILE_FLAG_OVERLAPPED),所以得配专用线程,不能和异步逻辑混用
  • 源文件句柄必须是 GENERIC_READ 打开,且支持 FILE_FLAG_SEQUENTIAL_SCAN(提升预读效率)
  • 目标句柄必须是命名管道或套接字,且处于已连接状态(ConnectNamedPipe 返回 true 后)
  • 别忘了加 TF_USE_KERNEL_APC 标志,否则在高负载下可能触发 APC 注入延迟

示例关键调用:TransmitFile(hPipe, hFile, liFileSize.QuadPart, 0, nullptr, nullptr, TF_USE_KERNEL_APC)

接收端用 ReadFile 读到 0 字节就代表传完了?

不是。命名管道关闭时,ReadFile 才返回 0;但发送端可能只是暂时停顿、网络抖动、或对方进程卡住。盲目按 0 字节结束,会导致文件截断。

正确做法是双方约定协议头:

  • 发送端先写 8 字节 uint64_t file_size,再发原始数据
  • 接收端先用循环 ReadFile 确保读满 8 字节,解析出总长度
  • 之后累计读取字节数,等于 file_size 时才关闭目标文件句柄
  • 额外加一层校验:发送端末尾追一个 32 位 crc32,接收端验证失败则删除临时文件并报错

别依赖 EOF 或错误码判断完成——管道没有“文件结束符”概念,只有连接断开或写端关闭。

最易被忽略的是缓冲区生命周期管理:所有 OVERLAPPED 结构体必须在对应 I/O 完成前保持有效,不能放在栈上然后函数返回;异步读写回调里释放内存前,务必检查 GetLastError() == ERROR_IO_INCOMPLETE 是否仍存在未完成操作。

C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!

相关文章

数码产品性能查询
数码产品性能查询

该软件包括了市面上所有手机CPU,手机跑分情况,电脑CPU,电脑产品信息等等,方便需要大家查阅数码产品最新情况,了解产品特性,能够进行对比选择最具性价比的商品。

下载

相关标签:

c++

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

2025.06.09

4874

18

golang结构体方法
golang结构体方法

本专题整合了golang结构体相关内容,请阅读专题下面的文章了解更多。

2025.07.04

5031

25

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

2023.07.18

6087

5

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

2023.08.10

2588

6

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

2023.08.10

4358

6

windows查看端口占用情况
windows查看端口占用情况

Windows端口可以认为是计算机与外界通讯交流的出入口。逻辑意义上的端口一般是指TCP/IP协议中的端口,端口号的范围从0到65535,比如用于浏览网页服务的80端口,用于FTP服务的21端口等等。怎么查看windows端口占用情况呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.07.26

3279

3

查看端口占用情况windows
查看端口占用情况windows

端口占用是指与端口关联的软件占用端口而使得其他应用程序无法使用这些端口,端口占用问题是计算机系统编程领域的一个常见问题,端口占用的根本原因可能是操作系统的一些错误,服务器也可能会出现端口占用问题。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.07.27

2858

6

windows照片无法显示
windows照片无法显示

当我们尝试打开一张图片时,可能会出现一个错误提示,提示说"Windows照片查看器无法显示此图片,因为计算机上的可用内存不足",本专题为大家提供windows照片无法显示相关的文章,帮助大家解决该问题。

2023.08.01

1508

3

windows查看端口被占用的情况
windows查看端口被占用的情况

windows查看端口被占用的情况的方法:1、使用Windows自带的资源监视器;2、使用命令提示符查看端口信息;3、使用任务管理器查看占用端口的进程。本专题为大家提供windows查看端口被占用的情况的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

2619

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C++ Core Guidelines
C++ Core Guidelines

共0课时 | 0人学习

C++ Reference
C++ Reference

共0课时 | 0人学习

C++ 官方标准说明
C++ 官方标准说明

共0课时 | 0人学习