【CS144-2021】Stanford 计算机网络课程学习

news2025/4/14 21:36:14

CS144

  • 2019 Fall:https://kangyupl.gitee.io/cs144.github.io/
  • 2020 Fall:https://github.com/lawliet9712/Stanford-CS144-2021
  • 【推荐】2021 Fall:https://github.com/Kiprey/sponge

前前后后弄了半个月,终于啃完 CS144 了,感谢 K大博客,大概弄懂了 80% 吧

Linux 环境

  • Mac VirtualBox 安装 Ubuntu desktop 版:https://www.zhihu.com/tardis/zm/art/109808506
  • 安装 ubuntu server 版:https://blog.csdn.net/weixin_46658699/article/details/114693006
  • VirtualBox 增强工具(方便双向复制):https://blog.csdn.net/zhu_1997/article/details/117814728
  • SSH 连接 VirtualBox Ubuntu 虚拟机:https://www.cnblogs.com/linxiaoxu/p/16260601.html
  • Ubuntu18.04 升级 gcc:https://blog.csdn.net/weixin_44128857/article/details/108554751
  • Sponge 仓库 2021:https://gitee.com/ericps/cs144-2021

Lab0: ByteStream

  • webget: 利用已有 API 获取网页
  • byte_stream: 实现一个有序字节流类(in-order byte stream),使之支持读写、容量控制。可以使用 std::dueue (双端开口)作为「可靠」字节流的底层。这个字节流类似于一个带容量的队列,从一头读,从另一头写。当流中的数据达到容量上限时,便无法再写入新的数据。特别的,写操作被分为了peek和pop两步。peek为从头部开始读取指定数量的字节,pop为弹出指定数量的字节。
  • string_view: https://blog.csdn.net/danshiming/article/details/122573151

Lab1: StreamReassembler

  • git 拉取远程所有分支到本地:for i in git branch -r; do git checkout basename $i && git pull --all; done
  • git 合并分支:参考
  • 内含 ByteStream 没有默认的构造函数为啥可以直接定义,这里面没有设置 ByteStream 的 capacity?
  • 不可靠的流失传输中每条数据可能 reorder、duplicate 等等,TCP 的功能就是使用不可靠的数据包提供可靠的字节流服务,因此需要实现「流重组器」来应对收到的乱序或者重复数据的情况,每条流带有一个 stream_index(类型是uint64_t,理解为不会 wrap),按照顺序重组字节流并送入指定的 ByteStream 中
  • 实现使用 std::map<size_t, std::string> 存放那些还未重组的数据,维护下图中的 first_unassembled 即可,收到一个子串就会调用 push_substring,根据 map 中的内容重组 ByteStream,整个思路就是比较 stream_idx,找到比 stream_idx 小的第一个位置以及比 stream_idx 大的第一个位置(二分查找)

reassembled

Lab2: TCPReceiver

  • make 出错:安装 libpcap-dev 库

  • 三个索引:

    • (relative) seqno:从 ISN 开始,包含 SYN 和 FIN,32 位循环计数(这也是 TCP Header 的一个字段)
    • absolute seqno:从 0 开始,包含 SYN 和 FIN,64 位非循环技术(为什么不会循环,pdf 给出了说明:Transmitting at 100 gigabits/sec, it would take almost 50 years to reach 2^64 bytes. By contrast, it takes only a third of a second to reach 2^32 bytes.)
    • stream_index:从 0 起步,排除 SYN 和 FIN,64 位非循环技术
  • 首先理解 wrapping_intergers 转换过程

    • absolute seqno 转 seqno 比较简单:absolute seq 转 32 位之后直接和 isn 相加即可,溢出自动处理
    • seqno 转 absolute seqno 需要思考一下:需要利用上一次收到的 checkpoint 参考
  • 实现 TCPReceiver

    • segment_received():该函数将会在每次获取到 TCP 报文被调用,完成两个功能
      • 如果接受到 SYN 包就设置 ISN 编号(SYN 和 FIN 包仍然可以携带用户数据并一同传输。同时,同一个数据包下既可以设置 SYN 标志也可以设置 FIN 标志。why ??? 有点不太理解)
      • 将收到的数据直接丢进 stream reassembler,并在接收到 FIN 包时终止数据传输
    • ackno() 返回接收方下一次期望接收到的字节索引,根据 ByteStream 已写字节数得到 absolute seqno (注意如果是 FIN 需要 ++),然后转换成 seqno 即可
    • window_size() 返回接受窗口的大小,也就是 capacity - ByteStream 的 BUFFER_SIZE,可以用于「流量控制

Lab3: TCPSender

  • TCPSender 需要将 ByteStream 中的数据以 TCP 报文形式持续发送给 receiver(利用写好的 TCPSegment 这个类填充其中的头部字段以及 payload 信息)

  • 需要处理 TCPReceiver 传入的 ackno 和 window size,以追踪接收者当前的接收状态,以及检测丢包情况

  • 经过一个超时时间后仍然没有接收到 TCPReceiver 发送的针对某个数据包的 ack 包,则重传对应的原始数据包,主要这里的检测主要是通过 tick 函数实现,不需要使用 timer 相关的系统调用,tick 函数的入参就是上一次调用到现在的时间(ms),因此需要维护一次 全局计时器变量 timecount,另外采用“指数退避”的思想,每次超时之后 timeout *= 2,另外需要维护一个全局的重传次数 retans_count,如果某个报文连续重传次数达到 8 次需要发送 RST 报文终止连接(当然这个是在 Lab4 中实现的)

  • 注意!!!remote_window_size 应该初始化为 1,否则如果「初始」就丢包的话 remote_window_size = 0 不会退避 timeout,另外接收方的 Windows size 为 0,发送方也将按照接收方 window size 为 1 的情况进行处理,持续发包。为了 keep alive

    • 退避的前提的是 window_size > 0,接收方可以接收数据但是网络拥塞了导致还没有收到数据,超时一次超时时长会乘2 (实行网络拥塞控制)
  • 维护一个已经发送但未被确认的 segment 队列 std::queue<std::pair<size_t, TCPSegment>>, 如果 ack_receiver 是收到 ackno 以及 window_size 之后需要检查 queue 以及移除并 reset timer 相关变量

    • 相当于采取累计确认方式通过维护缓存队列重传
    • 根据 ackno --> abs_seq,如果 abs_seq > _next_seqno 直接丢弃并返回,否则从头遍历 queue 移除那些已经确认的 segment

system

Lab4: TCPConnection

  • TCPConnection 需要将 TCPSender 和 TCPReceiver 结合,实现成一个 TCP 终端,同时收发数据。

  • 接受数据端:

    • 如果收到 RST 直接关闭连接,否则交给 TCPReceiver 处理,对其中各个字段解析

    • 收到 ACK 需要向当前自己的 TCPConnection 的 TCPSender 对端的 ackno 和 window_size 信息

      这一步相当重要,因为数据包在网络中以乱序形式发送,因此远程发送给本地的 ackno 存在滞后性。将远程的 ackno 和 window size 附加至发送数据中可以降低这种滞后性,提高 TCP 效率。

  • 发送数据端:

    • 当 TCPSender 从 ByteStream 读取数据组成一个 TCPSegment 放入待发送的队列时,TCPConnection 从其中取出并将其发送(push 到 _segment_out 队列即可)
    • 在发送当前数据包之前,TCPConnection 会获取当前它自己的 TCPReceiver 的 ackno 和 window size(用来表示自己下一次期望接收到 seqno 以及自己的 window_size),将其放置进待发送 TCPSegment 中,并设置其 ACK 标志。
  • TCPConnection 需要检测时间的流逝。它存在一个 tick 函数,该函数将会被操作系统持续调用。当 TCPConnection 的 tick 函数被调用后,它需要

    • 告诉 TCPSender 时间的流逝,让其重新发送丢失的数据包
    • 如果 sender 的「连续重传次数」超过 TCPConfig::MAX RETX ATTEMPTS,发送一个 RST 包终止连接
    • 考虑 TIME_WAIT 状态
  • 关闭连接

    • 接收方收到 RST 标志或者发送方发送 RST 标志后,设置当前 TCPConnection 的输入输出字节流的状态为错误状态,并立即停止退出。这种属于暴力退出(unclear shutdown),可能会导致尚未传输完成的数据丢失(例如仍然在网络中运输的数据包在接收方收到RST标志后被丢弃)。
    • 若想让双方都在数据流收发完整后退出(clear shutdonw),考虑四次挥手,参考 K 大的博客

Lab5: NetworkInterface

  • ARP 协议:根据 IP 地址获取 Mac 地址,实现简单的 ARP 协议

  • 维护 ARP 条目哈希表,每个条目 TTL 为 30s,到期之后删除

  • send_datagram(dgram, next_hop) 时如果 ARP 表中没有 IP 地址对应的表项就广播发送(构造 ARPMessage 以及 Ethernet Frame TYPE_ARP),如果有的话就直接构造 Ethernet Frame TYPE_IPv4 将 dgram 发送

  • recv_frame(dgram) 首先判断是不是 frame 的 目的地址是不是 自己的Mac/广播地址,不是直接 丢弃

    • TYPE_IPv4:收到 IP 数据包直接转发丢给上层

      为啥 recv_frame 为什么还可以收到 IPv4 的数据包呢?因为 ARP 数据包加上 Ethernet Frame Header 之后变成以太网帧在数据链路层传输,Frame 类型有很多,包括 IPv4、IPv6、ARP 等等,如果是 IPv4 数据包仅仅只需要转发给 caller 即可,因为作为数据链路层不需要管 TCP 状态、IP 字段等等其他信息

    • TYPE_ARP:收到 ARP 包分为请求和应答两种情况处理

      • 请求:将自己的 Mac 封装之后发送,并且更新 ARP 表项
      • 应答:更新 ARP 表项,并且检查 IP 请求列表,如果相符就 send_dgram,并删除对应的表项
  • tick(ms_since_last_tick) 删除过期的 ARP 表项以及 已经发送 dgram 表项

Lab6: Router

  • router:实现简单的路由表,转发数据包,维护路由表(哈希表),最长匹配原则

  • add_route(route_prefix, prefix_length, next_hop, interface_num):添加一条路由表项

  • route_one_datagram(dgram):查询路由表,如果存在匹配项切 TTL > 1 就转发给下一跳/直连,并将 TTL 减一,其余情况全部丢弃

Lab7

  • 将之前所有内容合为一个 app

参考

  • https://csdiy.wiki/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/CS144/
  • K大博客:https://kiprey.github.io/tags/CS144/
  • 掘金博客: https://juejin.cn/user/822883244836461
  • CS144-2019翻译:http://doraemonzzz.com/tags/CS144/
  • CS144-2019:https://blog.csdn.net/kangyupl/article/details/108589594
  • https://zhuanlan.zhihu.com/p/464281077
  • https://gitee.com/dying1122/cs144-lab

我的CPP面试仓库笔记

  • https://github.com/EricPengShuai/Interview

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/680390.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VLAN基础知识3_VLAN间三层通信(单臂路由)

目录 1.VLAN单臂路由简介 2.基于单臂路由VLAN间三层通信原理 3.VLAN间三层通信单臂路由实验 3.1 常用配置命令 3.2 实验配置步骤 3.3实验效果 1.VLAN单臂路由简介 单臂路由&#xff08;One-Arm Router&#xff09;是一种网络拓扑结构&#xff0c;其中一个路由器的一个接…

动手写一个 Java JWT Token 生成组件

OAuth2 中默认使用 Bearer Tokens (一般用 UUID 值)作为 token 的数据格式&#xff0c;但也支持升级使用 JSON Web Token(JWT) 来作为 token 的数据格式。实际来说&#xff0c;OAuth 规范中并无限制 Token 采取何种格式。今天我们就采用 JWT 来作为 Token&#xff0c;它的一个好…

四种缓存的避坑总结

背景 分布式、缓存、异步和多线程被称为互联网开发的四大法宝。今天我总结一下项目开发中常接触的四种缓存实际项目中遇到过的问题。 JVM堆内缓存 JVM堆内缓存因为可以避免memcache、redis等集中式缓存网络通信故障问题&#xff0c;目前还在项目中广泛使用。 堆内缓存需要注…

FFmpeg5.0源码阅读——avformat_find_stream_info

摘要&#xff1a;在使用FFmpeg库时通常使用avformat_find_stream_info相关函数来探测流的基本信息&#xff0c;为了更加深入理解FFmpeg的基本流程&#xff0c;本文根据FFmpeg 5.0的源码详细描述了该函数的具体实现。   关键字&#xff1a;FFmpeg   读者须知&#xff1a;读者…

数学之美:神奇的杨辉三角形,比帕斯卡早了近600年,致敬中国古代数学家(63)

小朋友们好&#xff0c;大朋友们好&#xff01; 我是猫妹&#xff0c;一名爱上Python编程的小学生。 和猫妹学Python&#xff0c;一起趣味学编程。 今日主题 什么是杨辉三角形&#xff1f; 杨辉三角形有什么规律&#xff1f; 中国古代数学家杨辉。 西方科学家帕斯卡。 杨…

【开源与项目实战:开源实战】81 | 开源实战三(上):借Google Guava学习发现和开发通用功能模块

上几节课&#xff0c;我们拿 Unix 这个超级大型开源软件的开发作为引子&#xff0c;从代码设计编写和研发管理两个角度&#xff0c;讲了如何应对大型复杂项目的开发。接下来&#xff0c;我们再讲一下 Google 开源的 Java 开发库 Google Guava。 Google Guava 是一个非常成功、…

智能指针类模板:auto_ptr、unique_ptr、shared_ptr的原理与使用

1. 什么是智能指针 智能指针是行为类似于指针的类对象&#xff0c;通常用于管理动态内存分配。C程序通常手动动态分配堆内存&#xff0c;但如果动态分配的内存没有释放&#xff0c;则会发生内存泄漏。 例如代码段1.1。 // 代码段1.1 void demo() {double *pd new double;*pd…

使用STM32F103的串口实现IAP程序升级功能

使用STM32F103的串口实现IAP程序升级功能 &#x1f3ac;IAP程序烧录全过程演示&#xff1a; ✨这几天折腾IAP升级功能&#xff0c;狂补了很多相关BootLoader相关的知识。本来最想实现IAP升级程序的方式是&#xff0c;基于SPI通讯的SD卡&#xff0c;借助挂载的FatFS文件系统&am…

C++中的内存分区

目录 操作系统的内存区域 C内存分区模型 1. 程序运行前 2. 程序运行后 3. new 操作符的使用 操作系统的内存区域 text段&#xff1a;存储程序的二进制指令&#xff0c;即程序源码编译后的二进制代码data段&#xff1a;存储已被初始化的全局变量、常量bss段&#xff1a;存储…

ES-工作原理

前言 ​ 搜索引擎是对数据的检索&#xff0c;而数据总体分为两种&#xff1a;结构化数据和非结构化数据。而对于结构化数据&#xff0c;因为他们具有特定的结构&#xff0c;所以一般都是可以通过关系型数据库MySQL/oracle的二维表的方式存储和搜索&#xff0c;也可以建立索引。…

Redis的简单使用 (实现Session持久化)

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔&#x1f93a;&#x1f93a;&#x1f93a; 目录 一、Redis数据类型的使用 1. 字符串&#xff…

Redis【入门篇】---- Redis的Java客户端-Jedis

Redis【入门篇】---- Redis的Java客户端-Jedis 1. Jedis快速入门2. Jedis连接池1. 创建Jedis连接池2. 改造原始代码 在Redis官网中提供了各种语言的客户端&#xff0c;地址&#xff1a;https://redis.io/docs/clients/ 其中Java客户端也包含很多&#xff1a; 标记为❤的就是推荐…

密码学证明方案寒武纪大爆发——扩容、透明性和隐私的变革潜力

1. 引言 前序博客有&#xff1a; ZKP大爆炸 本文主要参考&#xff1a; StarkWare 2023年6月博客 Cambrian Explosion of Cryptographic Proofs----The transformative potential for scalability, transparency, and privacy2023年3月Eli Ben-Sasson在The 13th BIU Winter …

JavaWeb之Cookie和Session

文章目录 CookieCookie基本介绍Cookie的基本使用Cookie的创建从服务器获取CookieCookie值的修改方案一方案二 浏览器查看CookieCookie声明控制Cookie有效路径Path的设置 SessionSession基本介绍Session的创建和获取&#xff08;id号&#xff0c;是否为新&#xff09;Session域数…

【SQL server关键字】

目录&#xff1a; 前言一、CREATE -- 创建二、INSERT INTO VALUES -- 插入数据三、SELECT FROM -- 查找数据1.SEKECT简单了解2.函数的使用3.选择列表与group by子句的对应4.exists子查询 四、UPDATE SET -- 更改数据五、ALTER -- 修改属性六、JOIN ON-- 链接多个表1. join初…

JDBC 望舒客栈项目 万字详解

目录 一、前言 二、项目结构 三、准备工作 1.建立子包 : 2.导入jar包 : 3.工具类 : 1 Utility工具类 2 JDBCUtilsDruid工具类 4.导入配置文件 : 5.引入BasicDAO : 四、项目主体 1.界面显示 : 1 代码演示 2 运行测试 2.用户登录 : 1 创建员工表employee 2 创建Ja…

JavaScript 事件加载有哪些应用场景?

&#x1f482; 个人网站:【海拥】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 目录 前言什么是JavaScript事…

VUE L MVVM模型 ③

目录 文章有误请指正&#xff0c;如果觉得对你有用&#xff0c;请点三连一波&#xff0c;蟹蟹支持✨ V u e j s Vuejs Vuejs M V V M MVVM MVVM模型Data与El的2种写法总结 文章有误请指正&#xff0c;如果觉得对你有用&#xff0c;请点三连一波&#xff0c;蟹蟹支持✨ ⡖⠒⠒⠒…