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

@@ -4,7 +4,7 @@
### 1.1 理解网络编程和套接字
#### 1.1.1构建打电话套接字
#### 1.1.1 构建打电话套接字
以电话机打电话的方式来理解套接字。
@@ -32,7 +32,7 @@ int socket(int domain, int type, int protocol);
```c
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen);
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);
//成功时返回0失败时返回-1
```
@@ -40,7 +40,7 @@ int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen);
**调用 listen 函数(连接电话线)时进行的对话**
> 问:已架设完电话机后是否只需接电话线?
> 问:已架设完电话机后是否只需接电话线?
>
> 答:对,只需要连接就能接听电话。
@@ -62,11 +62,11 @@ int listen(int sockfd, int backlog);
```c
#include <sys/socket.h>
int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//成功时返回文件描述符,失败时返回-1
```
网络编程中接受连接请求的套接字创建过程可整理如下:
网络编程中接受连接请求的套接字创建过程可整理如下:
1. 第一步:调用 socket 函数创建套接字。
2. 第二步:调用 bind 函数分配IP地址和端口号。
@@ -106,11 +106,11 @@ gcc hello_client.c -o hclient
./hclient 127.0.0.1 9190
```
运行的时候,首先在 9190 端口启动服务,然后 hserver 就会一直等待客户端进行连接,当客户端连接位于本地的 IP 为 127.0.0.1 的地址的 9190 端口时,客户端就会收到服务端的回应,输出`Hello World!`
运行的时候,首先在 9190 端口启动服务,然后 hserver 就会一直等待客户端进行连接,当客户端连接到本地地址 127.0.0.1 的 9190 端口时,客户端就会收到服务端的回应,输出`Hello World!`
### 1.2 基于 Linux 的文件操作
讨论套接字的过程中突然谈及文件也许有些奇怪。但是对于 Linux 而言socket 操作与文件操作没有区别,因而有必要详细了解文件。在 Linux 世界里socket 也被认为是文件的一种,因此在网络数据传输过程中自然可以使用 I/O 的相关函数。Windows 与 Linux 不同,是要区分 socket 和文件的。因此在 Windows 中需要调用特殊的数据传输相关函数。
讨论套接字的过程中突然谈及文件也许有些奇怪。但是对于 Linux 而言socket 操作与文件操作没有区别,因而有必要详细了解文件。在 Linux 世界里socket 也被认为是文件的一种,因此在网络数据传输过程中自然可以使用 I/O 的相关函数。Windows 与 Linux 不同,是要区分套接字和文件的。因此在 Windows 中需要调用特殊的数据传输相关函数。
#### 1.2.1 底层访问和文件描述符
@@ -126,7 +126,7 @@ gcc hello_client.c -o hclient
文件描述符也被称为「文件句柄」,但是「句柄」主要是 Windows 中的术语。因此,在本书中如果涉及 Windows 平台将使用「句柄」,如果是 Linux 将使用「描述符」。
#### 1.2.2 打开文件:
#### 1.2.2 打开文件
```c
#include <sys/types.h>
@@ -229,7 +229,7 @@ file data: Let's go!
#### 1.2.6 文件描述符与套接字
下面将同时创建文件和套接字,并用整数型比较返回的文件描述符的值.
下面将同时创建文件和套接字,并用整数型比较返回的文件描述符的值
代码见:[fd_seri.c](fd_seri.c)
@@ -244,8 +244,8 @@ gcc fd_seri.c -o fds
```
file descriptor 1: 3
file descriptor 2: 15
file descriptor 3: 16
file descriptor 2: 4
file descriptor 3: 5
```
### 1.3 基于 Windows 平台的实现
@@ -262,17 +262,17 @@ file descriptor 3: 16
1. 套接字在网络编程中的作用是什么?为何称它为套接字?
> 操作系统会提供「套接字」socket的部件套接字是网络数据传输用的软件设备因此「网络编程」也叫「套接字编程」。「套接字」是用来连接网络的工具。
> 操作系统会提供「套接字」socket的部件套接字是网络数据传输用的软件设备因此「网络编程」也叫「套接字编程」。「套接字」是 socket 的中文译名socket 原意为「插座、插口」,在网络编程中引申为数据通信的连接端点,即用来连接网络的工具。
2. 在服务器端创建套接字以后,会依次调用 listen 函数和 accept 函数。请比较二者作用。
> 答:调用 listen 函数将套接字转换成可受连接状态(监听),调用 accept 函数受理连接请求。如果在没有连接请求的情况下调用该函数,则不会返回,直到有连接请求为止。
> 答:调用 listen 函数将套接字转换成可受连接状态(监听),调用 accept 函数受理连接请求。如果在没有连接请求的情况下调用该函数,则不会返回,直到有连接请求为止。
3. Linux 中,对套接字数据进行 I/O 时可以直接使用文件 I/O 相关函数;而在 Windows 中则不可以。原因为何?
> 答:在 Linux 中套接字socket被视为文件的一种遵循「一切皆文件」的设计哲学。因此可以使用标准的文件 I/O 函数(如 `read`、`write`、`close`)对套接字进行操作。而在 Windows 中,套接字和文件是区分开的,套接字操作需要使用专门的 Winsock 函数(如 `send`、`recv`、`closesocket`),不使用文件 I/O 函数(如 `ReadFile`、`WriteFile`)直接操作套接字。
> 答:在 Linux 中套接字socket被视为文件的一种遵循「一切皆文件」的设计哲学。因此可以使用标准的文件 I/O 函数(如 `read`、`write`、`close`)对套接字进行操作。而在 Windows 中,套接字和文件是区分开的,套接字操作需要使用专门的 Winsock 函数(如 `send`、`recv`、`closesocket`通常不使用文件 I/O 函数(如 `ReadFile`、`WriteFile`)直接操作套接字。
4. 创建套接字后一般会给分配地址,为什么?为了完成地址分配需要调用哪个函数?
4. 创建套接字后一般会给分配地址,为什么?为了完成地址分配需要调用哪个函数?
> 套接字被创建之后只有为其分配了IP地址和端口号后客户端才能够通过IP地址及端口号与服务器端建立连接需要调用 bind 函数来完成地址分配。

View File

@@ -8,7 +8,7 @@ int main()
int fd1, fd2, fd3;
//创建一个文件和两个套接字
fd1 = socket(PF_INET, SOCK_STREAM, 0);
fd2 = open("test.dat", O_CREAT | O_WRONLY | O_TRUNC);
fd2 = open("test.dat", O_CREAT | O_WRONLY | O_TRUNC, 0644);
fd3 = socket(PF_INET, SOCK_DGRAM, 0);
//输出之前创建的文件描述符的整数值
printf("file descriptor 1: %d\n", fd1);

View File

@@ -9,7 +9,7 @@ int main()
int fd;
char buf[] = "Let's go!\n";
// O_CREAT | O_WRONLY | O_TRUNC 是文件打开模式,将创建新文件,并且只能写。如存在 data.txt 文件,则清空文件中的全部数据。
fd = open("data.txt", O_CREAT | O_WRONLY | O_TRUNC);
fd = open("data.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1)
error_handling("open() error!");
printf("file descriptor: %d \n", fd);

View File

@@ -15,8 +15,10 @@ int main()
error_handling("open() error!");
printf("file descriptor: %d \n", fd);
if (read(fd, buf, sizeof(buf)) == -1)
ssize_t read_cnt = read(fd, buf, sizeof(buf) - 1);
if (read_cnt == -1)
error_handling("read() error!");
buf[read_cnt] = '\0';
printf("file data: %s", buf);
close(fd);
return 0;