命名管道跨平台开发需注意:windows用createnamedpipe需正确设置dwopenmode和dwpipemode,linux用mkfifo需避免o_rdwr竞态;读写应合理分包并统一消息边界处理。

Windows 上用 CreateNamedPipe 创建服务端管道
命名管道在 Windows 是同步阻塞 I/O 模型,服务端必须先创建、等待连接,客户端才能连上。直接调用 CreateNamedPipe 是最可控的方式,比封装库更易调试。
常见错误是没设对 dwOpenMode:比如用了 PIPE_ACCESS_DUPLEX 却没配 PIPE_TYPE_MESSAGE,结果读写行为不一致;或者 nMaxInstances 设成 1,第二个客户端直接 ERROR_PIPE_BUSY 被拒。
-
lpName必须是\\.\pipe\xxx格式,开头双反斜杠不能少,否则CreateNamedPipe返回INVALID_HANDLE_VALUE - 建议设
dwPipeMode = PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,避免字节流粘包问题 -
ConnectNamedPipe在阻塞模式下会挂起线程,别在 UI 线程里直接调,容易卡死
Linux 上用 mkfifo + open 模拟命名管道
Linux 没有原生“命名管道服务端/客户端”概念,mkfifo 创建的是一个特殊文件,靠 open 的阻塞语义来同步——一端 open(O_WRONLY) 会等另一端 open(O_RDONLY) 才返回。
典型坑是两端都用 O_RDWR:Linux 允许,但行为不可靠,某些内核版本下 read 可能永远返回 0;还有人忘了 mkfifo 后要检查 errno == EEXIST,重复创建导致失败。
立即学习“C++免费学习笔记(深入)”;
-
mkfifo("/tmp/my_pipe", 0666)后,权限可能被 umask 截断,建议后续用chmod补全 - 服务端应先
open(O_RDONLY | O_NONBLOCK)尝试打开,再open(O_WRONLY)触发阻塞,避免竞态 - 不要用
fscanf读命名管道——遇到 EOF(对方关闭)会直接失败,改用read判断返回值
WriteFile 和 read 的缓冲区大小不是越小越好
Windows 下 WriteFile 发送小块数据(比如每次 1 字节),会触发大量系统调用和上下文切换;Linux 下 read 用太小的缓冲区(如 1 字节),同样效率极低,还可能因管道缓冲区满而阻塞。
命名管道本身有内建缓冲(Windows 默认 64KB,Linux PIPE_BUF 通常是 4KB),但应用层仍需合理分包。消息边界不等于 TCP 那种“自动分帧”,得自己处理。
- Windows 推荐单次
WriteFile不超过 64KB,大于这个值会被拆包,且不保证原子性 - Linux 下若用
PIPE_TYPE_MESSAGE类似语义,得自己加长度头或分隔符,不然read可能只读到半条消息 - 跨平台代码别假设
sizeof(size_t)一致,长度字段统一用uint32_t更安全
客户端连不上时先查 GetLastError() 或 errno
连不上不一定是代码逻辑错,更多是环境或权限问题。Windows 下 ERROR_FILE_NOT_FOUND 往往是管道名拼错或服务端根本没启动;ERROR_ACCESS_DENIED 多半是 UAC 权限或服务端用 SECURITY_DESCRIPTOR 限制了访问。
Linux 下 errno == ENXIO 表示服务端已关闭或未 open 读端;EAGAIN 是非阻塞模式下暂时无数据,不是错误。
- Windows 客户端用
CreateFile连接时,dwDesiredAccess必须和服务端dwOpenMode匹配,比如服务端是GENERIC_READ,客户端就不能只请求GENERIC_WRITE - Linux 下
ls -l /tmp/my_pipe看权限位,如果显示prw-------,其他用户进程根本打不开 - 调试时用
Process Explorer(Win)或lsof | grep pipe(Linux)确认句柄是否真被占用
命名管道最难的不是写通,而是两边对“消息结束”的理解是否一致。别依赖底层自动切分,老老实实加长度头或换行符——这点比选 API 更关键。










