mirror of
https://github.com/riba2534/TCP-IP-NetworkNote.git
synced 2026-06-30 09:56:04 +08:00
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:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 套接字的结束过程也非常优雅。如果对方还有数据需要传
|
||||
|
||||

|
||||
|
||||
图中数据包内的 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 第一次发出的 ACK,B 会重传 FIN,主机 A 收到后再次发送 ACK 5001 予以确认。
|
||||
|
||||
### 5.3 基于 Windows 的实现
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user