具体实现细节请参考 我的实现
Lab2 Start
这个实验要求实现一个TCPReceiver类
这个类是基于前两个实验,所以要熟悉前两个实验完成的接口
越靠后的实验,越是在封装上进行编程,如果忘记了,记得回去看接口
从这个实验开始,我们就真正设计到tcp协议中的内容,包括但不限于标志位,重发等机制
以下是各种标志位的意义
SYN (Synchronize) - 同步序列号
用于建立连接 ACK (Acknowledgment) - 确认标志 用于确认收到数据 RST (Reset) - 重置标志 重置连接
在一个tcp连接中可以同时有多个标志位,上述只是大致阐述,但在实现中仍旧有很多值的注意的细节
(预告:在Lab3中你将要同时处理各种标志位带来的情况)
TCPReceiver
接收函数
void TCPReceiver::receive( TCPSenderMessage message );
这个函数功能很清晰明了,提供一个TCPSenderMessage,我们需要进行解析,并且推送到*流重组器接口
那么思路也是十分简单,但是要注意处理结构体TCPSenderMessage中的标志位
但是接收信息还需要记录isn,和维护状态
std::optional<Wrap32> isn_ {}; // 记录初始序列号
bool synced_ {}; // 是否收到sycn
需要在私有成员中添加这两个成员变量
程序的思路是这样的:
收到SYN的数据包后,就设置isn(起始seqno)
(在这个过程中SYN和FIN包都可以携带payload)
随后根据isn计算abs_seqno,计算first_index -> 流重组器接口需要
最后写入流重组器
void TCPReceiver::receive( TCPSenderMessage message )
{
// 处理重连标志
if ( message.RST ) {
reassembler_.reader().set_error();
}
// 处理SYN标志
if ( message.SYN ) {
isn_ = message.seqno;
synced_ = true;
}
if ( !synced_ )
return;
uint64_t checkpoint = reassembler_.writer().bytes_pushed();
uint64_t abs_seqno = message.seqno.unwrap( isn_.value(), checkpoint );
// SYN占一个序号
// 这里如果有SYN建立连接的信号,就让SYN(first_index)占第一个(索引为0)的位置
uint64_t first_index = ( message.SYN ) ? 0 : abs_seqno - 1;
reassembler_.insert( first_index, std::move( message.payload ), message.FIN );
}
需要结合理解的就是checkpoint和abs_seqno的计算,结合前面Lab1的笔记理解它的封装和设计思路
可以参考这个博客,讲述很清晰
