实验任务A

    假如你是facebook(meta)公司的一个网络架构师,负责在波士顿郊区建一个机房,服务整个美国东北部的用户,支撑用户发动态、发图、发视频、发评论等功能。现在请你估计机房的大致规模,设计网络结构,并优化L2/L3的传输协议。

1. 估算机房大致规模

**a) 用户数量估计:**根据美国东北部地区的人口统计数据和互联网普及率,假设该地区有400万活跃用户。
**b) 每月消耗数据量估计:**假设每个用户每天平均浏览20条说说(包含动态和发图)和20个视频,每条说说消耗1MB,每个视频消耗20MB。每月按30天计算。则每个用户每月产生的数据量为:(20条说说x1MB+20个视频x10MB)x30天=6600MB因此,整个美国东北部地区的用户每月产生的数据量为:400万用户x6600MB=25177PB。
**c) 网络流量估计:**根据用户行为和数据传输需求,假设每个用户每天平均访问Facebook平台30分钟,期间上传和下载数据的平均速率为100Mbps。因此,整个美国东北部地区的用户每天的网络流量为:400w用户x100Mbps=400Gbps。

2. 设计网络架构

a) 核心网络:建议采用分层设计,其中核心网络层负责连接数据中心和用户边缘。在机房内部,可以采用高速交换机构建一个可靠的核心网络,提供高带宽和低延迟的连接。
b) 边缘网络:边缘网络层负责连接用户设备和核心网络。可以使用多台路由器和交换机来构建冗余的边缘网络,确保网络可靠性和负载均衡。
c) 用户接入:用户接入层通过无线和有线连接提供对用户的接入。建议部署无线接入点(如Wi-Fi)以覆盖机房范围内的用户,并提供有线连接(如光纤)以满足对高速、稳定连接的需求。
    总的来说,采用分布式架构,将不同机房之间通过高速网络连接起来,实现用户数据的分布式存储和传输。可以使用路由器、交换机和光纤等设备来构建高速网络。

3. 优化L2/L3

a) L2协议的优化:
链路聚合(Link Aggregation):将多个物理链路绑定成一个逻辑链路,提高链路带宽和冗余性。这可以通过诸如IEEE 802.3ad(LACP)协议来实现。
VLAN划分:使用虚拟局域网(VLAN)来划分网络,实现逻辑隔离和安全性。通过将不同用户、设备或服务隔离到不同的VLAN中,可以减少广播风暴和冲突,提高网络性能和可管理性。
Spanning Tree Protocol (STP) 的优化:STP用于防止网络中的环路,但它可能导致链路冗余时带宽浪费。优化方法包括使用快速收敛的Rapid Spanning Tree Protocol (RSTP) 或者使用基于链路状态的协议,如Multiple Spanning Tree Protocol (MSTP)。
b) L3协议的优化:
动态路由协议的优化:选择适当的动态路由协议,如OSPF(Open Shortest Path First)或BGP(Border Gateway Protocol),并进行合理的配置和调优。这包括优化路由更新间隔、调整Hello和Keepalive定时器,以及使用路由汇总和策略路由等技术。
负载均衡和流量工程:使用负载均衡技术,如等价路径装载(Equal-Cost Multipath)和流量工程,来平衡流量分布和优化网络利用率。这可以通过配置路由器来实现,根据流量类型、目的地或其他条件进行智能流量调度。
QoS(Quality of Service):通过实施QoS策略,对不同类型的流量进行优先级管理和带宽控制。这可以确保关键应用(如视频传输)具有足够的带宽和低延迟,同时限制非关键流量的影响。
Anycast:通过使用Anycast技术,将相同IP地址分配给位于不同位置的多个服务器,以实现负载均衡和减少延迟。用户的请求将被转发到最近的服务器,提供更快的响应速度。

实验任务B

    假如你是google公司的一个网络架构师,负责欧洲数据中心和美国数据中心之间的数据传输,主要服务搜索业务的需求。数据中心内的网络不用你负责,需要你估计的是欧美之间传输的带宽,并优化长距离大数据量传输的协议。

1. 带宽估计

    欧美之间的数据传输带宽主要依赖于跨大西洋的海底光缆网络。这些海底光缆通过大西洋连接欧洲和美国,承载着跨洋数据传输的主要负载。欧美之间的传输带宽通常是以千兆位(Gbps)或更高的速度进行衡量。其中,MEREA,其长约6600公里,传输速度高达160Tbps。
    Google曾经自己投资建设专属于自己的光缆网络系统,例如跨大西洋的”Dunant”光缆,以满足其数据传输需求。Dunant 光缆于2020年投入使用,连接了美国弗吉尼亚州和法国法兰西岛之间。该光缆采用了先进的光纤技术,预计提供高达250Tbps的传输能力。

2.优化长距离大数据传输的协议

    我选择对UDP协议进行优化。TCP协议是一种可靠的协议,但在高延迟和高带宽的网络中,其性能可能会受到限制。因此,我们可以考虑使用UDP协议,它可以提供更高的吞吐量和更低的延迟。但是,UDP协议不是可靠的协议,因此我们需要在应用层实现数据包的确认和重传机制,以确保数据的可靠传输。
1) 第一种优化方法:
    首先将大数据分割成较小的数据包(分片),以适应UDP协议的最大传输单元(MTU)限制。在接收端对接收到的分片进行重组,恢复原始的大数据。然后,实现基于时间戳的流量控制机制,控制数据发送速率,避免过多数据同时发送导致拥塞。并且引入前向纠错机制(Forward Error Correction, FEC),在发送端添加冗余数据,以允许接收端在接收到部分丢失或损坏的数据包时进行纠错。
在发送前对数据进行压缩,减少传输数据量。选择适当的压缩算法,权衡压缩率和压缩/解压缩开销。因为UDP是不可靠的传输协议,所以需要引入校验和机制,以确保数据的完整性。接收端在接收到数据后验证校验和,如有错误,请求发送端进行重传。
    同时,使用多条路径进行数据传输,通过负载均衡技术将数据分发到多个路径上,提高传输效率和带宽利用率。通过使用数据预加载、预连接或预分配资源等技术,减少传输开始的等待时间,从而减少传输延迟。
2) 第二种优化方法
**a) 拥塞控制: **
    包括速率控制和窗口控制。其中速率控制是主要的,用于调整发送包的周期,基于效率、公平和稳定性的综合考虑获得一个发送周期的初始经验值。当周期内丢包率小于阀值时,下一个周期内发送的包将增长。我们可以使用DAIMD算法。
窗口控制是辅助性的,用于动态的限制 NAK 包的数量。速率控制用于快速的达到带宽的最大阀值、快速的丢包恢复及协议内的公平性。窗口控制帮助减少包的丢失和振荡,避免拥塞。这两种机制共同作用保障了传输的高性能和稳定性,提高了效率及公平性。
b) 带宽估计
    使用对包(PP – Packet pair)的机制来估计 带宽值。即每 16 个包为一组,最后一个是对包,即 发送方不用等到下一个发送周期内再发送。接收方接 收到对包后对其到达时间进行记录,可结合上次记录 的值计算出链路的带宽(计算的方法称为中值过滤法), 并在下次 ACK 中进行反馈。
c) 可靠传输
    发送端和接收端都维护一个丢包链表,用于记录 丢包的序号。接收方周期性的发送 ACK 和 NAK 通告 发送方丢包信息。从而实现数据传输的可靠性。根据 拥塞发生严重时丢包是大量连续的特点,使用了较好 的数据压缩机制,一个丢失链表中的节点表示一个丢 包事件,其丢包报告中存放的 32-bit 信息如果首位是 0,则只代表其自身的包丢失,若是 1 则表明从当前 的包序号开始到下一个记录中的所有包都丢失了。例 如:0x00000002, 0x80000006, 0x00000008, 0x0000000B 则表明序号为 2,6,7,8,11 的包都丢失。[1]

实验任务C

    针对(1)和(2)的场景,写出你的分析和协议设计报告,通过代码实现协议,并在能找到的实验场景进行原型实验。例如(1)就可以试试看你在寝室wifi下怎么实现高效的1对多发送,(2)可以试试看在两个寝室之间实现高速传输。

1. 针对任务A:

以下是使用优化后的L2/L3协议在寝室wife下高效的实现1对多发送代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
int main() {
int sockfd;
struct sockaddr_in multicast_addr;
char *message = "Hello, multicast!";

// 创建套接字
if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}

// 设置多播组地址
memset(&multicast_addr, 0, sizeof(multicast_addr));
multicast_addr.sin_family = AF_INET;
multicast_addr.sin_addr.s_addr = inet_addr(MULTICAST_GROUP);
multicast_addr.sin_port = htons(PORT);

// 设置套接字选项,启用多播
int enable = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) {
perror("setsockopt failed");
exit(EXIT_FAILURE);
}

// 绑定套接字到指定地址和端口
if (bind(sockfd, (struct sockaddr*)&multicast_addr, sizeof(multicast_addr)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}

// 加入多播组
struct ip_mreq multicast_request;
multicast_request.imr_multiaddr.s_addr = inet_addr(MULTICAST_GROUP);
multicast_request.imr_interface.s_addr = htonl(INADDR_ANY);
if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&multicast_request, sizeof(multicast_request)) < 0) {
perror("setsockopt failed");
exit(EXIT_FAILURE);
}

// 发送数据
while (1) {
if (sendto(sockfd, message, strlen(message), 0, (struct sockaddr*)&multicast_addr, sizeof(multicast_addr)) < 0) {
perror("sendto failed");
exit(EXIT_FAILURE);
}
sleep(1); // 间隔1秒发送一次数据
}

// 关闭套接字
close(sockfd);

return 0;
}

2. 针对任务B:

下面是对UDP协议优化后的在两个寝室之间高速传输的主要代码,在WSL2下运行。
发送方代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
void sender(int sockfd, struct sockaddr* dest_addr, socklen_t dest_addr_len) {
uint32_t seq_num = 0;
Packet packet;
int base = 0; // 窗口的起始位置
int next_seq_num = 0; // 下一个待发送的包的序号
int window_count = 0; // 窗口内包的数量

while (1) {
// 发送窗口内的包
while (window_count < WINDOW_SIZE && next_seq_num < base + WINDOW_SIZE) {
// 构建并发送数据包
packet.seq_num = next_seq_num;
packet.size = sizeof(packet.data);
// 在这里填充packet.data的实际数据

if (rand() / (double)RAND_MAX >= LOSS_RATE) {
// 模拟丢包
sendto(sockfd, &packet, sizeof(packet), 0, dest_addr, dest_addr_len);
}

next_seq_num++;
window_count++;
}

// 接收ACK/NAK
fd_set fds;
FD_ZERO(&fds);
FD_SET(sockfd, &fds);

struct timeval timeout;
timeout.tv_sec = 1; // 设置超时时间
timeout.tv_usec = 0;

if (select(sockfd + 1, &fds, NULL, NULL, &timeout) > 0) {
// 有可读数据
Packet ack_packet;
recvfrom(sockfd, &ack_packet, sizeof(ack_packet), 0, NULL, NULL);

if (ack_packet.seq_num >= base) {
// 更新窗口的起始位置和窗口内包的数量
window_count -= ack_packet.seq_num - base + 1;
base = ack_packet.seq_num + 1;
}
} else {
// 超时,重传窗口内的包
for (int i = base; i < base + window_count; i++) {
packet.seq_num = i;
packet.size = sizeof(packet.data);
// 在这里填充packet.data的实际数据

sendto(sockfd, &packet, sizeof(packet), 0, dest_addr, dest_addr_len);
}
}

// 传输完成
if (base >= next_seq_num)
break;
}
}

接收方代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void receiver(int sockfd, struct sockaddr* src_addr, socklen_t src_addr_len) {
Packet packet;
uint32_t expected_seq_num = 0;

while (1) {
// 接收数据包
recvfrom(sockfd, &packet, sizeof(packet), 0, NULL, NULL);

if (packet.seq_num == expected_seq_num) {
// 发送ACK
sendto(sockfd, &packet, sizeof(packet), 0, src_addr, src_addr_len);

// 处理数据包
// 在这里处理接收到的数据包

expected_seq_num++;
} else {
// 发送NAK
Packet nak_packet;
nak_packet.seq_num = expected_seq_num - 1;
nak_packet.size = 0; // 表示NAK

sendto(sockfd, &nak_packet, sizeof(nak_packet), 0, src_addr, src_addr_len);
}
}
}

实验总结

    本次实验感觉还是比较难的,有的协议看不太懂,不知道如何优化,在查阅了资料后,找到了类似的优化方法。做的不足的地方还有很多,我根据自己的能力尽可能完成实验,其中还有很多不足的地方需要改进。这是本学期最后一个实验,希望以后能对计算机网络有更深刻的理解和认识。

参考文献

[1] 张磊,葛敬国,李俊.长距离高带宽环境下UDT协议的分析与应用[J].计算机系统应用,2009,18(10):148-151.
[2] Floyd S. High speed TCP for large congestion window. RFC 3646, IETF, Dec 2003
[3] https://blog.csdn.net/guanminwei/article/details/119105557
[4] 李士宁,王猛,赵磊.长距离高带宽环境下的TCP拥塞控制[J].无线通信技术,2006(01):47-50
[5] 丁一,汪永琳.长距离高带宽环境下FAST TCP的结构与性能分析[J].科技信息(学术研究),2007(30):1-3