15 套接字和标准I/O
2025/12/30大约 4 分钟
15 套接字和标准I/O
标准I/O函数的优点
标准I/O函数和非标准I/O函数的区别
- 标准:按照ANSI C标准定义,具有良好的移植性
- 带缓冲:标准I/O函数带有输入、输出缓冲,能够提高性能
socket缓冲和标准I/O缓冲区别:
socket 缓冲的主要目的是为了实现TCP协议而设立的,TCP传输丢失数据时需要重新传输,那么之前传输的数据(丢失的数据)需要缓存,缓存的位置就是socket的输出缓冲。
标准I/O缓冲的目的是提高性能
性能提升的体现
for(int i=0; i<10; i++)
{
write(sock, "a", 1);
}
FILE *fp = fdopen(sock, "w");
for(int i=0; i<10; i++)
{
fputs("a", fp);
}
fflush(fp);| 行号 | 功能 | 说明 |
|---|---|---|
| 1-4 | 通过非标准I/O函数write写入数据 | |
| 6 | 将fd转换为FILE结构体指针 | |
| 7-10 | 通过标准IO函数fputs写入 | |
| 11 | 刷新缓冲 | 如果不进行刷新,那么存在于标准IO缓冲中的数据不会写入到socket的缓冲中,也就是不会被发送 |
| 效果: |
write

循环10次写入的字符a,分为两包发送,第一包数据长度为1,第二包数据长度为9
这里是开启了Nagle 算法,如果关闭Nagle,那么总的数据包数量是10
fputs

只有一包
即使关闭Nagle也是一包
以上对比使用标准IO(带缓冲)的优点:
- 传输的数据量更大:可以将更多的数据写入到标准IO的缓冲中,然后一次性写入到socket中。如果使用write 那么每次写入的大小就是write的大小
- 减少数据向(socket)输出缓冲移动的次数:只要数据写入到socket缓冲,那么就可能被发送。并且向socket输出缓冲写入数据也会消耗不少时间。通过标准IO缓冲缓存数据可以减少向下级缓冲(socket)写入的次数,从而提升性能
书中示例程序:分别使用read、write ;fgets、fputs 拷贝一个大于300M的文本文件,测试执行时间
标准I/O函数的缺点
- 不容易进行双向通信:“每次切换读写工作状态时应调用fflush”,从write切换到read 比较好理解,将输出缓冲中的数据输出,然后再进行读取。从read 切换到write 也需要调用fflush。
- 可能需要频繁调用fflush
- 在fd 和 FILE 之间进行转换
fflush
NAME
fflush - flush a stream
SYNOPSIS
#include <stdio.h>
int fflush(FILE *stream);
DESCRIPTION
For output streams, fflush() forces a write of all user-space buffered data for the given output or update stream via the stream's underlying write function.
For input streams associated with seekable files (e.g., disk files, but not pipes or terminals), fflush() discards any buffered data that has been fetched from the underlying file, but has not been consumed by the application.
The open status of the stream is unaffected.
If the stream argument is NULL, fflush() flushes all open output streams.output streams
- user-space buffered data
- underlying write function
input streams
- seekable files:有起点 和 终点,可定位的文件,如disk files。对于pipes or terminals 我的理解,他们没有终点(或者说只有起点),不知道输入的次数,以及每次输入的长度。每次输入过后都回到起点。
fd 和 FILE 之间的转换
fd -》 FILE
FILE *fdopen(int fd, const char *mode);
Modes "w" or "w+" do not cause truncation of the file. The file descriptor is not dup'ed, and will be closed when the stream created by fdopen() is closed.正常情况下指定打开的mode 为 w 或 w+ 会清除文件原有内容
如:
实现:
FILE *fp1 = fopen("1.txt", "w");
fclose(fp1);
return 0;效果:
ming@ubuntu:/media/sf_share/Network/build$ cat 1.txt
hello world!
ming@ubuntu:/media/sf_share/Network/build$ ./Network
ming@ubuntu:/media/sf_share/Network/build$ cat 1.txt1.txt 中的内容被清除
但是通过fdopen 转换fd,使用 w 或 w+ 就不会清除原有内容
实现:
int fd = open("1.txt", O_RDWR);
FILE *file = fdopen(fd, "w+");
char buf[100] = {0};
printf("read:%s ", fgets(buf, sizeof(buf), file));
fclose(file);
return 0;效果:
ming@ubuntu:/media/sf_share/Network/build$ cat 1.txt
hello world!
ming@ubuntu:/media/sf_share/Network/build$ ./Network
read:hello world!
ming@ubuntu:/media/sf_share/Network/build$ cat 1.txt
hello world!FILE -》 fd
int fileno(FILE *stream);
The function fileno() examines the argument stream and returns its integer descriptor.基于套接字的标准I/O函数使用
clnt_sock = accept(...);
...
readfp = fdopen(clnt_sock, "r");
writefp = fdopen(clnt_sock, "w");
while(!feof(readfp))
{
fgets(message, BUF_SIZE, readfp);
fputs(message, writefp);
fflush(writefp);
}
fclose(readfp);
fclose(writefp);| 行号 | 功能 | 说明 |
|---|---|---|
| 3 | 以只读的方式获取客户端socket 的FILE | |
| 5 | 判断是否断开连接 | |
| 11 | 关闭输入流 | |
| 12 | 关闭输出流 |
通过fdopen,以两种方式(只读、只写)获取到的FILE 关闭一个,对另一个是否有影响?当打开的流全部关闭时文件被关闭?