第26.4章:transmit
1. transmit 用来做什么
transmit 把字节从输入设备搬到输出设备。它不格式化、不解析、不解释这些字节。
它是核心 I/O 三件套的第三个成员:print 写值,scan 读值,
transmit 搬字节流。
这个操作存在,是因为手写循环很容易在细节上出错。一次 read
可能少于你要求的字节数;一次 write 可能只接受一部分;
两者都可能被中断;带缓冲的输入设备可能已经持有一些字节,
而直接的描述符复制会越过它们。transmit 在一个地方处理所有这些情况。
2. 两个操作
| 操作 | 返回 | 含义 |
|---|---|---|
::fast_io::operations::transmit_some_bytes(out, off_out, in, off_in, size) |
::std::size_t |
最多搬 size 字节。返回这一轮实际搬了多少;0
表示输入结束。 |
::fast_io::operations::transmit_all_bytes(out, off_out, in, off_in, total) |
void |
重复直到输入结束或可选上界耗尽。 |
off_out 和 off_in 是 fpos_nullable_ptr。
传 {} 表示使用并推进设备自己的位置。传一个
intfpos_t 引用表示在显式偏移处传输,不移动设备自己的位置;
被引用的值会推进到已传输字节之后。
total 是 size_t_opt。{} 表示“直到 EOF”;
::fast_io::size_t_opt{n} 表示“最多 n 字节”。
这是给已经知道 body 长度的协议用的上界。
3. 文件到文件
#include <fast_io.h>
int main()
{
::fast_io::ibuf_file in("input.bin");
::fast_io::obuf_file out("output.bin");
::fast_io::operations::transmit_all_bytes(out, {}, in, {}, {});
}
应用代码里没有临时缓冲区。如果平台和设备类型支持内核复制路径,
transmit_all_bytes 可以使用 copy_file_range、
splice 或 sendfile;否则退回一个有界内部缓冲区。
应用不需要维护两个版本的复制。
设备不必是文件。同一个调用可以把字节从管道搬到文件、从 socket 搬到文件, 或从文件搬到 socket。这也是进程与管道那一章使用同一操作的原因: 子进程写进管道,父进程把管道 transmit 到自己的输出。
4. 有界传输
带长度前缀的协议应该把长度传进去。即使输入还有更多字节,传输也会在上界处停止:
::std::size_t const body_length{content_length};
::fast_io::operations::transmit_all_bytes(
out, {}, in, {}, ::fast_io::size_t_opt{body_length});
需要这一轮搬了多少字节时用 transmit_some_bytes,
例如更新进度计数或交错处理多个流。返回 0 表示 EOF,不是错误。
5. 带缓冲的输入与输出
transmit 对带缓冲和不带缓冲的设备、输入和输出两侧都成立。
实现会小心处理两个顺序风险:
- 输入预读。 带缓冲的输入设备可能已经在缓冲区里持有字节。 直接描述符到描述符的复制会跳过它们。操作会先转发挂起的输入缓冲区, 再从设备传输。
- 输出缓冲。 带缓冲的输出设备可能持有待写字节。 操作会在内核级复制之前刷新该缓冲区,保证目标顺序。
这就是为什么 transmit 对 scan 和 print
使用的带缓冲类型也安全,而不只是对原始 native 文件安全。
6. 失败与部分进展
两个操作都通过 herbception 报告设备失败。部分传输不是失败:
transmit_all_bytes 会继续到 EOF 或上界,
transmit_some_bytes 报告已搬字节数。一个字节只有在真正到达输出之后才计数,
所以短写不会悄悄丢失输入数据。
偏移是另一类失败面。给不能 seek 的设备(管道或 socket)传非空输出偏移是错误;
这些设备传 {}。
7. transmit、print 与 scan
| 操作 | 输入 | 输出 | 数据 |
|---|---|---|---|
print |
值 | 设备 | 格式化文本 |
scan |
带缓冲设备 | 值 | 解析文本 |
transmit |
设备 | 设备 | 原始字节 |
不要用 print 搬二进制 blob,也不要用 transmit 写数字。
边界和第11.5章说的一样:字节用 transmit,结构化数据用 scan 和 print。
fast_io 模块导出列表不包含
transmit_some_bytes 或 transmit_all_bytes;
在它们被导出之前,使用 #include <fast_io.h>。
要点
transmit_some_bytes搬一轮有界数据并返回字节数;transmit_all_bytes循环到 EOF 或上界。- 偏移是可选的;管道、socket 和普通流式传输传
{}。 - 返回
0表示 EOF,不是错误。 - 操作会在内核复制之前转发带缓冲输入并刷新带缓冲输出。
- 平台支持时可以使用内核复制路径,否则退回有界缓冲区。
- 字节用 transmit,值用 print,解析输入用 scan。
- transmit 操作目前属于头文件侧,不在模块导出列表里。