CS144 lab2 TCPReceiver实现笔记
3 实现TCP接收器
在本实验的其余部分,您将实现 TCPReceiver。它将 (1) 从其对等方接收Segment,(2)使用您的 StreamReassembler 重新组装 ByteStream,并计算 (3) 确认号 (ackno) 和 (4) 窗口大小。
首先,请查看 TCP 段的格式。这是数据报的结构两个端点相互发送的有效载荷。确认和窗口大小都是字段TCPSegment 结构。
您可能需要注意length_in_sequence_space()方法,它计算一个段占据多少序列号(包括事实SYN 和 FIN 都按顺序计为一个位置,以及有效负载的每个字节)。
接下来说一下你的 TCPReceiver 将提供的接口:
//! \brief The ackno that should be sent to the peer std::optional<WrappingInt32> ackno() const; //! \brief The window size that should be sent to the peer size_t window_size() const;
//! \brief number of bytes stored but not yet reassembled size_t unassembled_bytes() const { return _reassembler.unassembled_bytes(); } //! \brief handle an inbound segment void segment_received(const TCPSegment &seg);
TCPReceiver 是围绕您的 StreamReassembler 构建的。我们已经实施了.hh 文件中的构造函数和未组装的字节和流输出方法。
3.3.1 segment_received()
这是主要的方法!
该方法需要:
• 如有必要,设置初始序列号。第一个的序列号 -设置了 SYN 标志的到达段是初始序列号。你会想要跟踪它,以便在 32 位封装的 seqnos/acknos 之间进行转换以及它们的绝对等价物。(请注意,SYN 标志只是标头中的一个标志。同一段也可以携带数据,甚至可以设置 FIN 标志。)
• 将任何数据或流结束标记推送到StreamReassembler。如果FIN 标志设置在 TCPSegment 的标头中,这意味着流以最后一个负载的字节——它相当于一个 EOF。
• 确定线段的任何部分是否落在窗口内。这种方法
如果段的任何部分落入窗口内,则应返回 true,否则返回 false-
否则。2
这就是我们的意思:一个段占据一个序列范围
数字——从它的序列号开始的范围,长度等于它的
序列 space() 中的长度(这反映了 SYN 和 FIN 每个计数的事实
一个序列号,以及有效载荷的每个字节)。一个段是可以接受的
(并且该方法应该返回true)如果它占据的任何序列号下降
在接收器的窗口内。
有一些特殊情况:
– 如果尚未设置 ISN,则当(且仅当)具有SYN 位设置。
– 如果序列空间中的段长度为零(例如,只是一个裸确认没有有效载荷,也没有 SYN 或 FIN 标志),那么它的大小应该被视为一个字节,用于确定它占用的第一个和最后一个序列号。
3.3.2 ackno()
返回一个可选的<WrappingInt32> 包含接收者还不知道第一个字节的序列号。这是窗口的左边缘:接收器有兴趣接受的的第一个字节。
1如果尚未设置 ISN,则返回一个空的可选。
2如果为 false,则 TCPSender 不知何故弄糊涂了,需要对窗口进行更正。
3这一可接受性定义是对 RFC 793,第 25 至 26 页中定义的轻微改编。
3.3.3 window_size()
实验结果:
实验总结:
本实验是实现一个TCP接收端,接受TCPsegment,需要提前了解项目中tcp_helper包下的TCPSegment类
需要对syn,fin进行判断处理,提取data并发送到流重组器。
实现细节:
1.序列号与绝对序列号转换
由于seg序列号只有32位,并且为了提高网络安全性,初始字节号(isn)是随机的,可能会越界,我们需要添加绝对序列号,
而绝对序列号是流序列号去除syn和fin,所以我实现的时候直接用的流序列号,把第一个字节(图中的'c'字节)作为isn,而不是syn。
绝对序列号和序列号之间的转换在wrap和unwrap中实现,checkpoint为已经重组字节流的大小
2.窗口大小
窗口大小就是(总容量-字节流缓冲区容量)
3.syn和fin控制
如果还没收到syn,除非该包带有syn,否则就丢弃该包。
如果fin=true,数据读入流重组器时标记eof=true;
4.判断是否可接受
先得到窗口两端以及seg数据起始字节的绝对序列号,只要数据和窗口有交集,则将seg所带data读入流重组器
5.ackno
接收端发送的ackno的范围是syn_seqno+1到fin_seqno+fin_datasize+1。
字节流读取的字节长度就是已近重组的数据长度,也就是我们需要返回的下一个需要发送字节的绝对序列号,调用wrap把其转换为32位序列号返回即可。
需要注意的是,如果数据完全被重组,需要返回fin的确认信号,而不再是最后一个重组字节的确认信号。
代码:
wrapping_integers.cc
const uint64_t max_int=4294967296; using namespace std; WrappingInt32 wrap(uint64_t n, WrappingInt32 isn) { WrappingInt32 res(static_cast<uint32_t>((static_cast<uint64_t>(isn.raw_value())+n)%max_int)); return res; } uint64_t unwrap(WrappingInt32 n, WrappingInt32 isn, uint64_t checkpoint) { uint64_t mid = (max_int - static_cast<uint64_t>(isn.raw_value()) + static_cast<uint64_t>(n.raw_value())) % max_int; mid += (checkpoint / max_int) * max_int; uint64_t right = mid + max_int; uint64_t left = mid - max_int; if (mid > checkpoint) { if (mid - checkpoint < checkpoint - left||mid<max_int) { return mid; } else { return left; } } else { if (right - checkpoint < checkpoint - mid) { return right; } else { return mid; } } }
tcp_receiver.hh
class TCPReceiver { //! Our data structure for re-assembling bytes. StreamReassembler _reassembler; //! The maximum number of bytes we'll store. size_t _capacity; bool recieve_isn=false; WrappingInt32 isn=WrappingInt32(0u); uint64_t fin_off_set=UINT64_MAX;
.........
tcp_receiver.cc
#include "tcp_receiver.hh" #include<iostream> // Dummy implementation of a TCP receiver // For Lab 2, please replace with a real implementation that passes the // automated checks run by `make check_lab2`. const uint64_t max_int=4294967296; using namespace std; void TCPReceiver::segment_received(const TCPSegment &seg) { if(seg.header().syn){ recieve_isn=true; isn=wrap(1ull,seg.header().seqno); } if(!recieve_isn){ return ; } const uint64_t check_point=static_cast<uint64_t>(_reassembler.stream_out().bytes_written()); const uint64_t win_left=check_point; const uint64_t win_right=static_cast<uint64_t>(_capacity-_reassembler.stream_out().buffer_size())+win_left-1; const uint64_t seg_start=seg.header().syn?0:unwrap(seg.header().seqno,isn,check_point); if(seg_start>win_right||seg_start+seg.length_in_sequence_space()<=win_left){ return ; } string data=string(seg.payload().str().data(),seg.payload().size()); if(seg.header().fin){ fin_off_set=seg_start+data.size(); } _reassembler.push_substring(data,seg_start,seg.header().fin); } optional<WrappingInt32> TCPReceiver::ackno() const { if(recieve_isn){ if(fin_off_set==_reassembler.stream_out().bytes_written()){ return wrap(1ull+static_cast<uint64_t>(_reassembler.stream_out().bytes_written()),isn); }else{ return wrap(static_cast<uint64_t>(_reassembler.stream_out().bytes_written()),isn); } } return {}; } size_t TCPReceiver::window_size() const { return _capacity-_reassembler.stream_out().buffer_size(); }