Linux 有一个准则:一切皆文件。所以在 Linux 中,文件 I/O 函数 read 和 write 就是金字招牌,哪里都可以使用。但是在 Windows 中,文件 I/O 函数和套接字 I/O 函数需要被严格区分使用。现在将它们总结一下放到一个表格里
| Windows 系统 | Linux 系统 |
|---|---|
| 文件 I/O 函数:fopen 和 fwrite | 文件 I/O 函数:open、read 和 write |
| 套接字函数:send 和 recv | 套接字函数:(write 和 read) 或 (send 和 recv)或 (writev 和 readv) |
readv & writev 函数的功能概括为:对数据进行整合传输及发送。
也就是说,通过 writev 函数可以将分散保存在多个缓冲中的数据一并发送,通过 readv 函数可以由多个缓冲分别接受。因此,适当使用这 2 个函数可以减少 I/O 函数的调用次数。
writev 函数
函数原型
#include <sys/uio.h>// 成功时返回发送的字节数,失败时返回 -1ssize_t writev(int filedes, const struct iovec *iov, int iovcnt);
上述函数参数:
- filedes:表示数据传输对象的套接字文件描述符。但该函数并不只限于套接字,因此,可以像 read 函数一样传递文件或标准输出符。
- iov:iovec 结构体数组的地址值,结构体 iovec 中包含待发送数据的位置和大小信息。
- iovcnt:向第二个参数传递的数组长度。
上述第二个参数 iov 指向的 iovec 结构体如下所示:
struct iovec {void *iov_base; // 缓冲地址size_t iov_len; // 缓冲大小}
可以看到,结构体 iovec 由保存待发送数据的缓冲(char 型数组)地址值和实际发送的数据长度信息构成。我们先通过下图看一下该函数的使用方法:

writev 是向某个地方输出数据,而 filedes=1 表示向控制台输出数据(所以,如果是 readv,对应的描述符为 filedes=0 )
下面是示例代码
#include <stdio.h>#include <sys/uio.h>int main(int argc, char *argv[]) {struct iovec vec[2];char buf1[] = "ABCDEFG";char buf2[] = "1234567";int str_len;vec[0].iov_base = buf1;vec[0].iov_len = 7;vec[1].iov_base = buf2;vec[1].iov_len = 4;str_len = writev(1, vec, 2);puts("");printf("Write bytes: %d \n", str_len);return 0;}
运行上述程序:

readv 函数
函数原型
#include <sys/uio.h>// 成功时返回接受的字节数,失败时返回 -1ssize_t readv(int fieldes, const struct iovec *iov, int iovcnt);
上述函数参数:
- filedes:表接收数据的文件(或套接字)描述符。
- iov:包含数据保存位置和大小信息的 iovec 结构体数组的地址值。
- iovcnt:第二个参数中数组长度。
下面是示例代码
#include <stdio.h>#include <sys/uio.h>#define BUF_SIZE 100int main(int argc, char *argv[]) {struct iovec vec[2];char buf1[BUF_SIZE] = {0, };char buf2[BUF_SIZE] = {0, };int str_len;vec[0].iov_base = buf1;vec[0].iov_len = 5; // 无论 buf1 的大小是多少,最多只能保存 5 个字节,剩余数据保存到 vec[1] 中vec[1].iov_base = buf2;vec[1].iov_len = BUF_SIZE;str_len = readv(0, vec, 2);printf("Read bytes: %d \n", str_len);printf("First message: %s \n", buf1);printf("Second message: %s \n", buf2);return 0;}
下面看一下运行结果:

