CS144实验注意点

CS144 实验注意点

TCP implement

ByteStream

- 看测试,通过所有测试即可
- 重要的是 input_ended() 、end_input() 和 eof() 的 区别
-    input_ended 是 查看 byte_stream 是否 已经被 写入 eof
-    end_input 是 提供给 writer 的,用来 表示 要输入的数据 已经输入完了
-    eof 是 用来查看 byte_stream 中的数据是否已经被 reader 读完

Reassembler

- 看测试,通过所有测试即可
- 最吃性能的地方,如果这里复制了大量的字符串,就会导致 throughout 较低
- 最重要的是 
	1. 需要维护  unreassemble bytes,并 进行重新排序,选择合适的数据结构
	2. 在 组合完成后,需要将 有序的数据 写入 Bytestream
	3. 需要 维护 下一个期待的 absolute_seqno_idx,这就是 下一个包的 确认号 的 索引
	4. 携带 fin 的包可能提前到达,需要 在 内部维护好 它,并在所有数据都到达后,对 bytestream 进行 end_input
	5. 要尽可能地减少 数据的拷贝,所以 最好将 数据的校验 放在 这里
	6. 可以选择使用 index 进行标记,存储 shared_ptr 而不是 数据,仅仅释放 无效的数据
	   或者根据 要 复制的数据的大小,选择 copy or  使用索引
	7. 整个 window_size (capacity) = unread bytes + win (待发送的可接受的窗口大小)
                                                   包括 unreassemble bytes

WrapInt32

- tcp header's ackno and seqno is uint32_t
- absolute_seqno/absolute_ackno = actual seqno/ackno + _isn
- absolute_seqno_index, actually is data_index without SYN and ACK


- absolute_seqno = seqno + _isn
- absolute_seqno_index = absolute_seqno - 1 (SYN)

- unwrap 是将 WrapInt32 使用 _isn 和 checkpoint
-      在 多个 2^32 的 区域内选择一个 离 checkpoint最近的
-      使用 checkpoint 可以 查看在 哪几个 2 ^32 的区域
-      进行比较后,得到距离 checkpoint 最近的 uint64_t 的 值,确保 该值 > _isn
-      得到 uint64_t 后,减去 _isn 即可得到 有效的 absolute_seqno/ackno

Receiver

- receiver concerns SYN FIN Payload Seqno

- 还是要看测试
- 有限状态机可以帮助处理各种棘手的条件判断
- 这里数据的有效性判定,既可以在这里判定,也可以交给 Reassembler 来 判断
- 还是交给 Reassembler 来判断比较好,这样不需要复制字符串
- Payload 是 Buffer 类型,其中的 数据实际上是一个 智能指针,所以可以随便拷贝
- 最好将 Payload 的 数据 直接 拷贝到 字符串 中

- 这里的 window_size,指的是 Byte_stream 中 的 capacity - unread bytes (to send by next tcpsegment)

- 每个 sender 和 receiver 都有一个 Byte_stream
	- sender's Byte_stream    => write data from user, read data for sender to send
	- receiver's Byte_stream  => write data from receiver, read data for user

- 每个 sender 和 receiver 都有 一个 window_size
	- receiver's window_size   =   capacity - unread bytes    (will be sent by next tcp segment's win field)
	- sender's _window_size, in fact is the number of bytes that can send
	-      receive window_size from tcp_segment's win field
	-           waiting for retransmission     the number of byte that can send
	-                bytes_in_flight                 _window_size
	-                    window_size (win field of tcpsegment)
		- sender can receive data size from user, is the capacity of byte_stream

Sender

- sender concerns  ack and win in tcpsegment

- tick()        => 用于重传 _wait 队列中的 超时的 segment

- fill_window() => 填充所有可以发送的 _window_size
	- firstly invoke fill_window, will send SYN tcp segment

- segment_receive(ackno, win) => 将 确认的 ackno 全都 从 _wait 中删除
                              => 更新 _window_size 通过 传入 的 win
							  => 传来的 window_size = _window_size + bytes_in_flight()

- 每次虽然可以发送多个 tcpsegment,但是 只有 第一个 tcp segment 可以 进行超时重传
-     剩下的 tcpsegment 仅仅 发送一次,不进行重传
-     当 第一个 tcp_segment 被确认后,将 下一个 未确认的 tcp_segment 的 超时重传开启

- 重传的标志
	- _ddl_tick (截止时间) =  _tick (活跃的 ticks) + rt_time (超时时间)
	- 下一次超时,用当时的 _tick + power(2, 第n次重传) * rt_time
	-            不能使用 上一次的 ddl_tick

Connection

- 只需要 把 receiver 和 sender 关心的东西 传给 他们即可
- ack 不会 被 重传,所以 connection 每次需要传输

- as server
	- LISTEN 状态,实际上就是 什么都不做的状态,只分配一个  receiver 和 sender (connection)
		这样,它就有了收发数据包的能力
	- LISTEN 状态收到 ack 和 rst,直接忽略;接收 SYN 就相当于 服务器收到 连接请求
	-                                    需要传递给 receiver
	- RST 必须 使用 有效的 seqno,才能被接收,并断开连接并设置 流为 error 状态

- 每个 有长度 的 tcp_segment 必须被回应一个 tcpsegment,也就是必须被确认
-     单独的 ack 不需要被重传,所以 通过 sender 获取有效的 seqno 后,由 connection 发送即可
-         不需要有数据
-     每个 从 sender 中 读出的 segment,都使用 receiver's ackno 和 win_size

- 从 _sender.segment_out 获得 tcp segment 后,得到 seqno、payload、SYN、FIN
- 	查看 _receiver 中 得到 ackno 和 win
-   	如果 ackno 是空,说明 尚未收到 SYN,适用于 client 调用 connect 时 发送 SYN

- as client
-   use connect, will invoke _sender.fill_window() 发送 SYN
-   server 收到 SYN,_receiver 初始化了 ackno,_sender 调用 fill_window 发送 SYN
-        从 connection 的 segment_out 中拿到 tcp_segment
-        从 receiver 得到的 ack 和 win
-        放到 connection 的 segments_out

- cleanly finished
   - before receive fin, send fin will linger. means that firstly send fin => linger
   - after receive fin, send fin will not linger, means that send fin secondly => no linger

- judge after _sender.segment_receive(ack, win)

- the sign that send fin
	- _sender.next_seqno_absolute == _sender.stream.bytes_written() + 2;
	-    SYN_SENT
	-         1                  <                  2
	-    ESTABLISHED
	-   1 + sender's bytes       <             all written bytes + 2
	-   FIN_SENT(FIN_WAIT1)
	- 	1 + all data + 1         ==             all data + 2



- the sign that fin is acked
-    _sender.next_seqno_absolute == _sender.stream.bytes_written() + 2  =>  fin sent
-    bytes_in_flight() == 0  =>  fin is acked



- judge after _receive.segment_receive()

- the sign that receive fin
	- _receiver's stream_out.input_ended == true


- tick()
	- 用于 最后的 TIME_WAIT 状态,到达时间后 设置 _active 为 false
	- 用于 调用 sender 的 tick,重传 超时的 tcp_segment

测试

- revisit webget
-   tun144 and tun145 network interface must be open
-   can not have any output to pass the test, in other word, must clean the debug info before test
-   must make check_lab4, then tun144 tun145 is open, then use webget/tcp_ipv4 to send requests

bugs

  • tcp_receive 中,增加 _ackno 时,需要使用 实际写入数据的 absolute_seqno

LISTEN 状态

  • 等待 SYN,其他的没有什么特别的地方

  • TCP 重点就是 connection,而 listen 仅仅是 创建一个 tcp connection,等待 SYN

  • client 调用 connect 是 发送 一个 SYN,通过调用 _sender.fill_window(),_reveiver 没有 初始化 _ackno,所以 不带 ACK

  • server 收到 后,_receiver 初始化了 _ackno, _sender 调用 fill_window(),所以 SYN 会带 ACK

重点

  • 查看 tcp_state.hh/.cc 查看 tcp 的 状态转换

  • 查看 如何使用 tun 和 tap

  • 查看如何 写测试

  • 查看工程经验,eg: list 和 map 删除当前节点

  • container.erase(iter++); => iter 已经 指向下一个,而且还可以使用当前的值

  • 不需要使用中间变量

  • 最后画图,看 sponge 网络库的实现

  • tcp 状态转换图,查看 RFC 的文档

下一步计划

- 首先修改 receiver 和 Reassembler,由 Reassembler 全权处理数据
- 理清每一个组件的关系 和 功能,重新设计类的实现
- 	使用真正面向对象的方法
- 理清各个组件的功能之后,将其总结在 一个 博客文章中,待下次实现时 参考,尤其是 其中的一些坑点
  • 查看别人的实现,慢慢修改 自己的实现,画图理清逻辑
    • eg: unwrap 的 实现
    • receive 查看数据有效性的 实现
      
    • 如何更好地组织代码
      

cs144 框架

  • webget 应用层 => lab0

  • tcp 传输层 => lab0 (bytestream), lab1 (reassembler), lab2 (sender), lab3 (receiver), lab4 (connection)

  • ip 网络层 => lab 自带

  • 2.5 layer

  • network interface arp 协议 => lab5 (ens33, lo, tun144, tap10)
    - 重点是 接收 和 解封 EthernetFrame,可能时 ARP 包,也可能是 IPV4 包
    - 可能是 ARP Request 或 Response
    - 需要维护一个 cache for ip to mac

  • link 以太网 frame,lab 自带

  • router

    • 进行匹配,最长前缀匹配,然后进行转发,转发到 对应 的 network interface 即可

    • 如果有 next_hop,说明目的地址在下一个路由器内部

    • 如果没有 next_hop,说明目的地址 在 路由器

    • 有多个 network interface

    • router 只需要改变 目的 IP 地址即可,即 只查看 IP 数据包

    •  不 查看 EthernetHeader 和 TCPHeader
      
    •  不需要管 端口号 和 MAC 地址,ARP 由 network interface 自己去做
      
    •  保持最大程度的简单
      
    • route table entry

    • 一个路由器有多个 interface num
      
    • route_prefix    prefix_length(mask)   next_hop    interface num
      
    • 子网前缀             子网掩码           下一跳         网络接口号
      
    • route_prefix 可能是 0.0.0.0,即 默认路由
      

tcp in udp in ip

  • 使用 udp 传输 tcp 的 数据,可以 使用 kernel 提供的 API

tcp in ip

  • tun network interface 以上的包头 application 添加

tcp in ip in Ethernet

  • tap network interface 也自己添加
posted @ 2023-08-16 21:34  chumoath  阅读(71)  评论(0)    收藏  举报