docs: 全面审查并修正所有章节文档内容

- 修正各章节中的错别字和术语错误(如 IPv4 大写规范、接收/接受区分等)
- 补充和完善部分习题答案
- 优化技术描述的准确性和专业性
- 合并所有章节内容到根 README.md

新增文件:
- CLAUDE.md: 项目开发指南
- .claude/agents/content-reviewer.md: 内容审查 subagent
- .claude/agents/merger.md: 文档合并 subagent

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
riba2534
2026-01-05 15:28:29 +08:00
parent f163bca3a9
commit d44ecdf807
23 changed files with 1933 additions and 825 deletions

View File

@@ -6,7 +6,7 @@
#### 13.1.1 Linux 中的 send & recv
首先看 sned 函数定义:
首先看 send 函数定义:
```c
#include <sys/socket.h>
@@ -27,8 +27,8 @@ flags: 传输数据时指定的可选项信息
ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);
/*
成功时返回接收的字节数(收到 EOF 返回 0失败时返回 -1
sockfd: 表示数据接对象的连接的套接字文件描述符
buf: 保存接数据的缓冲地址值
sockfd: 表示数据接对象的连接的套接字文件描述符
buf: 保存接数据的缓冲地址值
nbytes: 可接收的最大字节数
flags: 接收数据时指定的可选项参数
*/
@@ -41,7 +41,7 @@ send & recv 函数的可选项意义:
| 可选项Option | 含义 | send | recv |
| ---------------- | ------------------------------------------------------------ | ---- | ---- |
| MSG_OOB | 用于传输带外数据Out-of-band data | O | O |
| MSG_PEEK | 验证输入缓冲中是否存在接的数据 | X | O |
| MSG_PEEK | 验证输入缓冲中是否存在接的数据 | X | O |
| MSG_DONTROUTE | 数据传输过程中不参照本地路由Routing在本地Local网络中寻找目的地 | O | X |
| MSG_DONTWAIT | 调用 I/O 函数时不阻塞用于使用非阻塞Non-blockingI/O | O | O |
| MSG_WAITALL | 防止函数返回,直到接收到全部请求的字节数 | X | O |
@@ -121,14 +121,14 @@ TCP 数据包实际包含更多信息。TCP 头部包含如下两种信息:
#### 13.1.4 检查输入缓冲
同时设置 MSG_PEEK 选项和 MSG_DONTWAIT 选项,以验证输入缓冲是否存在接收的数据。设置 MSG_PEEK 选项并调用 recv 函数时,即使读取了输入缓冲的数据也不会删除。因此,该选项通常与 MSG_DONTWAIT 合,用于以非阻塞方式验证待读数据存在与否。下面的示例是二者的含义:
同时设置 MSG_PEEK 选项和 MSG_DONTWAIT 选项,以验证输入缓冲是否存在接收的数据。设置 MSG_PEEK 选项并调用 recv 函数时,即使读取了输入缓冲的数据也不会删除。因此,该选项通常与 MSG_DONTWAIT 合,用于以非阻塞方式验证待读数据存在与否。下面的示例是二者的含义:
- [peek_recv.c](https://github.com/riba2534/TCP-IP-NetworkNote/blob/master/ch13/peek_recv.c)
- [peek_send.c](https://github.com/riba2534/TCP-IP-NetworkNote/blob/master/ch13/peek_send.c)
编译运行:
```
```shell
gcc peek_recv.c -o recv
gcc peek_send.c -o send
./recv 9190
@@ -156,7 +156,7 @@ readv & writev 函数的功能可概括如下:
ssize_t writev(int filedes, const struct iovec *iov, int iovcnt);
/*
成功时返回发送的字节数,失败时返回 -1
filedes: 表示数据传输对象的套接字文件描述符。但该函数并不仅限于套接字,因此,可以像 read 一样向其传递文件或标准输出描述符.
filedes: 表示数据传输对象的套接字文件描述符。但该函数并不仅限于套接字,因此,可以像 read 一样向其传递文件或标准输出描述符.
iov: iovec 结构体数组的地址值,结构体 iovec 中包含待发送数据的位置和大小信息
iovcnt: 向第二个参数传递数组长度
*/
@@ -208,7 +208,7 @@ int main(int argc, char *argv[])
```shell
gcc writev.c -o writev
./writevi
./writev
```
结果:
@@ -222,10 +222,10 @@ Write bytes: 7
```c
#include <sys/uio.h>
ssize_t readv(int filedes, const struct iovc *iov, int iovcnt);
ssize_t readv(int filedes, const struct iovec *iov, int iovcnt);
/*
成功时返回接收的字节数,失败时返回 -1
filedes: 表示数据传输对象的套接字文件描述符。但该函数并不仅限于套接字,因此,可以像 write 一样向其传递文件或标准输出描述符.
filedes: 表示数据传输对象的套接字文件描述符。但该函数并不仅限于套接字,因此,可以像 write 一样向其传递文件或标准输出描述符.
iov: iovec 结构体数组的地址值,结构体 iovec 中包含待数据保存的位置和大小信息
iovcnt: 第二个参数中数组的长度
*/
@@ -288,12 +288,63 @@ gcc readv.c -o rv
### 13.3 基于 Windows 的实现
暂略
Windows 下的 Winsock 提供了与 Linux 类似的 I/O 函数,主要有以下区别:
#### 13.3.1 send 和 recv 函数
Winsock 中的 send 和 recv 函数原型与 Linux 基本一致:
```c
#include <winsock2.h>
int send(SOCKET s, const char *buf, int len, int flags);
int recv(SOCKET s, char *buf, int len, int flags);
```
主要区别:
- 参数类型Linux 使用 `int sockfd`Windows 使用 `SOCKET s`(实际是 `typedef UINT_PTR SOCKET;`
- 缓冲区类型Linux 使用 `void *`Windows 使用 `char *`
- 返回值Linux 返回 `ssize_t`Windows 返回 `int`(失败时返回 `SOCKET_ERROR`,即 -1
#### 13.3.2 WSASend、WSARecv 和 WSARecvEx
Windows 还提供了扩展版本的异步 I/O 函数:
```c
int WSASend(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
LPDWORD lpNumberOfBytesSent, DWORD dwFlags,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
int WSARecv(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
```
这些函数支持重叠 I/OOverlapped I/O和完成端口Completion Port模型适合高性能服务器开发。
#### 13.3.3 可选项差异
Windows 与 Linux 在可选项上存在一些差异:
| 可选项 | Linux | Windows |
| ------ | ----- | ------- |
| MSG_OOB | 支持 | 支持 |
| MSG_PEEK | 支持 | 支持 |
| MSG_DONTWAIT | 支持 | 不支持(需通过 ioctlsocket 设置非阻塞模式) |
| MSG_WAITALL | 支持 | 支持 |
| MSG_PARTIAL | 不支持 | 支持(仅用于流式套接字) |
Windows 不支持 MSG_DONTWAIT需要通过 `ioctlsocket` 函数设置套接字为非阻塞模式:
```c
u_long mode = 1;
ioctlsocket(sock, FIONBIO, &mode);
```
### 13.4 习题
> 以下答案仅代表本人个人观点,可能不是正确答案。
>
1. **下列关于 MSG_OOB 可选项的说法错误的是**
@@ -308,6 +359,25 @@ gcc readv.c -o rv
答:需要传输的数据分别位于不同缓冲(数组)时,需要多次调用 write 函数。此时可通过 1 次 writev 函数调用替代操作,当然会提高效率。同样,需要将输入缓冲中的数据读入不同位置时,可以不必多次调用 read 函数,而是利用 1 次 readv 函数就能大大提高效率。
从 I/O 缓冲的角度来看writev 函数可以将分散的数据整合为一次系统调用,减少用户态与内核态之间的上下文切换次数,同时减少网络数据包的个数(尤其在禁用 Nagle 算法时效果更明显)。
3. **通过 recv 函数验证输入缓冲中是否存在数据时(确认后立即返回时),如何设置 recv 函数最后一个参数中的可选项?分别说明各可选项的含义**
答:使用 MSG_PEEK 来验证输入缓冲中是否存在待接收的数据。各个可选项的意义参见上面对应章节的表格。
答:应同时设置 `MSG_PEEK``MSG_DONTWAIT` 两个可选项。
各可选项的含义:
- **MSG_PEEK**验证输入缓冲中是否存在待接收的数据。设置此选项后recv 函数会读取输入缓冲中的数据但不会将其删除(数据仍保留在缓冲中),可以再次读取。
- **MSG_DONTWAIT**:调用 I/O 函数时不阻塞用于非阻塞Non-blockingI/O。设置此选项后如果输入缓冲中没有数据recv 函数会立即返回错误errno 设为 EAGAIN 或 EWOULDBLOCK而不是阻塞等待。
示例代码:
```c
int len = recv(sockfd, buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT);
if (len > 0) {
// 缓冲中有数据
} else if (len == 0) {
// 连接已关闭
} else {
// 无数据或出错
}
```