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:
@@ -4,21 +4,21 @@
|
||||
|
||||
### 14.1 多播
|
||||
|
||||
多播(Multicast)方式的数据传输是基于 UDP 完成的。因此 ,与 UDP 服务器端/客户端的实现方式非常接近。区别在于,UDP 数据传输以单一目标进行,而多播数据同时传递到加入(注册)特定组的大量主机。换言之,采用多播方式时,可以同时向多个主机传递数据。
|
||||
多播(Multicast)方式的数据传输是基于 UDP 完成的。因此,与 UDP 服务器端/客户端的实现方式非常接近。区别在于,UDP 数据传输以单一目标进行,而多播数据同时传递到加入(注册)特定组的大量主机。换言之,采用多播方式时,可以同时向多个主机传递数据。
|
||||
|
||||
#### 14.1.1 多播的数据传输方式以及流量方面的优点
|
||||
|
||||
多播的数据传输特点可整理如下:
|
||||
|
||||
- 多播服务器端针对特定多播组,只发送 1 次数据。
|
||||
- 即使只发送 1 次数据,但该组内的所有客户端都会接收数据
|
||||
- 即使只发送 1 次数据,该组内的所有客户端也都会接收数据。
|
||||
- 多播组数可以在 IP 地址范围内任意增加
|
||||
|
||||
多播组是 D 类IP地址(224.0.0.0~239.255.255.255),「加入多播组」可以理解为通过程序完成如下声明:
|
||||
|
||||
> 在 D 类IP地址中,我希望接收发往目标 239.234.218.234 的多播数据
|
||||
|
||||
多播是基于 UDP 完成的,也就是说,多播数据包的格式与 UDP 数据包相同。只是与一般的 UDP 数据包不同。向网络传递 1 个多播数据包时,路由器将复制该数据包并传递到多个主机。像这样,多播需要借助路由器完成。如图所示:
|
||||
多播是基于 UDP 完成的,也就是说,多播数据包的格式与 UDP 数据包相同。只是与一般的 UDP 数据包不同,向网络传递 1 个多播数据包时,路由器将复制该数据包并传递到多个主机。像这样,多播需要借助路由器完成。如图所示:
|
||||
|
||||

|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
#### 14.1.2 路由(Routing)和 TTL(Time to Live,生存时间),以及加入组的办法
|
||||
|
||||
为了传递多播数据包,必须设置 TTL 。TTL 是 Time to Live的简写,是决定「数据包传递距离」的主要因素。TTL 用整数表示,并且每经过一个路由器就减一。TTL 变为 0 时,该数据包就无法再被传递,只能销毁。因此,TTL 的值设置过大将影响网络流量。当然,设置过小,也无法传递到目标。
|
||||
为了传递多播数据包,必须设置 TTL。TTL 是 Time to Live 的简写,是决定「数据包传递距离」的主要因素。TTL 用整数表示,并且每经过一个路由器就减一。TTL 变为 0 时,该数据包就无法再被传递,只能销毁。因此,TTL 的值设置过大将影响网络流量。当然,设置过小,也无法传递到目标。
|
||||
|
||||

|
||||
|
||||
@@ -76,8 +76,8 @@ struct ip_mreq
|
||||
|
||||
下面是两个代码:
|
||||
|
||||
- [news_sender.c](https://github.com/riba2534/TCP-IP-NetworkNote/blob/master/ch14/news_sender.c)
|
||||
- [news_receiver.c](https://github.com/riba2534/TCP-IP-NetworkNote/blob/master/ch14/news_receiver.c)
|
||||
- [news_sender.c](news_sender.c)
|
||||
- [news_receiver.c](news_receiver.c)
|
||||
|
||||
编译运行:
|
||||
|
||||
@@ -92,11 +92,11 @@ gcc news_receiver.c -o receiver
|
||||
|
||||

|
||||
|
||||
通过结果可以看出,使用 sender 多播信息,通过 receiver 接收广播,如果延迟运行 receiver 将无法接受之前发送的信息。
|
||||
通过结果可以看出,使用 sender 多播信息,通过 receiver 接收多播数据,如果延迟运行 receiver 将无法接收之前发送的信息。
|
||||
|
||||
### 14.2 广播
|
||||
|
||||
广播(Broadcast)在「一次性向多个主机发送数据」这一点上与多播类似,但传输数据的范围有区别。多播即使在跨越不同网络的情况下,只要加入多播组就能接受数据。相反,广播只能向同一网络中的主机传输数据。
|
||||
广播(Broadcast)在「一次性向多个主机发送数据」这一点上与多播类似,但传输数据的范围有区别。多播即使在跨越不同网络的情况下,只要加入多播组就能接收数据。相反,广播只能向同一网络中的主机传输数据。
|
||||
|
||||
#### 14.2.1 广播的理解和实现方法
|
||||
|
||||
@@ -109,7 +109,7 @@ gcc news_receiver.c -o receiver
|
||||
|
||||
反之,本地广播中使用的IP地址限定为 255.255.255.255 。例如,192.32.24 网络中的主机向 255.255.255.255 传输数据时,数据将传输到 192.32.24 网络中所有主机。
|
||||
|
||||
**数据通信中使用的IP地址是与 UDP 示例的唯一区别。默认生成的套接字会阻止广播,因此,只需通过如下代码更改默认设置。**
|
||||
**广播与普通 UDP 示例的区别在于:目标 IP 地址使用广播地址,且需通过 SO_BROADCAST 选项启用广播。默认生成的套接字会阻止广播,因此需要通过如下代码更改默认设置。**
|
||||
|
||||
```c
|
||||
int send_sock;
|
||||
@@ -121,12 +121,12 @@ setsockopt(send_sock,SOL_SOCKET,SO_BROADCAST,(void*)&bcast,sizeof(bcast));
|
||||
...
|
||||
```
|
||||
|
||||
### 14.2.2 实现广播数据的 Sender 和 Receiver
|
||||
#### 14.2.2 实现广播数据的 Sender 和 Receiver
|
||||
|
||||
下面是广播数据的 Sender 和 Receiver的代码:
|
||||
|
||||
- [news_sender_brd.c](https://github.com/riba2534/TCP-IP-NetworkNote/blob/master/ch14/news_sender_brd.c)
|
||||
- [news_receiver_brd.c](https://github.com/riba2534/TCP-IP-NetworkNote/blob/master/ch14/news_receiver_brd.c)
|
||||
- [news_sender_brd.c](news_sender_brd.c)
|
||||
- [news_receiver_brd.c](news_receiver_brd.c)
|
||||
|
||||
编译运行:
|
||||
|
||||
@@ -201,7 +201,7 @@ setsockopt(send_sock, SOL_SOCKET, SO_BROADCAST, (char*)&bcast, sizeof(bcast));
|
||||
|
||||
1. 头文件:Windows 使用 `winsock2.h`,Linux 使用 `sys/socket.h` 等头文件
|
||||
2. 初始化:Windows 需要调用 `WSAStartup` 初始化 Winsock,使用完后调用 `WSACleanup`
|
||||
3. 套接字类型:Windows 使用 `SOCKET` 类型(实际是 `unsigned __int64`),Linux 使用 `int`
|
||||
3. 套接字类型:Windows 使用 `SOCKET` 类型(`UINT_PTR`,32 位系统为 `unsigned int`,64 位系统为 `unsigned __int64`),Linux 使用 `int`
|
||||
4. 关闭套接字:Windows 使用 `closesocket`,Linux 使用 `close`
|
||||
5. 指针类型转换:Windows 下 `setsockopt` 的第四个参数通常转换为 `char*`,Linux 下转换为 `void*`
|
||||
|
||||
@@ -215,20 +215,25 @@ setsockopt(send_sock, SOL_SOCKET, SO_BROADCAST, (char*)&bcast, sizeof(bcast));
|
||||
|
||||
2. **多播与广播的异同点是什么?请从数据通信的角度进行说明**。
|
||||
|
||||
答:在「一次性向多个主机发送数据」这一点上与多播类似,但传输的数据范围有区别。多播即使在跨越不同网络的情况下,只要加入多播组就能接受数据。相反,广播只能向同一网络中的主机传输数据。
|
||||
答:在「一次性向多个主机发送数据」这一点上与多播类似,但传输的数据范围有区别。多播即使在跨越不同网络的情况下,只要加入多播组就能接收数据。相反,广播只能向同一网络中的主机传输数据。
|
||||
|
||||
3. **下面关于多播的说法描述错误的是**?
|
||||
3. **下面关于多播的说法描述错误的是?**
|
||||
|
||||
1. 多播是用来向加入多播组的所有主机传输数据的协议
|
||||
2. 主机连接到同一网络才能加入到多播组,也就是说,多播组无法跨越多个网络
|
||||
3. 能够加入多播组的主机数并无限制,但只能有 1 个主机(Sender)向该组发送数据
|
||||
4. 多播时使用的套接字是 UDP 套接字,因为多播是基于 UDP 进行数据通信的
|
||||
|
||||
答:第 2 项描述错误。正确说明如下:
|
||||
|
||||
1. 多播是用来向加入多播组的所有主机传输数据的协议
|
||||
2. ~~主机连接到同一网络才能加入到多播组,也就是说,多播组无法跨越多个网络~~(错误。多播可以跨越多个网络,只要路由器支持多播功能,主机就可以加入跨网络的多播组。即使路由器不支持,也可以通过隧道技术实现。)
|
||||
3. 能够加入多播组的主机数并无限制,但只能有 1 个主机(Sender)向该组发送数据
|
||||
3. 能够加入多播组的主机数并无限制,向该组发送数据的主机数也无限制(任何主机均可向多播组地址发送数据,且无需先加入该组)
|
||||
4. 多播时使用的套接字是 UDP 套接字,因为多播是基于 UDP 进行数据通信的
|
||||
|
||||
4. **多播也对网络流量有利,请比较 TCP 交换方式解释其原因**
|
||||
|
||||
答:TCP 是必须建立一对一的连接,如果要向1000个主机发送文件,就得传递1000次。但是此时用多播方式传输数据,就只需要发送一次。
|
||||
答:TCP 必须建立一对一的连接,如果要向 1000 个主机发送文件,就得传递 1000 次。但是此时用多播方式传输数据,就只需要发送一次。
|
||||
|
||||
5. **多播方式的数据通信需要 MBone 虚拟网络。换言之,MBone 是用于多播的网络,但它是虚拟网络。请解释此处的「虚拟网络」**
|
||||
|
||||
|
||||
@@ -31,12 +31,12 @@ int main(int argc, char *argv[])
|
||||
//初始化结构体
|
||||
join_adr.imr_multiaddr.s_addr = inet_addr(argv[1]); //多播组地址
|
||||
join_adr.imr_interface.s_addr = htonl(INADDR_ANY); //待加入的IP地址
|
||||
//利用套接字选项 IP_ADD_MEMBERSHIP 加入多播组,完成了接受指定的多播组数据的所有准备
|
||||
//利用套接字选项 IP_ADD_MEMBERSHIP 加入多播组,完成了接收指定的多播组数据的所有准备
|
||||
setsockopt(recv_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&join_adr, sizeof(join_adr));
|
||||
|
||||
while (1)
|
||||
{
|
||||
//通过 recvfrom 函数接受多播数据。如果不需要知道传输数据的主机地址信息,可以向recvfrom函数的第5 6参数分贝传入 NULL 0
|
||||
//通过 recvfrom 函数接收多播数据。如果不需要知道传输数据的主机地址信息,可以向 recvfrom 函数的第 5、6 个参数分别传入 NULL、0
|
||||
str_len = recvfrom(recv_sock, buf, BUF_SIZE - 1, 0, NULL, 0);
|
||||
if (str_len < 0)
|
||||
break;
|
||||
|
||||
@@ -29,7 +29,7 @@ int main(int argc, char *argv[])
|
||||
error_handling("bind() error");
|
||||
while (1)
|
||||
{
|
||||
//通过 recvfrom 函数接受数据。如果不需要知道传输数据的主机地址信息,可以向recvfrom函数的第5 6参数分贝传入 NULL 0
|
||||
//通过 recvfrom 函数接收数据。如果不需要知道传输数据的主机地址信息,可以向 recvfrom 函数的第 5、6 个参数分别传入 NULL、0
|
||||
str_len = recvfrom(recv_sock, buf, BUF_SIZE - 1, 0, NULL, 0);
|
||||
if (str_len < 0)
|
||||
break;
|
||||
|
||||
@@ -31,9 +31,8 @@ int main(int argc, char *argv[])
|
||||
if ((fp = fopen("news.txt", "r")) == NULL)
|
||||
error_handling("fopen() error");
|
||||
|
||||
while (!feof(fp)) //如果文件没结束就返回0
|
||||
while (fgets(buf, BUF_SIZE, fp) != NULL) //逐行读取,读到文件末尾返回 NULL
|
||||
{
|
||||
fgets(buf, BUF_SIZE, fp);
|
||||
sendto(send_sock, buf, strlen(buf), 0, (struct sockaddr *)&mul_adr, sizeof(mul_adr));
|
||||
sleep(2);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ int main(int argc, char *argv[])
|
||||
int so_brd = 1;
|
||||
if (argc != 3)
|
||||
{
|
||||
printf("Usage : %s <GroupIP> <PORT>\n", argv[0]);
|
||||
printf("Usage : %s <BroadcastIP> <PORT>\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
send_sock = socket(PF_INET, SOCK_DGRAM, 0); //创建 UDP 套接字
|
||||
@@ -29,9 +29,8 @@ int main(int argc, char *argv[])
|
||||
if ((fp = fopen("news.txt", "r")) == NULL)
|
||||
error_handling("fopen() error");
|
||||
|
||||
while (!feof(fp)) //如果文件没结束就返回0
|
||||
while (fgets(buf, BUF_SIZE, fp) != NULL) //逐行读取,读到文件末尾返回 NULL
|
||||
{
|
||||
fgets(buf, BUF_SIZE, fp);
|
||||
sendto(send_sock, buf, strlen(buf), 0, (struct sockaddr *)&broad_adr, sizeof(broad_adr));
|
||||
sleep(2);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user