lab_4_144

server/2024/11/20 12:21:09/

lab4

建立起基本的连接、数据传输和连接终止的过程

  1. 建立连接过程: 实现 TCP 握手协议的逻辑,包括客户端和服务器端的连接建立过程。
  2. 数据传输: 涉及如何传输数据段,并处理数据包的丢失或损坏等情况。
  3. 连接的终止: 实现 TCP 连接的正常终止过程,确保数据传输的完整性和可靠性

创建名为TCPConnection的总体模块,该模块将TCPSender和TCPReceiver结合起来,并处理连接的全局管理。连接的TCP段可以让我们的代码与Internet上使用相同TCP/IP语言的数十亿其他计算机通信
在这里插入图片描述

图解:

1、实现表示客户端,虚线表示服务器server

2、客户端(client)和服务器端(server)在连接的初始状态下都处于CLOSED状态

3、客户端和服务器的角色分别是SenderReceiver,它们之间通过TCPConnection进行交互

4、TCP连接的状态:

​ 服务器端在开始监听连接时,处于LISTEN状态。客户端则处于CLOSED状态

5、服务器端的状态:

​ 服务器端的Receiver会在调用listen()时进入LISTEN状态。此时,Sender(客户端)依然处于CLOSED状态,表示尚未建立连接

​ 服务器端处于LISTEN状态,准备接收来自客户端的连接请求

三次握手:

1、客户端会发送SYN包请求建立连接(第一次握手),并进入SYN_SENT状态。而服务器端在这时处于LISTEN状态,准备接受连接

2、当服务器接收到客户端的SYN包时,客户端仍处于SYN_SENT状态,等待服务器的确认,服务器收到后回复SYN-ACK包(第二次握手),服务器会将自己的接收端状态设置为SYN_RCVD,表示它已经收到连接请求,

3、客户端接收到服务器的SYN-ACK包后,向服务器发送一个ACK包,确认服务器的响应(第三次握手);客户端进入ESTABLISHED状态,服务器接收到ACK包后,也进入ESTABLISHED状态,TCP连接建立成功

两次握手无法有效防止网络中滞留的旧请求导致的误连接
假设客户端发送了一个连接请求 SYN 包(第一次握手),但由于网络延迟,该请求被滞留在网络中。
客户端认为连接失败后,会重新发送新的连接请求,并与服务端建立连接并正常通信。
滞留的旧 SYN 包到达服务端时,服务端会误认为这是一个新的连接请求,返回 SYN-ACK,建立一个无效连接三次握手已经能确认双方的通信能力和连接状态,增加额外步骤(四次握手)没有实际意义,浪费通信资源

四次挥手:

1、TCP连接的断开过程是由四个报文(Packet)组成的,通常被称为“四次挥手”

2、第一次挥手:

1. 由主动关闭连接的一方(端A)发送一个`FIN`包(即“结束”标志)给另一方(端B)
1. 发送FIN包的目的是通知对方,端A已经没有数据要发送了,但它仍然可以接收来自端B的数据
1. 端A发送FIN包后,自己进入FIN_WAIT_1状态

3、端B收到FIN包并回复ACK包

1. 端B收到端A的FIN包后,发送一个ACK包确认收到了FIN
1. 端B进入`CLOSE_WAIT`状态,表示它已准备关闭连接,但仍然等待数据的发送

4、端B发送FIN包给端A

1. 在端B准备好关闭连接后,它发送一个`FIN`包给端A,表示端B也没有数据要发送了,准备关闭连接
1. 端B进入LAST_ACK状态,等待端A的最终确认

5、端A收到FIN包并回复ACK包

1. 端A收到端B的FIN包后,发送一个ACK包确认端B的FIN包
1. 端A进入TIME_WAIT状态,等待足够的时间以确保端B收到确认
1. 最终,端A进入CLOSED状态,连接完全关闭
第一次挥手(主动关闭方 → 被动关闭方):
主动关闭方发送 FIN,表示“我已经没有数据要发送了,但还能接收你的数据”。第二次挥手(被动关闭方 → 主动关闭方):
被动关闭方收到 FIN 后,回复 ACK,表示“我知道你要关闭发送方向了”。第三次挥手(被动关闭方 → 主动关闭方):
被动关闭方发送 FIN,表示“我也没有数据要发送了”。第四次挥手(主动关闭方 → 被动关闭方):
主动关闭方收到 FIN 后,回复 ACK,表示“我知道你要关闭发送方向了”,连接完全关闭三次挥手:如果被动关闭方在收到主动方的 FIN 后直接发送 FIN 而不先确认,会使得主动方无法确认自己方向的数据是否已完全被对方接收

tcp_connection.hh:

class TCPConnection {private:TCPConfig _cfg;  // 保存TCP连接的配置信息TCPReceiver _receiver{_cfg.recv_capacity};  // 用于接收数据的接收方,传入接收缓冲区大小TCPSender _sender{_cfg.send_capacity, _cfg.rt_timeout, _cfg.fixed_isn};  // 用于发送数据的发送方,传入发送缓冲区大小、重传超时和初始序列号std::queue<TCPSegment> _segments_out{};  // 存储待发送的TCP段bool _linger_after_streams_finish{true};  // 指示是否在数据流结束后继续保持连接活跃,用于避免对方不知道我们已接收到整个流public:void connect();  // 发起连接,发送SYN报文size_t write(const std::string &data);  // 向TCP连接写入数据,返回实际写入的字节数size_t remaining_outbound_capacity() const;  // 返回发送缓冲区中剩余的容量void end_input_stream();  // 关闭输出流,但仍然允许读取输入数据流ByteStream &inbound_stream() { return _receiver.stream_out(); }  // 获取接收到的字节流size_t bytes_in_flight() const;  // 返回已发送但未被确认的字节数,包括SYN/FIN每个视为一个字节size_t unassembled_bytes() const;  // 返回接收到的但尚未重新组装的字节数size_t time_since_last_segment_received() const;  // 返回自上次接收到TCP段以来的时间(毫秒)TCPState state() const { return {_sender, _receiver, active(), _linger_after_streams_finish}; };  // 返回当前连接的状态void segment_received(const TCPSegment &seg);  // 处理接收到的TCP段void tick(const size_t ms_since_last_tick);  // 定时调用,处理超时和其他定时相关的任务std::queue<TCPSegment> &segments_out() { return _segments_out; }  // 返回待发送的TCP段队列bool active() const;  // 判断连接是否仍然活跃explicit TCPConnection(const TCPConfig &cfg) : _cfg{cfg} {}  // 使用配置创建TCP连接~TCPConnection();  // 析构函数,如果连接仍然活跃,发送RST报文TCPConnection() = delete;  // 禁止默认构造函数TCPConnection(TCPConnection &&other) = default;  // 允许移动构造TCPConnection &operator=(TCPConnection &&other) = default;  // 允许移动赋值TCPConnection(const TCPConnection &other) = delete;  // 禁止复制构造函数TCPConnection &operator=(const TCPConnection &other) = delete;  // 禁止复制赋值运算符
};

TCPConnection 类封装了TCP连接的管理,包括连接建立、数据发送、接收处理、状态跟踪等功能。通过包含 TCPSender、TCPReceiver 和一些辅助方法,类实现了TCP协议的基本功能,如数据流的传输和连接的关闭。类中的多个方法和成员变量帮助跟踪连接状态、管理数据流、处理TCP段以及确保连接的正确关闭

class TCPConfig {public:static constexpr size_t DEFAULT_CAPACITY = 64000;  //!< Default capacitystatic constexpr size_t MAX_PAYLOAD_SIZE = 1000;   //!< Conservative max payload size for real Internetstatic constexpr uint16_t TIMEOUT_DFLT = 1000;     //!< Default re-transmit timeout is 1 secondstatic constexpr unsigned MAX_RETX_ATTEMPTS = 8;   //!< Maximum re-transmit attempts before giving upuint16_t rt_timeout = TIMEOUT_DFLT;       //!< Initial value of the retransmission timeout, in millisecondssize_t recv_capacity = DEFAULT_CAPACITY;  //!< Receive capacity, in bytessize_t send_capacity = DEFAULT_CAPACITY;  //!< Sender capacity, in bytesstd::optional<WrappingInt32> fixed_isn{};
};

TCPConfig是TCP连接所需的基本参数

  1. DEFAULT_CAPACITY表示TCP连接的默认缓冲区容量,这里TCP连接的发送和接收缓冲区默认可以存储最多64000字节的数据
  2. MAX_PAYLOAD_SIZE表示单个TCP段(segment)的最大有效载荷(payload)大小
  3. MAX_RETX_ATTEMPTS这个值表示在放弃之前,最大重传尝试次数
  4. DEFAULT_CAPACITY这个成员表示接收缓冲区的容量(字节)
  5. fixed_isn表示一个32位的循环整数。这个成员可能用于配置一个固定的初始序列号(ISN)
  6. std::optional 是一个容器,可以包含一个值或者不包含任何值(即为空)

TCPState封装了上图的TCP三次握手和四次挥手的各种状态

TCPState state() const { return {_sender, _receiver, active(), _linger_after_streams_finish}; };

class TCPState {private:std::string _sender{};std::string _receiver{};bool _active{true};bool _linger_after_streams_finish{true};public:bool operator==(const TCPState &other) const;bool operator!=(const TCPState &other) const;//! \brief Official state names from the [TCP](\ref rfc::rfc793) specificationenum class State {LISTEN = 0,   //!< Listening for a peer to connectSYN_RCVD,     //!< Got the peer's SYNSYN_SENT,     //!< Sent a SYN to initiate a connectionESTABLISHED,  //!< Three-way handshake completeCLOSE_WAIT,   //!< Remote side has sent a FIN, connection is half-openLAST_ACK,     //!< Local side sent a FIN from CLOSE_WAIT, waiting for ACKFIN_WAIT_1,   //!< Sent a FIN to the remote side, not yet ACK'dFIN_WAIT_2,   //!< Received an ACK for previously-sent FINCLOSING,      //!< Received a FIN just after we sent oneTIME_WAIT,    //!< Both sides have sent FIN and ACK'd, waiting for 2 MSLCLOSED,       //!< A connection that has terminated normallyRESET,        //!< A connection that terminated abnormally};//! \brief Summarize the TCPState in a stringstd::string name() const;//! \brief Construct a TCPState given a sender, a receiver, and the TCPConnection's active and linger bitsTCPState(const TCPSender &sender, const TCPReceiver &receiver, const bool active, const bool linger);//! \brief Construct a TCPState that corresponds to one of the "official" TCP state namesTCPState(const TCPState::State state);//! \brief Summarize the state of a TCPReceiver in a stringstatic std::string state_summary(const TCPReceiver &receiver);//! \brief Summarize the state of a TCPSender in a stringstatic std::string state_summary(const TCPSender &receiver);
};TCPState state() const { return {_sender, _receiver, active(), _linger_after_streams_finish}; };// return {_sender, _receiver, active(), _linger_after_streams_finish}; 这里的 _sender、_receiver、active() 和 _linger_after_streams_finish 都是假设的成员变量或函数

TCP协议的标准状态

LISTEN:正在监听新的连接请求(如服务器端处于被动打开)。
SYN_RCVD:收到对端发送的 SYN 数据段。
SYN_SENT:发送了一个 SYN 数据段以主动建立连接。
ESTABLISHED:三次握手完成,连接建立。CLOSE_WAIT:收到对端发送的 FIN 数据段,等待关闭连接。
LAST_ACK:本地发送了 FIN,等待对端的确认。
FIN_WAIT_1:本地发送了 FIN,尚未收到对端的确认。
FIN_WAIT_2:收到对端对 FIN 的确认,等待对端的 FIN。
CLOSING:本地发送 FIN 后,立刻收到对端的 FIN。
TIME_WAIT:双方完成了 FIN 和 ACK 的交换,等待两倍最大段寿命(2 MSL)以确保对端收到 ACK。
CLOSED:连接已正常关闭。RESET:连接因异常原因被中断
TCPConnection实现

1、remaining_outbound_capacity()

size_t TCPConnection::remaining_outbound_capacity() const {return _sender.stream_in().remaining_capacity();
}
// 返回当前 TCP 连接的剩余可写容量,即还可以向发送缓冲区写入的字节数
// 就是发送者 TCPSender 的发送缓冲区的剩余可用字节数。这个值表明还能写入多少数据到发送者的缓存中

2、发送者的其他参数

size_t TCPConnection::bytes_in_flight() const {return _sender.bytes_in_flight();
}
// 返回发送者的飞行中的字节(in-flight bytes);返回当前已经发送但尚未被对方确认(ACK)的字节数size_t TCPConnection::unassembled_bytes() const { return _receiver.unassembled_bytes(); }size_t TCPConnection::time_since_last_segment_received() const { return _last_recv_et;}

3、segment_received

void TCPConnection::segment_received(const TCPSegment &seg) {_last_recv_et = 0; // 重置时间计数器(表示刚收到一个段)const TCPHeader &header = seg.header(); // 获取 TCP 段的头部信息if (header.rst) { // 如果收到 RST 段_shutdown(false); // 异常关闭连接return;}_receiver.segment_received(seg); // 处理接收到的 TCP 段(更新接收器状态)if (header.ack) { // 如果收到 ACK 段_sender.ack_received(header.ackno, header.win); // 更新发送器状态}// 如果收到的数据段有数据,且发送器没有挂起的段需要发送if (seg.length_in_sequence_space() > 0 && _sender.segments_out().size() == 0) {_sender.fill_window(); // 尝试填满发送窗口if (_sender.segments_out().size() == 0) {_sender.send_empty_segment(); // 如果窗口仍为空,发送一个空段用于保持连接}}// 如果接收器的 ACK 有效,并且收到的段是窗口边界的重传段(窗口左边界 - 1)if (_receiver.ackno().has_value() && (seg.length_in_sequence_space() == 0) &&(header.seqno == _receiver.ackno().value() - 1)) {_sender.send_empty_segment(); // 发送一个空段以确认接收}_clear_sendbuf(); // 清空发送队列(将数据段从发送器移到连接的 `_segments_out` 队列)// 如果接收器的输入流结束,而发送器的输出流尚未结束if (_receiver.stream_out().input_ended() && !_sender.stream_in().input_ended()) {_linger_after_streams_finish = false; // 关闭 LINGER 模式(立即释放资源)}
}

4、_clear_sendbuf

将发送器(TCPSender)生成的待发送 TCP 段,从发送器的队列转移到 TCP 连接的输出队列(_segments_out)。在转移过程中,它还会为每个段设置必要的 TCP 头部字段(如窗口大小、ACK 标志和序号)

void TCPConnection::_clear_sendbuf() {auto &sender_queue = _sender.segments_out(); // 获取发送器的待发送队列while (!sender_queue.empty()) { // 循环处理发送器中的每个段TCPSegment &seg = sender_queue.front(); // 获取队列中的第一个 TCP 段TCPHeader &header = seg.header(); // 获取 TCP 段的头部信息// 确定接收窗口的大小,限制最大值为 16 位无符号整数的最大值uint16_t max_winsize = std::numeric_limits<uint16_t>::max();if (_receiver.window_size() > max_winsize) {header.win = max_winsize; // 如果接收窗口大于最大值,则将窗口大小设为最大值} else {header.win = _receiver.window_size(); // 否则设置为实际接收窗口大小}// 如果接收器有有效的 ACKif (_receiver.ackno().has_value()) {header.ack = true; // 设置 ACK 标志header.ackno = _receiver.ackno().value(); // 设置 ACK 序号}_segments_out.push(seg); // 将 TCP 段推入连接的输出队列sender_queue.pop(); // 从发送器队列中移除该段}
}

5、writetick 方法

size_t TCPConnection::write(const string &data) {size_t data_size = _sender.stream_in().write(data); // 写入数据到发送器流_sender.fill_window(); // 尝试填充发送窗口_clear_sendbuf(); // 清理并转移发送器的待发送段return data_size; // 返回实际写入的字节数
}
// 上层应用需要通过 TCP 连接发送数据时调用此方法void TCPConnection::tick(const size_t ms_since_last_tick) {_last_recv_et += ms_since_last_tick; // 累积未接收数据段的时间// 超过最大重传次数,发送 RST 并关闭连接if (_sender.consecutive_retransmissions() >= _cfg.MAX_RETX_ATTEMPTS) {_send_rst_segment();_shutdown(false); // 非正常关闭连接return;}// 通知发送器时间流逝_sender.tick(ms_since_last_tick);// 检查是否应该关闭连接if (_should_shutdown()) {if (_linger_after_streams_finish) { // 是否在关闭前等待对端确认if (_last_recv_et >= 10 * _cfg.rt_timeout) { // 等待超时,关闭连接_shutdown(true);}} else {_shutdown(true);}}// 清理并转移发送段_clear_sendbuf();
}

_sender.tick(ms_since_last_tick);

// 检查是否应该关闭连接
if (_should_shutdown()) {if (_linger_after_streams_finish) { // 是否在关闭前等待对端确认if (_last_recv_et >= 10 * _cfg.rt_timeout) { // 等待超时,关闭连接_shutdown(true);}} else {_shutdown(true);}
}// 清理并转移发送段
_clear_sendbuf();

}



http://www.ppmy.cn/server/143470.html

相关文章

【贪心算法】贪心算法三

贪心算法三 1.买卖股票的最佳时机2.买卖股票的最佳时机 II3.K 次取反后最大化的数组和4.按身高排序5.优势洗牌&#xff08;田忌赛马&#xff09; 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#…

vue2 cafe-ofd实现ofd格式文件预览

cafe-ofd实现ofd格式文件预览 下载依赖 npm install cafe-ofd --save在main.js引用 import cafeOfd from cafe-ofd import cafe-ofd/package/index.css Vue.use(cafeOfd)使用 <template><div class"dashboard-container"><el-button type"te…

uniapp 微信小程序地图标记点、聚合点/根据缩放重合点,根据缩放登记显示气泡marik标点

如图&#xff0c;如果要实现上方的效果&#xff1a; 上方两个效果根据经纬度标记点缩放后有重复点会添加数量 用到的文档地址https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.addMarkers.htmlMapContext.addMarkers(Object object) 添加标记点Ma…

ERROR TypeError: AutoImport is not a function

TypeError: AutoImport is not a function 原因&#xff1a;unplugin-auto-import 插件版本问题 Vue3基于Webpack&#xff0c;在vue.config.js中配置 当unplugin-vue-components版本小于0.26.0时&#xff0c;使用以下写法 const { defineConfig } require("vue/cli-se…

人工智能之机器学习概念3【培训机构学习笔记】

定义及作用&#xff1a; 无监督学习是通过试图学习或提取数据背后的数据特征&#xff0c;或者从数据中抽取出重要的特征信息&#xff0c;常见的算法有类聚、降维、文本处理&#xff08;特征抽取&#xff09;等。无监督学习一般是作为有监督学习的前期数据处理&#xff0c;功能…

24.UE5枚举,怪物分类,龙卷风技能

2-26 枚举、怪物分类、龙旋风技能、掉落概率_哔哩哔哩_bilibili 目录 1.枚举 1.1枚举类型的创建 1.2 将枚举类型绑定到怪物蓝图上 1.3枚举类型的使用 1.3.1创建新的掉落物 1.3.2更改怪物掉落逻辑 2.龙卷风技能 2.1输入映射 2.2龙卷风发射物的创建 2.3龙卷风伤害逻辑…

Spring Boot汽车资讯:科技与汽车的对话

5系统详细实现 5.1 管理员模块的实现 5.1.1 用户信息管理 汽车资讯网站的系统管理员可以管理用户&#xff0c;可以对用户信息修改删除审核以及查询操作。具体界面的展示如图5.1所示。 图5.1 用户信息管理界面 5.1.2 汽车品牌管理 系统管理员可以汽车品牌信息进行添加&#xf…

泷羽sec-安全见闻(9)

安全见闻&#xff08;9&#xff09; 声明&#xff01; 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其…