当前位置:网站首页>【斯坦福计网CS144项目】Lab3: TCPSender
【斯坦福计网CS144项目】Lab3: TCPSender
2022-07-07 04:13:00 【Altair_alpha】
本节进行 TCP 协议中发送端 TCPSender 的实现。Sender 拥有一个输入的 ByteStream,代表用户提供的待发送的数据,而 Sender 要负责将其组装成 TCP 数据包并发送出去。所谓发送,就是将数据包 push 到一个队列 _segments_out
中即可,下一节实现的 TCPConnection 类(也就是 Sender 和 Receiver 的所有者)会负责从该队列中取走数据包并实际发出。
除了保证 TCPSegment 中数据、序号以及 SYN,FIN 等标识设置正确,Sender 主要还需要考虑两个问题。一是接收窗口,Receiver 一次性能够接收的数据量是有限的,会通过 Header 中 Window Size 字段向 Sender 动态反馈这一信息。Sender 应保证发送途中(即已发送且未被确认接收)的数据量小于等于该窗口。
二是数据重传,由于数据包在发送途中可能丢失,Sender 在发送一个包后不能立即将其丢弃,而是放在暂存区中(称为 outstanding data),如果一段时间内没有收到 Receiver 的确认信号(通过 ackno)则需要重传。讲义中规定了将真实 TCP 协议略简化后的一系列规则如下:
- 时间的感知。为了保证程序测试的确定性,Sender 实现中不主动调用任何实际的时间 API,而是被动地通过外部调用其
tick
函数感知时间的经过,该函数的参数为距离上次调用tick
后经过的毫秒数。 - 定时器的概念。需要实现一个 Timer,其可以被启动,经某个设定的时间间隔后触发,也可以手动停止。由于时间的感知是被动的,因此 Timer 只可能在
tick
函数中触发。 - Sender 构造时接受一个初始的 重传超时(retransmission timeout, RTO) 参数,该初始参数固定,同时有一个当前的 RTO 参数。每发送一个数据包,如果 Timer 未启动,启动并使其于当前 RTO 毫秒后触发。当所有暂存区数据都被 ack 时,停止 Timer。
- 当 Timer 在
tick
中触发时,将没有被完全 ack 的最早的(即序列号最小的)暂存数据包重传。同时,如果窗口大小非0,记录连续重传的次数,该信息将供下节实现的 TCPConnection 用于判断是否要结束连接,并使当前 RTO 翻倍(指数退避)。 然后重新根据当前 RTO(可能刚更新过)启动 Timer。 - 当收到对新发送数据的 ack 时,将当前 RTO 重置为初始 RTO,并将连续重传次数归零。如果暂存区非空,启动 Timer。
实现上,主要是 Timer + 三个主要函数。参考网上很多同学的代码 Timer 功能是放在了 Sender 类中实现,我这里还是单独做了一个类如下:
// Helper class to determine whether a given timeout has reached (i.e., expired) since started.
// It won't emit any signal but provide accessor for caller to check its state
// Also the class won't call any system time function but rely on its update() called to
// know time elapsed and whether timeout is reached.
class Timer {
public:
// start the timer with given timeout.
// call start() on a started timer acts like reset() called first
void start(unsigned int timeout);
// update the timer with info of time elapsed since start/last update
void update(unsigned int time_elapsed);
// reset(stop) the timer
void reset();
// check whether the timer is active
bool active() const {
return _active; }
// check whether the timer has timed out (if the timer is inactive, return value is false)
bool expired() const {
return _active && _expired; }
private:
bool _active{
false};
bool _expired{
false};
unsigned int _current_time{
0};
unsigned int _timeout{
0};
};
主要函数之一: void ack_received(const WrappingInt32 ackno, const uint16_t window_size)
。窗口大小用一个类的成员 _window_size
记录更新,该参数在后面的发送函数中用到。ackno 经 unwrap 后与暂存区中的数据包序号一一比较,将完全被确认的数据包移除(注意可以利用暂存区中数据包序号是从小到大有序的性质)。如果能够移除,按规则重置 RTO 和重传计数。如果移除后暂存区为空,将 Timer 停止。由于暂存区可能腾出了空位,最后调用 fill_window
(见下文)尝试发送新数据包。代码如下:
void TCPSender::ack_received(const WrappingInt32 ackno, const uint16_t window_size) {
_window_size = window_size;
// use next seqno as checkpoint
uint64_t ack_seqno = unwrap(ackno, _isn, _next_seqno);
// it's impossible that ackno > _next_seqno, because that byte hasn't been sent yet!
if (ack_seqno > _next_seqno) {
return;
}
// remove completely ack-ed segments from the retransmission buffer
// because the segment in retrans buffer is ordered by seqno,
// it's ok to break once current seg can't be erased (subsequent seg has larger seqno)
for (auto it = _retrans_buf.begin(); it != _retrans_buf.end();) {
if (unwrap((*it).header().seqno, _isn, _next_seqno) + (*it).length_in_sequence_space() <= ack_seqno) {
it = _retrans_buf.erase(it);
_retrans_timeout = _initial_retransmission_timeout;
_timer.start(_retrans_timeout);
_consec_retrans_count = 0;
} else
break;
}
// stop the timer if retransmission buffer is clear
if (_retrans_buf.empty())
_timer.reset();
// refill the window
fill_window();
}
主要函数之二:void tick(const size_t ms_since_last_tick)
。如果 Timer 已启动,则根据参数进行时间的更新,然后检查是否触发。如果触发,则按规则进行重传,增加重传计数,使 RTO 翻倍,并重启 Timer。代码如下:
void TCPSender::tick(const size_t ms_since_last_tick) {
if (_timer.active())
_timer.update(ms_since_last_tick);
if (_timer.expired()) {
_segments_out.emplace(_retrans_buf.front());
if (_window_size > 0) {
_consec_retrans_count++;
_retrans_timeout *= 2;
}
_timer.start(_retrans_timeout);
}
}
主要函数之三:void fill_window()
。即开头一笔带过的 “发送数据、序号以及 SYN,FIN 等标识设置正确的数据包”。概括来说其流程就是:判断窗口是否未被填满 → 如果未被填满,造一个 TCPSegment,具体又分为判断放 SIN 标识,放数据,判断放 FIN 标识三步 → 将 TCPSegment 加入到 _segments_out
和暂存区中,更新序列号和剩余空间大小。循环直至窗口被填满。具体细节见下面的源文件,就不粘贴了。
完整代码链接:
tcp_sender.hh
tcp_sender.cc
通关截图
边栏推荐
- How do I get the last part of a string- How to get the last part of a string?
- 4、 High performance go language release optimization and landing practice youth training camp notes
- 身边35岁程序员如何建立起技术护城河?
- 外包幹了三年,廢了...
- Detailed explanation of uboot image generation process of Hisilicon chip (hi3516dv300)
- Project practice five fitting straight lines to obtain the center line
- BGP experiment (1)
- Wechat applet full stack development practice Chapter 3 Introduction and use of APIs commonly used in wechat applet development -- 3.10 tabbar component (I) how to open and use the default tabbar comp
- Wx is used in wechat applet Showtoast() for interface interaction
- About some details of final, I have something to say - learn about final CSDN creation clock out from the memory model
猜你喜欢
Example of Pushlet using handle of Pushlet
Initial experience of teambiion network disk (Alibaba cloud network disk)
Sqlmap tutorial (IV) practical skills three: bypass the firewall
Detailed explanation of transform origin attribute
抽丝剥茧C语言(高阶)指针的进阶
Stockage et pratique des données en langage C (haut niveau)
URP - shaders and materials - simple lit
計算機服務中缺失MySQL服務
1089: highest order of factorial
Advanced practice of C language (high level) pointer
随机推荐
About some details of final, I have something to say - learn about final CSDN creation clock out from the memory model
【云原生】内存数据库如何发挥内存优势
Solve could not find or load the QT platform plugin "xcb" in "
Leetcode-206. Reverse Linked List
Deep learning Flower Book + machine learning watermelon book electronic version I found
深度学习花书+机器学习西瓜书电子版我找到了
聊聊异步编程的 7 种实现方式
3、 High quality programming and performance tuning practical youth training camp notes
gatk4中的interval是什么??
Flutter riverpod is comprehensively and deeply analyzed. Why is it officially recommended?
Simple example of ros2 planning system plansys2
测试周期被压缩?教你9个方法去应对
The annual salary of general test is 15W, and the annual salary of test and development is 30w+. What is the difference between the two?
IO流 file
抽丝剥茧C语言(高阶)数据的储存+练习
微信小程序中使用wx.showToast()进行界面交互
Detailed explanation of transform origin attribute
JS small exercise
Calculus key and difficult points record part integral + trigonometric function integral
解决could not find or load the Qt platform plugin “xcb“in ““.