CS144-3
![](https://cdn.jsdelivr.net/gh/huanxueshengmou/picture-host/logo.png)
CS144-3
幻雪CS144-3
如果已经通过了lab2的测试,就可以开始lab3
然后就可以开始同步实验文件了
cmake --build build --target check 3
实验开始
这次需要实现的其实也就四个函数,其他函数只需要调用头文件中维护的变量
我当时卡在不知道tick要如何实现,不让调用系统时间,只通过传参要怎么实现,说到底还是不了解这个传参的作用,其实每一次该函数被调用结束,都会重置为0,换一句话说,就是调用到第二次调用所经历的时间间隔就是传参的值,相当于一个随机的数,你需要的就是处理这个传参。
代码实现
接下来我讲讲我的实现思路,想自己思考的可以去实验了。
整体上,需要找到一个数据结构来存放我们的数据,我这里使用的是deque,因为数据的增删频繁,用queque也可以,但是实现起来复杂,没有必要。
我们创建两存储,一个是已经发送的但是未确认的,一个是准备发送的报文段。push函数用来把需要发送的报文段放入准备发送的deque类型存储,receive函数用来改变已经发送但未确认的deque类存储。时钟的开关用一个变量来维护,当发送出去后就开启时钟,如果当前发送但未确认的deque类存储中没有元素那么表明这个阶段的所有数据均发送过去,可以重置有关时钟的参数,如RTO。具体可以看我的代码和代码中的注释。
TCP_sender.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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
using namespace std;
/* TCPSender constructor (uses a random ISN if none given) */
TCPSender::TCPSender( uint64_t initial_RTO_ms, optional<Wrap32> fixed_isn )
: isn_( fixed_isn.value_or( Wrap32 { random_device()() } ) ), initial_RTO_ms_( initial_RTO_ms )
,RTO_ms_( initial_RTO_ms )
{}
//返回当前在传输中的序列号数量,也就是已经发送但还未收到确认的数据包数量。
uint64_t TCPSender::sequence_numbers_in_flight() const
{
return abs_seqno-por_abs_ackno;
}
//返回连续重传的次数,即同一数据包被重传的次数。
uint64_t TCPSender::consecutive_retransmissions() const
{
// Your code here.
return retransmissions;
}
//该方法尝试发送
optional<TCPSenderMessage> TCPSender::maybe_send()
{
// Your code here.
//是否没有准备
if(_ready_collections.empty()){
return nullopt;
}
//如果准备了的话
TCPSenderMessage maybe_send_Message;
maybe_send_Message=_ready_collections.front();
//缓存数据(空包不要缓存)
if(maybe_send_Message.sequence_length()==0){
return maybe_send_Message;
}
_outstanding_collections.push_back(maybe_send_Message);
_ready_collections.pop_front();
//让时钟启动
tick_start=true;
return maybe_send_Message;
}
//该方法初定为去放数据
void TCPSender::push( Reader& outbound_stream )
{ // Your code here.
//循环去处理每一个数据,保证其空间可以放入数据即可
//假设看看剩余空间是否仍然有空余(sequence_numbers_in_flight() 的最大值就是 window_size)不会产生uint64_t回滚
uint64_t available_window_space =max(static_cast<uint64_t>(1), window_size)-sequence_numbers_in_flight();
//用于测试
while (available_window_space>0)
{
// cout <<n<< "available_window_space1:"<<available_window_space<<","<<endl;
//建立一个默认的消息,范围在0到MAX_PAYLOAD_SIZE
TCPSenderMessage NewMessages;
size_t NewMessages_length=(static_cast<size_t>(available_window_space))>=TCPConfig::MAX_PAYLOAD_SIZE?
TCPConfig::MAX_PAYLOAD_SIZE:(static_cast<size_t>(available_window_space));
read(outbound_stream,NewMessages_length,NewMessages.payload);
available_window_space-=NewMessages.payload.size();
//是否需要SYN包装
if(!has_SYN){
has_SYN=true;
NewMessages.SYN=true;
NewMessages.seqno=isn_;
}else{
NewMessages.seqno = Wrap32::wrap( abs_seqno, isn_ );
}
//是否需要FIN包装
if(!has_FIN&&outbound_stream.is_finished()&&available_window_space>0){
has_FIN=true;
NewMessages.FIN=true;
}
//是否可以退出(只有当空间不足够以及到结尾以后才会产生空包)
if(!NewMessages.SYN&&!NewMessages.FIN&&NewMessages.payload.size()==0){
return;
}
//用于测试
// n++;
// cout <<n<< "psuh:"<<has_SYN<<","<<endl;
// cout <<n<< "available_window_space2:"<<available_window_space<<","<<endl;
// cout <<n<< "window_size:"<<window_size<<","<<endl;
// cout <<n<< "sequence_numbers_in_flight():"<<sequence_numbers_in_flight()<<","<<endl;
// cout <<n<< "NewMessages.sequence_length():"<<NewMessages.sequence_length()<<","<<endl;
// cout <<n<< "abs_seqno:"<<abs_seqno<<","<<endl;
//均可以放入
//更新下次的位置
_ready_collections.push_back(NewMessages);
abs_seqno += NewMessages.sequence_length();
}
// //当对方没有空间的时候,我们仍然假设他有一个,让他更新窗口
// uint16_t window_size=new_win_r-new_win_l; //获取窗口的大小
// if(window_size==0){
// _ready_collections.push_back( send_empty_message());
// return;
// }
// //保证已经建立好传输
// if(has_SYN){
// //生成尽可能多的TCPSenderMessage
// while(window_size!=0){
// TCPSenderMessage NewMessages;
// uint16_t NewMessages_length=window_size>=
// TCPConfig::MAX_PAYLOAD_SIZE?
// TCPConfig::MAX_PAYLOAD_SIZE:window_size;
// window_size-=NewMessages_length;
// //如果是最后一段,那么我们就标识FIN包
// if(outbound_stream.is_finished()){
// NewMessages.FIN=true;
// }
// //只给我看我怎么构建信息啊,我直接引用?
// NewMessages.payload{&outbound_stream.peek()};
// outbound_stream.pop(NewMessages_length);
// NewMessages.seqno=temp.wrap( outbound_stream.bytes_popped() + 1 + outbound_stream.is_finished(), zero_point );
// total__ready_collections_length=NewMessages.seqno;
// _ready_collections.push_back(NewMessages);
// };
// }else{
// //如果没有建立连接就先发出SYN包
// TCPSenderMessage Message_SYN;
// Message_SYN.SYN=true;
// Message_SYN.seqno=isn_;
// zero_point=isn.raw_value_;
// _ready_collections.push_back(Message_SYN);
// has_SYN=true;
// }
(void)outbound_stream;
}
//该方法初定为发送空数据
TCPSenderMessage TCPSender::send_empty_message() const
{
// Your code here.
//首先是定义一段空数据
return TCPSenderMessage{Wrap32::wrap(abs_seqno, isn_ ), false, Buffer(), false};
}
//该方法设置窗口大小,同时删除无用段
void TCPSender::receive( const TCPReceiverMessage& msg )
{
// Your code here.
//获得窗口左和窗口右(由于窗口大小是uint16MAX所以设置为uint16MAX)
window_size=msg.window_size;
//获得abs_ackno
if(msg.ackno.has_value()){
abs_ackno=msg.ackno.value().unwrap( isn_, por_abs_ackno );
}
//确认新的确认码的有效性
if(!msg.ackno.has_value()||abs_ackno>abs_seqno||abs_ackno<por_abs_ackno){
return;
}
// 删除已经确认的段
while (!_outstanding_collections.empty() ) {
if( _outstanding_collections.front().seqno.unwrap(isn_,abs_ackno) +_outstanding_collections.front().sequence_length() - 1 >= abs_ackno){
break;
}
_outstanding_collections.pop_front();
// RTO设置
cur_time = 0;
RTO_ms_ = initial_RTO_ms_;
retransmissions = 0;
// cout << "receive"<<RTO_ms_<<endl;
}
//设置时钟关闭
if(_outstanding_collections.empty()){
tick_start=false;
}
//更新确认
por_abs_ackno=abs_ackno;
(void)msg;
}
//该段设置为更新时间
void TCPSender::tick( const size_t ms_since_last_tick )
{
// Your code here.
//是否建立启动了时钟,是否建立了连接
if(!tick_start||!has_SYN){
// cout <<n<< "是否建立启动了时钟,是否建立了连接"<<"tick_start:"<<tick_start<<","<<"has_SYN:"<<has_SYN<<endl;
return;
}
// //测试temp
// uint64_t temp=cur_time;
cur_time+=ms_since_last_tick;
// cout <<n<< "赋值时的时间"<<"cur_time:"<<temp<<","<<"ms_since_last_tick:"<<cur_time-temp<<endl;
//是没有超时的话返回
if(cur_time<RTO_ms_){
// cout <<n<< "是没有超时的话返回"<<"cur_time:"<<cur_time<<","<<"RTO_ms_:"<<RTO_ms_<<endl;
return;
}
//RTO退避
if((window_size)>0){
RTO_ms_*=2;
// cout <<n<< "RTO退避"<<"RTO_ms_:"<<RTO_ms_<<","<<"window_size:"<<window_size<<endl;
}//从新放到我们准备发送的deque
if(!_outstanding_collections.empty()){
_ready_collections.push_front(_outstanding_collections.front());
retransmissions++;
cur_time=0;
// cout <<n<< "从新放到我们准备发送的deque"<<"cur_time:"<<cur_time<<","<<"retransmissions:"<<retransmissions<<endl;
}
// cout<<n<<"最后的所有参数一览"<<"ms_since_last_tick"<<ms_since_last_tick<<","
// <<"has_SYN:"<<has_SYN<<","
// <<"tick_start:"<<tick_start<<","
// <<"cur_time:"<<cur_time<<","
// <<"RTO_ms_:"<<RTO_ms_<<","
// <<"retransmissions:"<<retransmissions<<endl;
(void)ms_since_last_tick;
}
TCP_sender.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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
using namespace std;
class TCPSender
{
Wrap32 isn_;
uint64_t initial_RTO_ms_;
uint64_t RTO_ms_;
uint64_t window_size=1;
uint64_t cur_time=0;//这次的总毫秒数
uint64_t abs_seqno=0;//记录下次的绝对位置
uint64_t por_abs_ackno=0;//记录上次的绝对位置
uint64_t abs_ackno=0;//记录下次的绝对位置
uint8_t retransmissions=0;//记录重传次数
bool tick_start=false;//是否启动了时钟
bool has_SYN=false;//连接与否
bool has_FIN=false;//是否尝试断开
std::deque<TCPSenderMessage> _outstanding_collections={}; // 记录所有已发送未完成的段
std::deque<TCPSenderMessage> _ready_collections={}; // 记录所有准备好发送的段
public:
/* Construct TCP sender with given default Retransmission Timeout and possible ISN */
/* 使用给定的默认重传超时和可能的ISN构造TCP发送者 */
TCPSender( uint64_t initial_RTO_ms, std::optional<Wrap32> fixed_isn );
/* Push bytes from the outbound stream */
/* 从出站流中推送字节 */
void push( Reader& outbound_stream );
/* Send a TCPSenderMessage if needed (or empty optional otherwise) */
/* 如果需要发送TCPSenderMessage,则发送(否则返回空的optional) */
std::optional<TCPSenderMessage> maybe_send();
/* Generate an empty TCPSenderMessage */
/* 生成一个空的TCPSenderMessage */
TCPSenderMessage send_empty_message() const;
/* Receive an act on a TCPReceiverMessage from the peer's receiver */
/* 接收并响应对端接收者的TCPReceiverMessage */
void receive( const TCPReceiverMessage& msg );
/* Time has passed by the given # of milliseconds since the last time the tick() method was called. */
/* 自上次调用tick()方法以来,时间已经过去了给定的毫秒数 */
void tick( uint64_t ms_since_last_tick );
/* Accessors for use in testing */
/* 用于测试的访问器 */
uint64_t sequence_numbers_in_flight() const; // How many sequence numbers are outstanding?// 有多少序列号正在传输中?
uint64_t consecutive_retransmissions() const; // How many consecutive *re*transmissions have happened?// 发生了多少次连续的*重*传输?
};