Lab汇总
概述
lab4要求实现一个将 T C P R e c e i v e r TCPReceiver TCPReceiver与 T C P S e n d e r TCPSender TCPSender结合起来的 T C P C o n n e c t i o n TCPConnection TCPConnection,实现真正意义上的 T C P TCP TCP实体。当收到一个 T C P S e g m e n t TCPSegment TCPSegment时, T C P C o n n e c t i o n TCPConnection TCPConnection需要将该报文分别交付给 T C P R e c e i v e r TCPReceiver TCPReceiver与 T C P S e n d e r TCPSender TCPSender进行解析(调用 R e c e i v e r Receiver Receiver和 S e n d e r Sender Sender中相应的接口),进行全局的统筹协调。当经过一定的时间段,操作系统会调用 C o n n e c t i o n Connection Connection中的 t i c k tick tick函数进行时间记录(方便测试), C o n n e c t i o n Connection Connection将通知 S e n d e r Sender Sender此时经过的时间,方便 S e n d e r Sender Sender进行超时重传。
实现细节
TCP实体
T C P TCP TCP实体由三部分组成:
-
接收方:当从网络中接收到一个 T C P TCP TCP报文段时,会调用 T C P C o n n e c t i o n TCPConnection TCPConnection中
segment_received
接口进行处理-
如果报文段头部中的
RST
标记被设置为1,则对该 T C P TCP TCP实体执行非洁净关闭( u n c l e a n s h u t d o w n unclean\ shutdown unclean shutdown)操作。 -
否则
- 将该TCP报文段交给
R
e
c
e
i
v
e
r
Receiver
Receiver处理(通过
R
e
c
e
i
v
e
r
Receiver
Receiver的
segment_received
接口) - 且如果该
T
C
P
TCP
TCP报文段设置了
ACK
标志,则将其中的ackno
字段与window_size
字段交给 S e n d e r Sender Sender进行更新(通过ack_received
接口)。 - 当收到的报文段占有字节序号时(
length_in_sequence_space
返回值不为零), C o n n e c t i o n Connection Connection需要确保其至少回应一个报文段,以更新此时 R e c e i v e r Receiver Receiver中维护的ackno
和window_size
。 - 最后有一个特殊情况需要处理,对方终端可能会发送一个拥有不合法字节序号的报文用来检测你的
T
C
P
TCP
TCP实体是否仍然存活,此时需要发送一个空报文进行回复(会携带
ackno
和window_size
)。这种情况下的处理代码如下:(从给定的代码中可以看出,这种情况应该是规定好的,跟着写就是了)
if (_receiver.ackno().has_value() && (seg.length_in_sequence_space() == 0) && seg.header().seqno == _receiver.ackno().value() - 1) { _sender.send_empty_segment(); }
- 将该TCP报文段交给
R
e
c
e
i
v
e
r
Receiver
Receiver处理(通过
R
e
c
e
i
v
e
r
Receiver
Receiver的
-
-
发送方:每隔一段时间将调用发送方的接口对输入缓冲区中的字节流进行打包并发送
- 每隔一段时间(当
C
o
n
n
e
c
t
i
o
n
Connection
Connection中的
t
i
c
k
tick
tick函数被调用的时候),如果
S
e
n
d
e
r
Sender
Sender中的输入缓冲区
stream_in
已经被写入字节,那么需要调用 S e n d e r Sender Sender中的fill_window
接口进行字节流的打包和发送 - 在发送
T
C
P
TCP
TCP报文段之前,
C
o
n
n
e
c
t
i
o
n
Connection
Connection需要询问
R
e
c
e
i
v
e
r
Receiver
Receiver当前的
ackno
和window_size
,并添加到每一个报文段的相关字段中(如果ackno
存在,则设置ACK
标记为1)
- 每隔一段时间(当
C
o
n
n
e
c
t
i
o
n
Connection
Connection中的
t
i
c
k
tick
tick函数被调用的时候),如果
S
e
n
d
e
r
Sender
Sender中的输入缓冲区
-
时间流逝:当 C o n n e c t i o n Connection Connection中的 t i c k tick tick接口被调用时
- 调用
S
e
n
d
e
r
Sender
Sender中的
tick
接口告知时间流逝 - 如果此时超时重传次数超出上限,则进行非洁净关闭
- 如果此时满足洁净关闭的要求(见下一节),则执行洁净关闭
- 调用
S
e
n
d
e
r
Sender
Sender中的
关闭TCP连接
- 不洁净关闭:
当接收或发送一个带RST
标记的 T C P TCP TCP报文段时,将会启动不洁净关闭,即将当前 T C P TCP TCP实体的输入输出字节流都设置为出错状态,并将此实体设置为未激活状态(active()
接口返回false
)。 - 洁净关闭:
如果本地 T C P TCP TCP想要进行洁净关闭,首先要满足三个条件:
1、接收方的inbound stream
已经拼接完全且输入停止(input ended)
2、发送方的outbound stream
已经停止输入且被完全发出(即已经eof)
3、发送方发出的所有报文段均被确认
当本地 T C P TCP TCP满足以上三个条件,并确认对方 T C P TCP TCP已经满足上述三个条件时,可以进行洁净关闭,又分为以下两种情况:- 本地
T
C
P
TCP
TCP的
inbound stream
停止输入(input ended)但outbound stream
还未到达eof(即还未发送完,但是已经接受完了),那么此时对方 T C P TCP TCP已经满足上述的条件2;当本地 T C P TCP TCP满足上述三个条件时,它能够百分百确定对方 T C P TCP TCP已经满足三个条件。因为此时,对方 T C P TCP TCP必然满足条件1和2,且由于对方 T C P TCP TCP的数据比本地 T C P TCP TCP的数据早发完,则本地 T C P TCP TCP在发送剩余数据时,必然会携带对远程 T C P TCP TCP所有 T C P TCP TCP报文段的确认号,又因为本地 T C P TCP TCP发送方的所有报文均被确认,故能够确认对方 T C P TCP TCP肯定收到过自己的确认号,即已经满足条件3. - 本地 T C P TCP TCP不能够百分百的确认对方已经收到所有的确认号,故需要等待一段时间(10*_cfg.rt_timeout),如果在时间内未收到对方 T C P TCP TCP的报文,则说明对方大概率已经收到所有的确认,因为没有产生超时重传。
- 本地
T
C
P
TCP
TCP的
坑点
- 一开始不太清楚
time_since_last_segment_received
这个字段怎么维护,查阅了相关博客得知,由于系统是通过调用 t i c k tick tick函数来跟踪时间的,于是只需要在 t i c k tick tick函数被调用时累加即可,在接收到 T C P TCP TCP报文时将累计的时间情况即可。 - 在
C
o
n
n
e
c
t
i
o
n
Connection
Connection的
t
i
c
k
tick
tick函数被调用时,需要进行报文段的发送(即调用
S
e
n
d
e
r
Sender
Sender的
fill_window
接口)。注意如果 S e n d e r Sender Sender的输入缓存区如果还未写入字节的话,则不需要调用fill_window
进行报文段的发送,否则会进入到SYN_SENT
状态从而出错。