0.介绍
- 文件说明
apps # 应用层函数
CMakeLists.txt # 构建脚本
doctests # 内置函数测试脚本
libsponge # 应用层函数
tests # lab测试脚本
build # 构建文件
compile_commands.json # 编译命令
etc # 配置文件
README.md # 使用文档
writeups # lab0-4的文档极详细文档
-
使用说明
-
创建build文件夹
mkdir build
-
自动生成makefile
cmake ..
-
构建
make
-
运行可执行文件
make check # 测试实现结果 ./apps/tcp_benchmark # 测试带宽 ./apps/wegebt # 获取网页
-
1.前言
- 同样的项目曾经在21年做过一次,当时由于时间有限加上能力不够,磕磕绊绊写完了lab0-3。但做到lab4的时候由于概念理解不清加上官网文档过于复杂实在做不下去了。
- 最近时间比较宽裕,于是又花了一周的时间重写了一遍整个lab0-4,详细的记录了整个实验过程,做完之后不禁感叹整个实验设计的精妙,通过一步一步的完善代码,难度逐渐递进,实现了一个简单但又实用的基于TCP的套接字,可用于获取网页,也可用于socket双方的通信。
- 如果你有以下疑问,那么通过完成整个lab可以解决你的疑问。
- TCP的MAX_PAYLOAD_SIZE只有1536字节,为什么可向socket一次性写入1M的数据?
- 对socket一次写入n字节的数据,而对另一端一次性读取socket得到m字节的数据,n和m一定相等吗?为什么n和m有时候是不相等的?
- TCP是如何进行流量控制的?
- 为什么TCP需要三次握手?四次挥手?
- 什么是time-wait状态?为什么time-wait状态后还需要等待2个MSL?
- client和server应用程序如何判断对方已经关闭连接?为什么要这样判断?
2.实验简介
2.1 lab0 networking warmup
-
熟悉整个lab实验环境
-
用操作系统socket接口编写网络程序
-
实现一个可读可写的缓冲区
2.2 Lab1 stitching substrings into a byte stream
- 实现一个字节重组器
- 将字节重组器与缓冲区相联系起来
2.3 Lab2 the TCP receiver
- 32bit数-64bit数之间的转换与舍入
- 根据收到的报文段返回相应的ackno和win窗口字段值
2.4 Lab3 the TCP sender
- 将上层(ByteStream)发来的数据切片(写seqno,SYN,FIN,payload)
- 维护一个计时器工具类
- 基于计时器类维护已发送的报文段
- 在适当时机重发上述报文段
2.5 Lab4 the TCP Connection
- 将网络层收到的报文传递给TCP sender和receiver
- 在合适的时机将报文发出
- 将TCP sender待发送的报文添加字段发送出去
- 在合适的时机结束连接
3.实验须知
- 实验前需要对c++11面向对象编程有一定的了解。
- 整个lab绝大部分不懂的地方都可以从https://cs144.github.io/官方文档上获取,现在所有的代码和文档都已经开放,均可clone到本地。
- 整个实验ip层仍然基于linux的实现,ip层通过暴露两个接口与tcp_connection进行交互。
- 文档上会有少数一些摸棱两可和重复的描述,这时可以通过看到官方RFC文档来解决疑问,也可以有一些自己的尝试。
- 如果读完官方文档后,仍然一头雾水的话,可以提前查看少数对应该lab的测试用例。
- 在前面几个lab中尽可能完备的设计,为最后的lab4作铺垫,防止lab4中还要修改前面出现的bug。
- 在lab1中,需要尽可能设计高效的数据结构,不然可能在lab4中无法通过测试。
- libsponge/util中可能会有一些实验中能够用到的类,用于提高效率。
- 整个lab中调试语句用cerr代替cout打印中间变量,在lab4测试中需要删除打印语句,否则可能出现一些奇奇怪怪的错误。
4.实验结果
4.1 基于tcp_connection的客户端服务端交互过程抓包
-
三次握手
-
数据传输
-
四次挥手
4.2 基于benchmark的测试
- 已经超过讲义中要求的0.1Gbit/s, 考虑到仍有一些libsponge/util中的工具类没有使用,还有很大的优化空间,后续会对此继续进行优化。
4.3 基于自己实现的socket获取网页
- webget.c
void get_URL(const string &host, const string &path) {
// 基于自己实现的socket
CS144TCPSocket sock;
sock.connect(Address(host, "http"));
string url = "GET " + path + " " + "HTTP/1.1" + "\r\n";
url += "Host: " + host + "\r\n";
url += "Connection: close\r\n";
url += "\r\n";
sock.write(url);
constexpr size_t BUFFER_SIZE = 1024;
string buf;
while (!sock.eof()) {
sock.read(buf, BUFFER_SIZE);
cout << buf;
}
sock.wait_until_closed();
}
- 运行结果 又回到最初的起点lab0