├── .github └── workflows │ └── doc.yml ├── .gitignore ├── Makefile ├── README.md ├── docs ├── faq.md ├── framework.md ├── index.md ├── javascripts │ └── mathjax.js ├── lwip.md ├── requirement.md ├── setup.md └── submission.md ├── mkdocs.yml ├── pandoc ├── docx.yml ├── pdf.yml └── remove_duplicate_title.sh ├── poetry.lock ├── pyproject.toml ├── requirements.txt └── theme-override └── main.html /.github/workflows/doc.yml: -------------------------------------------------------------------------------- 1 | name: Build documentation with mkdocs 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Set up Python 3.12 12 | uses: actions/setup-python@v1 13 | with: 14 | python-version: 3.12 15 | - name: Install dependencies 16 | run: | 17 | python -m pip install --upgrade pip 18 | pip install -r requirements.txt 19 | - name: Build with mkdocs 20 | run: mkdocs build 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | site/ 2 | .vscode/ 3 | generated/ 4 | deploy.sh 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: combine pdf docx clean 2 | 3 | project_name := tcp_lab 4 | 5 | combine: generated/$(project_name).md 6 | 7 | pdf: generated/$(project_name).pdf 8 | 9 | docx: generated/$(project_name).docx 10 | 11 | generated/$(project_name).md: docs mkdocs.yml 12 | mkdir -p generated 13 | mkdocscombine -d -o $@ 14 | bash ./pandoc/remove_duplicate_title.sh $@ 15 | 16 | generated/$(project_name).docx: generated/$(project_name).md pandoc/docx.yml 17 | pandoc --defaults pandoc/docx.yml -s -o $@ $< 18 | 19 | generated/$(project_name).pdf: generated/$(project_name).md pandoc/pdf.yml 20 | pandoc --defaults pandoc/pdf.yml -o $@ $< 21 | 22 | clean: 23 | rm -rf generated 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TCP-Lab Documentation 2 | 3 |  4 | 5 | 本项目为《计算机网络专题训练》课程的 TCP 实验的实验文档,采用 `mkdocs` 编写。 6 | 7 | 本站点的自动编译版本在 [这里](https://lab.cs.tsinghua.edu.cn/tcp/doc/) 发布。 8 | 9 | ## 撰写 10 | 11 | 本站点内容使用 Markdown 进行编写。具体可查看 [mkdocs](https://www.mkdocs.org/) 和 [mkdocs-material](https://squidfunk.github.io/mkdocs-material/extensions/pymdown/) 文档。 12 | 13 | 如果创建了新页面,需要插入到 `mkdocs.yml` 的 `nav` 部分,否则将不会出现在编译结果中。 14 | 15 | ## 编译 16 | 17 | 首先安装依赖,而后编译即可: 18 | 19 | ```bash 20 | python3 -m pip install --user -r requirements.txt # 安装 Python 依赖包 21 | mkdocs serve # 直接在本地 serve,或者: 22 | mkdocs build --clean # 生成于 site/ 文件夹中 23 | ``` 24 | 25 | ## 生成离线版 26 | 27 | 生成 PDF 和 DOCX 版本需要(经过修改的) [`mkdocs-combine`](https://github.com/Harry-Chen/mkdocs-combine) 插件的支持。首先安装插件: 28 | 29 | ```bash 30 | pip3 install git+https://github.com/Harry-Chen/mkdocs-combine.git 31 | ``` 32 | 33 | 而后运行: 34 | 35 | ```bash 36 | make docx 37 | make pdf 38 | ``` 39 | 40 | 即可在 `generated` 目录下获得相应格式的文件。 41 | -------------------------------------------------------------------------------- /docs/faq.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | !!! question "RFC 793 中 TCP 状态机写得很复杂,我需要都实现吗?" 4 | 5 | 不需要,其中一些比较关键的部分已经在代码框架中给出,对于一些可选功能,可能会实现更完整的状态机。我们的建议是,对于没有实现的代码,使用 `UNIMPLEMENTED` 宏表示尚未实现,那么代码在运行到此处的时候,就会显示错误,此时再去实现对应的处理即可,没有必要先把完整的 TCP 状态机翻译成代码,那样代码量太大,并且会很枯燥。 6 | 7 | !!! question "Nagle 算法是什么呢?" 8 | 9 | 简单来说,Nagle 算法要解决的是小包问题:如果用户 `write` 调用的数据都很小,并且每次 `write` 都要发送一个 TCP 包给对端,就会产生大量的 TCP 包。一个简单解决的思路是,如果 `write` 的数据太小,那就等一小段时间,期望用户在这个时间里继续写入一些数据,直到超时或者积攒到足够大的数据可以发送。由于超时实现比较麻烦(需要用到 Timer,引入了额外的状态,实现不方便),一个更优雅的方式是,当所有发送过的数据都已经 ACK 了(类似于拿 rtt 做超时,在更新 `snd_una` 的时候判断),此时没有 unACKed 的数据,就把缓存的数据都发送出去。详细介绍见 [RFC896](https://tools.ietf.org/html/rfc896) 和 [Wikipedia 页面](https://en.wikipedia.org/wiki/Nagle%27s_algorithm)。 10 | 11 | !!! question "TCP Urgent 怎么实现?" 12 | 13 | 其实不是同学们的问题,而是 RFC 793 写得并不清晰。在 [RFC 6093](https://tools.ietf.org/html/rfc6093) 中,作者提到了历史上对 TCP Urgent 不同的处理和带来的一系列问题,并最后建议应用程序不要用 TCP Urgent 功能,TCP 协议栈实现它也只是为了兼容一些已有的程序。虽然我们把它列入了一个可选功能中,但并不建议大家实现。 14 | 15 | !!! question "在 WSL 中运行程序的时候,出现了 local datagram socket: Operation not supported 的报错,这是为什么呢?" 16 | 17 | WSL 的 `/mnt` 路径下是 mount 的 NTFS,不支持 unix domain socket,请换到 WSL 内部的路径(比如 home 目录)再尝试。 18 | 19 | !!! question "make 的时候报错:Division works only with integers" 20 | 21 | meson 版本太老,请用 `pip3 install -U meson` 升级。 22 | 23 | !!! question "make 的时候报错:meson: No such file or directory" 24 | 25 | 首先检查 meson 是否安装了,并且可以在命令行中执行;如果安装了还是显示失败,可能是因为更新了 meson,导致它的路径变了,可以删除整个 `builddir` 目录再重新 `make`。 26 | 27 | !!! question "设置了丢包以后,lwip 服务器一段时间没有收到 TCP 包就给客户端发送 FIN 了,能不能延长一下这个超时?" 28 | 29 | lwip 的 HTTPD 实现中,如果发现客户端一段时间(默认是 2s)都没有发送任何数据,就会终止连接。可以在 `lwipopt.h` 头文件中设置 `#define HTTPD_MAX_RETRIES 10`(默认值是 4),这样 HTTPD 会等待更长的时间。详细的设置可以阅读 lwip 的 `httpd_opts.h` 头文件。 -------------------------------------------------------------------------------- /docs/framework.md: -------------------------------------------------------------------------------- 1 | # 实验框架 2 | 3 | 实验框架代码在 [https://git.tsinghua.edu.cn/tcp-lab/tcp-lab](https://git.tsinghua.edu.cn/tcp-lab/tcp-lab) ,请同学克隆下来进行开发。 4 | 5 | ## 目录结构 6 | 7 | 目录结构大体如下: 8 | 9 | 1. include: 头文件目录 10 | 2. src: 源代码目录 11 | 3. thirdparty: 第三方库,目前只有 lwIP 12 | 4. Makefile: 用于 Make 命令构建 13 | 5. meson.build: 实际的构建描述文件 14 | 15 | 需要同学们修改的,主要是 `include` `src` 和 `meson.build`。一般不需要修改 lwIP 的源代码,关于如何配置 lwIP,请阅读 [lwIP 库](/tcp/doc/lwip/)。 16 | 17 | 在源代码目录 `src` 下,又有三个子目录: 18 | 19 | 1. lwip: 基于 lwIP 编写的 TCP 客户端和服务端。 20 | 2. lab: 同学需要补全的 TCP 协议栈,也包括了一个简单的 HTTP 客户端和服务端。 21 | 3. test: 用于测试 TCP 协议栈的测试代码和脚本 22 | 23 | 同学们主要需要编写的就是 `lab` 目录下的代码。 24 | 25 | ## 客户端与服务端通信 26 | 27 | 在 `src/common.cpp` 中,提供了进程间通信的代码。在本次实验中,客户端和服务端分别运行在一个进程中,为了发送 IP 分组给对方,采用的是 unix socket 的方法。如果出现创建 unix socket 失败的错误,请检查文件系统是否支持。 28 | 29 | 编译后,会生成四个可执行程序: 30 | 31 | - lab-client, lab-server:采用同学编写的 TCP 协议栈的客户端和服务端 32 | - lwip-client, lwip-server:采用 lwIP 协议栈的客户端和服务端 33 | 34 | ## 如何运行程序 35 | 36 | 程序所使用的 unix socket 路径通过命令行参数给出,比如,如果用 `s` 表示服务端,`c` 表示客户端,那么应该这样编译并运行 `lwip-server`: 37 | 38 | ```shell 39 | $ make && ./builddir/lwip-server -l s -r c -p lwip-server.pcap 40 | ``` 41 | 42 | 这表示 `lwip-server` 的本地(监听)unix socket 是 `s`,远端(目的)unix socket 是 `c`,并且会把收发的 IP 分组写入到 `lwip-server.pcap` 文件里。 43 | 44 | 类似地,可以运行 `lwip-client`: 45 | 46 | ```shell 47 | $ make && ./builddir/lwip-client -l c -r s -p lwip-client.pcap 48 | ``` 49 | 50 | 注意要保持这里参数的 `-l` `-r` 应该和 `lwip-server` 的次序正好颠倒,这样就可以保证两个进程可以正常通信。同样地,它会把收发的 IP 分组写入到 `lwip-client.pcap` 文件里。 51 | 52 | 如果同时运行 `lwip-server` 和 `lwip-client` 并且路径正确,应该可以看到 HTTP 获取到的结果: 53 | 54 | ```text 55 | tcp_recved: received 130 bytes, wnd 1738 (406). 56 | HTTP Got Body: 57 | 58 |
63 | ![]() |
66 | lwIP - A Lightweight TCP/IP Stack67 |68 | The web page you are watchitcp_output: nothing to send (0x0) 69 | RX: 45 00 02 40 00 02 00 00 FF 06 A5 B3 0A 00 00 01 0A 00 00 02 00 50 C0 01 00 00 1B 87 00 00 19 FD 50 18 07 D1 35 FA 00 00 6E 67 20 77 61 73 20 73 65 72 76 65 64 20 62 79 20 61 20 73 69 6D 70 6C 65 20 77 65 62 0D 0A 09 20 20 20 20 73 65 72 76 65 72 20 72 75 6E 6E 69 6E 67 20 6F 6E 20 74 6F 70 20 6F 66 20 74 68 65 20 6C 69 67 68 74 77 65 69 67 68 74 20 54 43 50 2F 49 50 20 73 74 61 63 6B 20 3C 61 0D 0A 09 20 20 20 20 68 72 65 66 3D 22 68 74 74 70 3A 2F 2F 77 77 77 2E 73 69 63 73 2E 73 65 2F 7E 61 64 61 6D 2F 6C 77 69 70 2F 22 3E 6C 77 49 50 3C 2F 61 3E 2E 0D 0A 09 20 20 3C 2F 70 3E 0D 0A 09 20 20 3C 70 3E 0D 0A 09 20 20 20 20 6C 77 49 50 20 69 73 20 61 6E 20 6F 70 65 6E 20 73 6F 75 72 63 65 20 69 6D 70 6C 65 6D 65 6E 74 61 74 69 6F 6E 20 6F 66 20 74 68 65 20 54 43 50 2F 49 50 0D 0A 09 20 20 20 20 70 72 6F 74 6F 63 6F 6C 20 73 75 69 74 65 20 74 68 61 74 20 77 61 73 20 6F 72 69 67 69 6E 61 6C 6C 79 20 77 72 69 74 74 65 6E 20 62 79 20 3C 61 0D 0A 09 20 20 20 20 68 72 65 66 3D 22 68 74 74 70 3A 2F 2F 77 77 77 2E 73 69 63 73 2E 73 65 2F 7E 61 64 61 6D 2F 6C 77 69 70 2F 22 3E 41 64 61 6D 20 44 75 6E 6B 65 6C 73 0D 0A 09 20 20 20 20 6F 66 20 74 68 65 20 53 77 65 64 69 73 68 20 49 6E 73 74 69 74 75 74 65 20 6F 66 20 43 6F 6D 70 75 74 65 72 20 53 63 69 65 6E 63 65 3C 2F 61 3E 20 62 75 74 20 6E 6F 77 20 69 73 0D 0A 09 20 20 20 20 62 65 69 6E 67 20 61 63 74 69 76 65 6C 79 20 64 65 76 65 6C 6F 70 65 64 20 62 79 20 61 20 74 65 61 6D 20 6F 66 20 64 65 76 65 6C 6F 70 65 72 73 0D 0A 09 20 20 20 20 64 69 73 74 72 69 62 75 74 65 64 20 77 6F 72 6C 64 2D 77 69 64 65 2E 20 53 69 6E 63 65 20 69 74 27 73 20 72 65 6C 65 61 73 65 2C 20 6C 77 49 50 20 68 61 73 0D 0A 09 20 20 20 20 73 70 75 72 72 65 64 20 61 20 6C 6F 74 20 6F 66 70 | tcp_receive: window update 2001 71 | HTTP Got Body: 72 | ng was served by a simple web 73 | server running on top of the lightweight TCP/IP stack lwIP. 75 | 76 |77 | lwIP is an open source implementation of the TCP/IP 78 | protocol suite that was originally written by Adam Dunkels 80 | of the Swedish Institute of Computer Science but now is 81 | being actively developed by a team of developers 82 | distributed world-wide. Since it's release, lwIP has 83 | spurred a lot oftcp_output: nothing to send (0x0) 84 | tcp_output: sending ACK for 7583 85 | TX: 45 00 00 28 00 02 00 00 FF 06 A7 CB 0A 00 00 02 0A 00 00 01 C0 01 00 50 00 00 19 FD 00 00 1D 9F 50 10 04 30 9F B4 00 00 86 | RX: 45 00 02 40 00 03 00 00 FF 06 A5 B2 0A 00 00 01 0A 00 00 02 00 50 C0 01 00 00 1D 9F 00 00 19 FD 50 10 07 D1 AF 50 00 00 20 69 6E 74 65 72 65 73 74 20 61 6E 64 20 68 61 73 20 62 65 65 6E 20 70 6F 72 74 65 64 20 74 6F 20 73 65 76 65 72 61 6C 0D 0A 09 20 20 20 20 70 6C 61 74 66 6F 72 6D 73 20 61 6E 64 20 6F 70 65 72 61 74 69 6E 67 20 73 79 73 74 65 6D 73 2E 20 6C 77 49 50 20 63 61 6E 20 62 65 20 75 73 65 64 20 65 69 74 68 65 72 0D 0A 09 20 20 20 20 77 69 74 68 20 6F 72 20 77 69 74 68 6F 75 74 20 61 6E 20 75 6E 64 65 72 6C 79 69 6E 67 20 4F 53 2E 0D 0A 09 20 20 3C 2F 70 3E 0D 0A 09 20 20 3C 70 3E 0D 0A 09 20 20 20 20 54 68 65 20 66 6F 63 75 73 20 6F 66 20 74 68 65 20 6C 77 49 50 20 54 43 50 2F 49 50 20 69 6D 70 6C 65 6D 65 6E 74 61 74 69 6F 6E 20 69 73 20 74 6F 20 72 65 64 75 63 65 0D 0A 09 20 20 20 20 74 68 65 20 52 41 4D 20 75 73 61 67 65 20 77 68 69 6C 65 20 73 74 69 6C 6C 20 68 61 76 69 6E 67 20 61 20 66 75 6C 6C 20 73 63 61 6C 65 20 54 43 50 2E 20 54 68 69 73 0D 0A 09 20 20 20 20 6D 61 6B 65 73 20 6C 77 49 50 20 73 75 69 74 61 62 6C 65 20 66 6F 72 20 75 73 65 20 69 6E 20 65 6D 62 65 64 64 65 64 20 73 79 73 74 65 6D 73 20 77 69 74 68 20 74 65 6E 73 0D 0A 09 20 20 20 20 6F 66 20 6B 69 6C 6F 62 79 74 65 73 20 6F 66 20 66 72 65 65 20 52 41 4D 20 61 6E 64 20 72 6F 6F 6D 20 66 6F 72 20 61 72 6F 75 6E 64 20 34 30 20 6B 69 6C 6F 62 79 74 65 73 0D 0A 09 20 20 20 20 6F 66 20 63 6F 64 65 20 52 4F 4D 2E 0D 0A 09 20 20 3C 2F 70 3E 0D 0A 09 20 20 3C 70 3E 0D 0A 09 20 20 20 20 4D 6F 72 65 20 69 6E 66 6F 72 6D 61 74 69 6F 6E 20 61 62 6F 75 74 20 6C 77 49 50 20 63 61 6E 20 62 65 20 66 6F 75 6E 64 20 61 74 20 74 68 65 20 6C 77 49 50 0D 0A 09 20 20 20 20 68 6F 6D 65 70 61 67 65 20 61 74 20 3C 61 0D 0A 09 20 20 20 20 87 | tcp_receive: window update 2001 88 | HTTP Got Body: 89 | interest and has been ported to several 90 | platforms and operating systems. lwIP can be used either 91 | with or without an underlying OS. 92 | 93 |94 | The focus of the lwIP TCP/IP implementation is to reduce 95 | the RAM usage while still having a full scale TCP. This 96 | makes lwIP suitable for use in embedded systems with tens 97 | of kilobytes of free RAM and room for around 40 kilobytes 98 | of code ROM. 99 | 100 |101 | More information about lwIP can be found at the lwIP 102 | homepage at http://savannah.nongnu.org/projects/lwip/ 108 | or at the lwIP wiki at http://lwip.wikia.com/. 110 | 111 | | 112 | 113 | |