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

@@ -35,6 +35,7 @@ int main(int argc, char *argv[])
char temp[20];
puts("请输入你要计算的数字个数:");
scanf("%d", &n);
message[0] = '\0';
sprintf(temp, "%d", n);
strcat(temp, " ");
strcat(message, temp);

View File

@@ -8,7 +8,7 @@
#define BUF_SIZE 10240
void error_handling(char *message);
char res[10];
char res[16];
char *calc(char *s)
{
int len = strlen(s), i;
@@ -93,7 +93,10 @@ int main(int argc, char *argv[])
if (clnt_sock == -1)
error_handling("accept() error");
str_len = read(clnt_sock, message, BUF_SIZE);
write(clnt_sock, calc(message), str_len);
if (str_len == -1) error_handling("read() error");
message[str_len] = 0;
char *result_str = calc(message);
write(clnt_sock, result_str, strlen(result_str));
close(clnt_sock);
close(serv_sock);
return 0;

View File

@@ -22,7 +22,7 @@ write(sock, message, strlen(message));
str_len = read(sock, message, BUF_SIZE - 1);
```
二者都在循环调用 read 和 write 函数。实际上之前的回声客户端将 100% 接收字节传输的数据,只不过接收数据时的单位有些问题。扩展客户端代码回顾范围,下面是客户端的代码:
二者都在循环调用 read 和 write 函数。实际上之前的回声客户端会接收服务器传输的全部数据,只不过接收数据时的单位有些问题。扩展客户端代码回顾范围,下面是客户端的代码:
```c
while (1)
@@ -48,7 +48,7 @@ while (1)
- [echo_client2.c](https://github.com/riba2534/TCP-IP-NetworkNote/blob/master/ch05/echo_client2.c)
这样修改为接收所有传输数据而循环调用 read 函数。测试及运行结果可参考第四章。
这样修改后,客户端为接收所有传输数据而循环调用 read 函数。测试及运行结果可参考第四章。
#### 5.1.3 如果问题不在于回声客户端:定义应用层协议
@@ -57,7 +57,7 @@ while (1)
现在写一个小程序来体验应用层协议的定义过程。要求:
1. 服务器从客户端获得多个数组和运算符信息。
2. 服务器接收到数字后对进行加减乘运算,然后把结果传回客户端。
2. 服务器接收到数字后对进行加减乘运算,然后把结果传回客户端。
例:
@@ -114,7 +114,7 @@ gcc op_server.c -o opserver
#### 5.2.1 TCP 套接字中的 I/O 缓冲
TCP 套接字的数据收发无边界。服务器即使调用 1 次 write 函数传输 40 字节的数据,客户端也有可能通过 4 次 read 函数调用每次读取 10 字节。但此处也有一些疑问,服务器一次性传输了 40 字节,而客户端竟然可以缓慢分批接收。客户端接收 10 字节后,剩下的 30 字节在何处等候呢?
TCP 套接字的数据收发无边界。服务器即使调用 1 次 write 函数传输 40 字节的数据,客户端也有可能通过 4 次 read 函数调用每次读取 10 字节。但此处也有一些疑问,服务器一次性传输了 40 字节,而客户端竟然可以缓慢分批接收。客户端接收 10 字节后,剩下的 30 字节在何处等候呢?
实际上write 函数调用后并非立即传输数据read 函数调用后也并非马上接收数据。如图所示write 函数调用瞬间数据将移至输出缓冲read 函数调用瞬间,从输入缓冲读取数据。
@@ -140,7 +140,7 @@ I/O 缓冲特性可以整理如下:
数据收发也是如此,因此 TCP 中不会因为缓冲溢出而丢失数据。
**write 函数在数据传输完成时返回**
**write 函数在数据成功移入输出缓冲时返回(并非等到对端接收完毕)**
#### 5.2.2 TCP 内部工作原理 1与对方套接字的连接
@@ -180,11 +180,11 @@ TCP 在实际通信中也会经过三次对话过程,因此,该过程又被
> 刚才传输的 SEQ 为 1000 的数据包接收无误,现在请传递 SEQ 为 1001 的数据包。
对于主机 A 首次传输的数据包的确认消息ACK 1001和为主机 B 传输数据做准备的同步消息SEQ 2000捆绑发送。因此类消息又称为 SYN+ACK。
对于主机 A 首次传输的数据包的确认消息ACK 1001和为主机 B 传输数据做准备的同步消息SEQ 2000捆绑发送。因此此类消息又称为 SYN+ACK。
收发数据前向数据包分配序号,并向对方通报此序号,这都是为了防止数据丢失做的准备。通过向数据包分配序号并确认,可以在数据包丢失时马上查看并重传丢失的数据包。因此 TCP 可以保证可靠的数据传输。
通过这三个过程,这样主机 A 和主机 B 就确认了彼此已经准备就绪。
通过这三个过程,主机 A 和主机 B 就确认了彼此已经准备就绪。
#### 5.2.3 TCP 内部工作原理 2与对方主机的数据交换
@@ -217,7 +217,7 @@ TCP 套接字的结束过程也非常优雅。如果对方还有数据需要传
![](images/5c3ed7503c18c.png)
图中数据包内的 FIN 表示断开连接。也就是说,双方各发送 1 次 FIN 消息后断开连接。此过程经历 4 个阶段因此又称四次握手Four-way handshaking。SEQ 和 ACK 的含义与之前讲解的内容一致,省略。图中,主机 A 传递了两次 ACK 5001也许这里会有困惑。其实第二次 FIN 数据包中的 ACK 5001 只是因为接收了 ACK 消息后未接收到的数据重传的
图中数据包内的 FIN 表示断开连接。也就是说,双方各发送 1 次 FIN 消息后断开连接。此过程经历 4 个阶段因此又称四次握手Four-way handshaking。SEQ 和 ACK 的含义与之前讲解的内容一致,省略。图中,主机 A 传递了两次 ACK 5001也许这里会有困惑。其实图中第二次出现的 ACK 5001 是重传确认:若主机 B 未收到主机 A 第一次发出的 ACKB 会重传 FIN主机 A 收到后再次发送 ACK 5001 予以确认
### 5.3 基于 Windows 的实现

View File

@@ -28,6 +28,7 @@ int main(int argc, char *argv[])
scanf("%s", file_name);
//打开文件名
fp=fopen(file_name, "wb");
if(fp==NULL) error_handling("fopen() error");
//创建套接字
sd=socket(PF_INET, SOCK_STREAM, 0);
@@ -37,7 +38,8 @@ int main(int argc, char *argv[])
serv_adr.sin_addr.s_addr=inet_addr(argv[1]);
serv_adr.sin_port=htons(atoi(argv[2]));
connect(sd, (struct sockaddr*)&serv_adr, sizeof(serv_adr));
if(connect(sd, (struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1)
error_handling("connect() error");
//写入要传输的文件
write(sd, file_name, strlen(file_name)+1);

View File

@@ -54,9 +54,8 @@ int main(int argc, char *argv[])
}
write(clnt_sd, buf, BUF_SIZE);
}
fclose(fp);
}
fclose(fp);
close(clnt_sd); close(serv_sd);
return 0;
}