docs: 全面校对全部章节文档与示例代码

通过多智能体工作流对 19 章笔记(README.md)与 96 个 .c 示例代码做深度
审查与对抗性验证,修复 317 处确认问题,涵盖:

技术正确性:
- 修复缓冲区溢出:echo_mpserv.c / echo_storeserv.c 等的 read(buf, BUFSIZ)
  改为 BUF_SIZE(buf 仅 30 字节,BUFSIZ 远大于此)
- 修复 open() 缺少 mode 参数:low_open.c / fd_seri.c / desto.c 等
  O_CREAT 调用补 0644(原导致 low_read 链路失败)
- 修复 feof 循环 off-by-one:news_sender.c / echo_stdserv.c 改用 fgets
  返回值判断
- 修复线程竞态:chat_server.c / webserv_linux.c 的 &clnt_sock 栈地址
  传子线程改为 malloc 分配 + free
- 修复索引混淆:char_EPLTserv.c 错用 clnt_sock 查找改为 ep_events[i].data.fd
- 修复格式化符:thread4.c 的 sizeof 用 %d 改为 %zu
- 修正习题答案:ch01 fd 序号、ch13 MSG_OOB 加粗项、ch09 Nagle 等

文档规范:
- 统一术语:IPv4/IPv6、接收(receive)/连接(connection)
- 修正错别字:occured→occurred、cooffee→coffee、Usgae→Usage、
  eerror→error、proess→process 等
- 修复病句、补全习题答案解释
- GitHub 绝对 URL 改为相对路径,统一项目引用规范
- 同步根 README.md(前言 + 19 章合并)

另:重命名 ch10/remove_zomebie.c → remove_zombie.c(修正拼写)

所有 .c 文件经 gcc 编译验证通过(ch17 epoll 文件因 macOS 无 sys/epoll.h
跳过,已人工复核)。
This commit is contained in:
riba2534
2026-06-28 12:47:46 +08:00
parent a9ef4b6dc4
commit 5625eea472
76 changed files with 707 additions and 629 deletions

View File

@@ -1,8 +1,8 @@
## 第 7 章 优雅断开套接字的连接
## 第 7 章 优雅断开套接字的连接
本章代码,在[TCP-IP-NetworkNote](https://github.com/riba2534/TCP-IP-NetworkNote)中可以找到。
本章讨论如何优雅断开套接字的连接,之前用的方法不够优雅是因为,我们是调用 close 函数或 closesocket 函数单方面断开连接的。
本章讨论如何优雅断开套接字的连接,之前用的方法不够优雅是因为,我们是调用 close 函数或 closesocket 函数单方面断开连接的。
### 7.1 基于 TCP 的半关闭
@@ -14,9 +14,9 @@ Linux 的 close 函数和 Windows 的 closesocket 函数意味着完全断开连
![](images/5c412a8baa2d8.png)
图中描述的是 2 台主机正在进行双向通信,主机 A 发送完最后的数据后,调用 close 函数断开了最后的连接,之后主机 A 无法再接主机 B 传输的数据。实际上,是完全无法调用与接数据相关的函数。最终,由主机 B 传输的、主机 A 必须要接的数据也销毁了。
图中描述的是 2 台主机正在进行双向通信,主机 A 发送完最后的数据后,调用 close 函数断开了最后的连接,之后主机 A 无法再接主机 B 传输的数据。实际上,是完全无法调用与接数据相关的函数。最终,由主机 B 传输的、主机 A 必须要接的数据也销毁了。
为了解决这类问题,「只关闭一部分数据交换中使用的流」的方法应运而生。断开一部分连接是指,可以传输数据但是无法接收,或可以接数据但无法传输。顾名思义就是只关闭流的一半。
为了解决这类问题,「只关闭一部分数据交换中使用的流」的方法应运而生。断开一部分连接是指,可以传输数据但是无法接收,或可以接数据但无法传输。顾名思义就是只关闭流的一半。
#### 7.1.2 套接字和流Stream
@@ -37,7 +37,7 @@ shutdown 用来关闭其中一个流:
int shutdown(int sock, int howto);
/*
成功时返回 0 ,失败时返回 -1
sock: 需要断开套接字文件描述符
sock: 需要断开套接字文件描述符
howto: 传递断开方式信息
*/
```
@@ -48,7 +48,7 @@ howto: 传递断开方式信息
- `SHUT_WR` : 断开输出流
- `SHUT_RDWR` : 同时断开 I/O 流
若向 shutdown 的第二个参数传递`SHUT_RD`,则断开输入流,套接字无法接收数据。即使输入缓冲收到数据也会抹去,而且无法调用相关函数。如果向 shutdown 的第二个参数传递`SHUT_WR`,则中断输出流,也就无法传输数据。如果输出缓冲中还有未传输的数据,则将传递给目标主机。最后,若传递关键字`SHUT_RDWR`,则同时中断 I/O 流。这相当于分 2 次调用 shutdown ,其中一次以`SHUT_RD`为参数,另一次以`SHUT_WR`为参数。
若向 shutdown 的第二个参数传递`SHUT_RD`,则断开输入流,套接字无法接收数据。即使输入缓冲收到数据也会抹去,而且无法调用相关函数。如果向 shutdown 的第二个参数传递`SHUT_WR`,则中断输出流,也就无法传输数据。如果输出缓冲中还有未传输的数据,则将传递给目标主机。最后,若传递关键字`SHUT_RDWR`,则同时中断 I/O 流。这相当于分 2 次调用 shutdown ,其中一次以`SHUT_RD`为参数,另一次以`SHUT_WR`为参数。
#### 7.1.4 为何要半关闭
@@ -56,15 +56,15 @@ howto: 传递断开方式信息
> 一旦客户端连接到服务器服务器将约定的文件传输给客户端客户端收到后发送字符串「Thank you」给服务器端。
此处「Thank you」的传递是多余的这只是用来模拟客户端断开连接前还有数据要传输的情况。此时程序的编写难度并不小因为传输文件的服务器端只需连续传输文件数据即可而客户端无法知道需要接收数据到何时。客户端也没办法无休止调用输入函数,因为这有可能导致程序**阻塞**。
此处「Thank you」的传递是多余的这只是用来模拟客户端断开连接前还有数据要传输的情况。此时程序的编写难度并不小因为传输文件的服务器端只需连续传输文件数据即可而客户端无法知道需要接收数据到何时。客户端也没办法无休止调用输入函数,因为这有可能导致程序**阻塞**。
> 是否可以让服务器和客户端约定一个代表文件尾的字符?
这种方式也有问题,因为这意味文件中不能有与约定字符相同的内容。为了解决该问题,服务端应最后向客户端传递 EOF 表示文件传输结束。客户端通过函数返回值接 EOF ,这样可以避免与文件内容冲突。那么问题来了,服务端如何传递 EOF
这种方式也有问题,因为这意味文件中不能有与约定字符相同的内容。为了解决该问题,服务端应最后向客户端传递 EOF 表示文件传输结束。客户端通过函数返回值接 EOF ,这样可以避免与文件内容冲突。那么问题来了,服务端如何传递 EOF
> 断开输出流时向主机传输 EOF。
当然,调用 close 函数的同时关闭 I/O 流,这样也会向对方发送 EOF 。但此时无法再接对方传输的数据。换言之,若调用 close 函数关闭流,就无法接客户端最后发送的字符串「Thank you」。这时需要调用 shutdown 函数,只关闭服务器的输出流。这样既可以发送 EOF ,同时又保留了输入流。下面实现收发文件的服务器端/客户端。
当然,调用 close 函数的同时关闭 I/O 流,这样也会向对方发送 EOF 。但此时无法再接对方传输的数据。换言之,若调用 close 函数关闭流,就无法接客户端最后发送的字符串「Thank you」。这时需要调用 shutdown 函数,只关闭服务器的输出流。这样既可以发送 EOF ,同时又保留了输入流。下面实现收发文件的服务器端/客户端。
#### 7.1.5 基于半关闭的文件传输程序
@@ -74,8 +74,8 @@ howto: 传递断开方式信息
下面的代码为编程简便,省略了大量错误处理代码。
- [file_client.c](https://github.com/riba2534/TCP-IP-NetworkNote/blob/master/ch07/file_client.c)
- [file_server.c](https://github.com/riba2534/TCP-IP-NetworkNote/blob/master/ch07/file_server.c)
- [file_client.c](file_client.c)
- [file_server.c](file_server.c)
编译运行:
@@ -90,7 +90,7 @@ gcc file_server.c -o fserver
![](images/5c4140bc8db2f.png)
客户端接完成后,服务器会接收到来自客户端的感谢信息。
客户端接完成后,服务器会接收到来自客户端的感谢信息。
### 7.2 基于 Windows 的实现

View File

@@ -23,7 +23,7 @@ int main(int argc, char *argv[])
exit(1);
}
fp = fopen("receive.cpp", "wb");
fp = fopen("receive.dat", "wb");
sd = socket(PF_INET, SOCK_STREAM, 0);
memset(&serv_adr, 0, sizeof(serv_adr));