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)或 (writevreadv

readv & writev 函数的功能概括为:对数据进行整合传输及发送。

也就是说,通过 writev 函数可以将分散保存在多个缓冲中的数据一并发送,通过 readv 函数可以由多个缓冲分别接受。因此,适当使用这 2 个函数可以减少 I/O 函数的调用次数。

writev 函数

函数原型

  1. #include <sys/uio.h>
  2. // 成功时返回发送的字节数,失败时返回 -1
  3. ssize_t writev(int filedes, const struct iovec *iov, int iovcnt);

上述函数参数:

  • filedes:表示数据传输对象的套接字文件描述符。但该函数并不只限于套接字,因此,可以像 read 函数一样传递文件或标准输出符。
  • iov:iovec 结构体数组的地址值,结构体 iovec 中包含待发送数据的位置和大小信息。
  • iovcnt:向第二个参数传递的数组长度。

上述第二个参数 iov 指向的 iovec 结构体如下所示:

  1. struct iovec {
  2. void *iov_base; // 缓冲地址
  3. size_t iov_len; // 缓冲大小
  4. }

可以看到,结构体 iovec 由保存待发送数据的缓冲(char 型数组)地址值和实际发送的数据长度信息构成。我们先通过下图看一下该函数的使用方法:

§ Linux 中的 readv & writev - 图1

writev 是向某个地方输出数据,而 filedes=1 表示向控制台输出数据(所以,如果是 readv,对应的描述符为 filedes=0

下面是示例代码

  1. #include <stdio.h>
  2. #include <sys/uio.h>
  3. int main(int argc, char *argv[]) {
  4. struct iovec vec[2];
  5. char buf1[] = "ABCDEFG";
  6. char buf2[] = "1234567";
  7. int str_len;
  8. vec[0].iov_base = buf1;
  9. vec[0].iov_len = 7;
  10. vec[1].iov_base = buf2;
  11. vec[1].iov_len = 4;
  12. str_len = writev(1, vec, 2);
  13. puts("");
  14. printf("Write bytes: %d \n", str_len);
  15. return 0;
  16. }

运行上述程序:

§ Linux 中的 readv & writev - 图2

readv 函数

函数原型

  1. #include <sys/uio.h>
  2. // 成功时返回接受的字节数,失败时返回 -1
  3. ssize_t readv(int fieldes, const struct iovec *iov, int iovcnt);

上述函数参数:

  • filedes:表接收数据的文件(或套接字)描述符。
  • iov:包含数据保存位置和大小信息的 iovec 结构体数组的地址值。
  • iovcnt:第二个参数中数组长度。

下面是示例代码

  1. #include <stdio.h>
  2. #include <sys/uio.h>
  3. #define BUF_SIZE 100
  4. int main(int argc, char *argv[]) {
  5. struct iovec vec[2];
  6. char buf1[BUF_SIZE] = {0, };
  7. char buf2[BUF_SIZE] = {0, };
  8. int str_len;
  9. vec[0].iov_base = buf1;
  10. vec[0].iov_len = 5; // 无论 buf1 的大小是多少,最多只能保存 5 个字节,剩余数据保存到 vec[1] 中
  11. vec[1].iov_base = buf2;
  12. vec[1].iov_len = BUF_SIZE;
  13. str_len = readv(0, vec, 2);
  14. printf("Read bytes: %d \n", str_len);
  15. printf("First message: %s \n", buf1);
  16. printf("Second message: %s \n", buf2);
  17. return 0;
  18. }

下面看一下运行结果:

§ Linux 中的 readv & writev - 图3