CS144-2
CS144-2
幻雪CS144-2
检查
在实验2之前,要保证之前的实验1已经通过测试cmake --build build --target check 1
如果出现如下就可以继续实验了
获取实验文件git fetch
来检索实验分配的最新版本git merge origin/check2-startercode
然后同步这次的实验代码
在这一部分中,你需要实现TCPReceiver,和一个wrapping_integers这是我们整个实验中的端口的服务端和绝对/相对序列号的转换
在这实验之前,你需要了解报文中的确认号(ackno),序列号(seqno)以及连接请求(SYN)和断开请求(FIN),了解这些就足以应对这个实验。
你需要知道的是以上四个量在传递过程中的值的变化,最好是自己尝试画出来
实验开始
首先这一部分难的地方并不在于去理解发送中的状态更新,而是在于如何在适当的时刻去利用相对位置和绝对位置这两个状态,而且要实现这两个转换个,如果在实现了转换的部分,同时如果这个在收发过程想明白,那么这个实验容易通过的。
首先你需要知道,当2的32次方超过就会溢出,如果是无符号型就会回滚到0,所以我们需要利用这一点,直接强转,让绝对位置变成相对位置。我当时对于unwrap的实现思路是算出有多少个循环(也就是2^32有多少个)然后把检查点左边的和右边的最近求出来,比较一下距离谁近就可以了。
关于这一部分,我之前的思路是直接用相对位置来比较检查点的距离,然后直接加上循环几圈,但是后面出现了端点是检查点的情况,以及端点是序列号的情况,这导致后面一直出现边界问题,所以倒不如直接求出来两个绝对位置,然后求他们到检查点的距离来得要巴适。
对于这一部分如果不清楚的话我们可以做一道题
时钟问题:一台机器每个一段时间都会记录累计的工作总小时数 m,每一个人只知道最后记得的工作时间在24刻度的时钟的 n 刻度上,小明现在只知道 n 和 m 如何求出总的工作实际数 g?
(m-n)%24就是求出从 n 到 m 有多少个完整的周期,然后 24 x (m-n)%24+n 就是最后的结果
换而 lab 2言之 (m-n)>>32<<32+n
这一部分可以琢磨一下,我当时琢磨了两天(泪目),当然实现起来还是以上的扩展版本,要多注意边界问题。
其次是你需要知道的结构体,第一个是TCP应答消息,第二个是TCP的发送消息
然后需要好好思考这些结构体的成员变量如何联系到本次实验的主题,以及如何在调用函数的时候用到他们,同时需要按F12去查看对应的成员变量类型是否有成员函数,清楚了这些函数作用可以极大减少代码量,这需要你自己去发现,因为这部分文档中也并没有直接给出来,估计故意是让大家思考的。
tcp_receiver.cc1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
using namespace std;
void TCPReceiver::receive( TCPSenderMessage message, Reassembler& reassembler, Writer& inbound_stream )
{
/*
需要使用到的有inser函数
:
Reassembler::insert( uint64_t first_index, string data, bool is_last_substring, Writer& output )
绝对序号,data,是不是最后一个数据包,output系统
而output是一个Writer类型,需要外界提供,所以目光在inbound_stream
message.payload是数据字段
所以写出来应该是
reassembler.insert(message.seqno,message.payload,message.FIN,inbound_stream)
但是应该分别为连接请求和确认和中间段包,和挥手包四种可能
而reassembler.insert(message.seqno,message.payload,message.FIN,inbound_stream)
已经包涵了FIN包的处理,以及SYN包的空包处理,也就是说,需要关心的也就first_index这个接口
当SYN=1时是SYN包,此时没有数据,且设定了初始的绝对位置,此时应该是不需要传递到insert中的
当SYN=0时是中间段,此时的seqno就是我们的数据index
*/
//此处我不知道SYN如果多发了几次要怎么处理,然后还是不同的情况下,一般来说一个是再开一个线程
//如果此时我们的SYN包接受到,那么我们保存我们已经接受到SYN包,保存一下状态,以及初始的序列号
if(message.SYN){
//如果接收到了就保存这个状态
SYN_received=true;
//第一次开启的时候会记录一下零点的位置
zero_point=message.seqno;
FIN_received=message.FIN;
}
abs_seq=message.seqno.unwrap( zero_point, inbound_stream.bytes_pushed() );
//排除掉我们可能出现的SYN情况,FIN包他本身就可以处理
if(SYN_received){
//insert接受的是下一个abs_seq的下标,而abs_seq是依据当前流系统接收的包,拆包后的长度-1是因为只改变index后的数据,以及是否是SYN包是为了包涵当开头时,是0,没有向前的下标了
reassembler.insert( abs_seq+ message.SYN - 1,message.payload,message.FIN,inbound_stream);
if(FIN_received){
//后面可以用作半退出状态去接收数据
half_close=true;
}
}
}
TCPReceiverMessage TCPReceiver::send( const Writer& inbound_stream ) const
{
/*
这里就应该发送SYN包和ACK包,问题是要一起发送还是发送两次?
看了一下结构体确实是需要发送两次,但是他返回的就一种,那算了,就返回ACK包就行了
直接通过返回来返回给对方?
窗户应该设置多大?
那么我认为inbound_stream似乎没有什么用
然而是用来获取检查点的
*/
//由于我们一次连接只需要对SYN响应一次,所以我们响应过一次就设置为false防止误用,这条废除,因为后续连接需要保存syn
TCPReceiverMessage message_ACK;
//SYN包:
uint64_t res = inbound_stream.available_capacity();
message_ACK.window_size = res > UINT16_MAX ? UINT16_MAX : res;
if(!SYN_received){
return message_ACK;
}
//这一步网上看到的是用inbound_stream.bytes_pushed当做要装包的数据,我寻思难道不是seqno?
//这是因为seq插入后,不一定是完全的包,我们会经历合并等等操作,最后返回的应该是我们接受到的有效长度,所以用的是inbound_stream.bytes_pushed()
//+1是为了当有效长度的一个确认,然后是否是FIN包,我们也需要确认如果是最后一个包,我们分别需要对其seq确认,和FIN包确认,
//正版的是分开两次确认不知道为什么这两必须是同时合并一步确认
message_ACK.ackno=temp.wrap( inbound_stream.bytes_pushed() + 1 + inbound_stream.is_closed(), zero_point );
return message_ACK;
}
tcp_receiver.hh1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class TCPReceiver
{
public:
TCPReceiver():SYN_received(false),half_close(false),FIN_received(false),zero_point(0),temp(0),abs_seq(0){}
/*
TCPReceiver接收TCPSenderMessages,将它们的有效载荷插入到正确的流索引的Reassembler中。
*/
void receive( TCPSenderMessage message, Reassembler& reassembler, Writer& inbound_stream );
/* TCPReceiver将TCPReceiverMessages发送回TCPSender。 */
TCPReceiverMessage send( const Writer& inbound_stream ) const;
private:
//是不是正在连接中
bool SYN_received;
//是不是完成了半关闭
bool half_close;
//用来标记结束
bool FIN_received;
//为ack数做准备
//初始的绝对长度
Wrap32 zero_point;
Wrap32 temp;
uint64_t abs_seq;
};
wrapping_integers.cc
1 |
|
wrapping_integers.hh
1 |
|