CS144 计算机网络 Lab4:TCP Connection

这篇具有很好参考价值的文章主要介绍了CS144 计算机网络 Lab4:TCP Connection。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

经过前面几个实验的铺垫,终于到了将他们组合起来的时候了。Lab4 将实现 TCP Connection 功能,内部含有 TCPReceiverTCPSender,可以与 TCP 连接的另一个端点进行数据交换。

CS144 计算机网络 Lab4:TCP Connection

CS144 计算机网络 Lab4:TCP Connection

实验要求

简单来说,这次实验就是要在 TCPConnection 类中实现下图所示的有限状态机:

CS144 计算机网络 Lab4:TCP Connection

这些状态对应 TCPState 的内部枚举类 State

//! \brief Official state names from the [TCP](\ref rfc::rfc793) specification
enum class State {
    LISTEN = 0,   //!< Listening for a peer to connect
    SYN_RCVD,     //!< Got the peer's SYN
    SYN_SENT,     //!< Sent a SYN to initiate a connection
    ESTABLISHED,  //!< Three-way handshake complete
    CLOSE_WAIT,   //!< Remote side has sent a FIN, connection is half-open
    LAST_ACK,     //!< Local side sent a FIN from CLOSE_WAIT, waiting for ACK
    FIN_WAIT_1,   //!< Sent a FIN to the remote side, not yet ACK'd
    FIN_WAIT_2,   //!< Received an ACK for previously-sent FIN
    CLOSING,      //!< Received a FIN just after we sent one
    TIME_WAIT,    //!< Both sides have sent FIN and ACK'd, waiting for 2 MSL
    CLOSED,       //!< A connection that has terminated normally
    RESET,        //!< A connection that terminated abnormally
};

除了三次握手和四次挥手外,我们还得处理报文段首部 RST 标志被置位的情况,这时候应该将断开连接,并将内部的输入流和输入流标记为 error,此时的 TCPState 应该是 RESET

代码实现

先在类声明里面加上一些成员:

class TCPConnection {
  private:
    TCPConfig _cfg;
    TCPReceiver _receiver{_cfg.recv_capacity};
    TCPSender _sender{_cfg.send_capacity, _cfg.rt_timeout, _cfg.fixed_isn};

    //! outbound queue of segments that the TCPConnection wants sent
    std::queue<TCPSegment> _segments_out{};

    //! Should the TCPConnection stay active (and keep ACKing)
    //! for 10 * _cfg.rt_timeout milliseconds after both streams have ended,
    //! in case the remote TCPConnection doesn't know we've received its whole stream?
    bool _linger_after_streams_finish{true};

    bool _is_active{true};

    size_t _last_segment_time{0};

    /**
     * @brief 发送报文段
     * @param fill_window 是否填满发送窗口
    */
    void send_segments(bool fill_window = false);

    // 发送 RST 报文段
    void send_rst_segment();

    // 中止连接
    void abort();

  public:
    // 省略其余成员
}

接着实现几个最简单的成员函数:

size_t TCPConnection::remaining_outbound_capacity() const { return _sender.stream_in().remaining_capacity(); }

size_t TCPConnection::bytes_in_flight() const { return _sender.bytes_in_flight(); }

size_t TCPConnection::unassembled_bytes() const { return _receiver.unassembled_bytes(); }

size_t TCPConnection::time_since_last_segment_received() const { return _last_segment_time; }

bool TCPConnection::active() const { return _is_active; }

主动连接

客户端可以调用 TCPConnection::connect 函数发送 SYN 报文段请求与服务端建立连接,由于 Lab3 中实现的 TCPSender::fill_window() 函数会根据发送方的状态选择要发送的报文段类型,在还没建立连接的情况下,这里直接调用 fill_window() 就会将一个 SYN 报文段放在队列中,我们只需将其取出放到 TCPConnection_segments_out 队列中即可:

void TCPConnection::connect() {
    // 发送 SYN
    send_segments(true);
}

void TCPConnection::send_segments(bool fill_window) {
    if (fill_window)
        _sender.fill_window();

    auto &segments = _sender.segments_out();


    while (!segments.empty()) {
        auto seg = segments.front();

        // 设置 ACK、确认应答号和接收窗口大小
        if (_receiver.ackno()) {
            seg.header().ackno = _receiver.ackno().value();
            seg.header().win = _receiver.window_size();
            seg.header().ack = true;
        }

        _segments_out.push(seg);
        segments.pop();
    }
}

主动关闭

当上层程序没有更多数据需要发送时,将会调用 TCPConnection::end_input_stream() 结束输入,这时候需要发送 FIN 报文段给服务端,告诉他自己没有更多数据要发送了,但是可以继续接收服务端发来的数据。客户端的状态由 ESTABLISHED 转移到 FIN_WAIT_1,服务端收到 FIN 之后变成 CLOSE_WAIT 状态,并回复 ACK 给客户端,客户端收到之后接着转移到 FIN_WAIT_2 状态。

如果服务端数据传输完成了,会发送 FIN 报文段给客户端,转移到 LAST_ACK 状态,此时客户端会回复最后一个 ACK 给服务端并进入 TIME_WAIT 超时等待状态,如果这个等待时间内没有收到服务端重传的 FIN,就说明 ACK 顺利到达了服务端且服务端已经变成 CLOSED 状态了,此时客户端也能断开连接变成 CLOSED 了。

void TCPConnection::end_input_stream() {
    // 发送 FIN
    _sender.stream_in().end_input();
    send_segments(true);
}

在上述情景中,客户端是主动关闭(Active Close)的一方,服务端是被动关闭(Passive Close)的一方。

CS144 计算机网络 Lab4:TCP Connection

主动重置连接

有两种情况会导致发送 RST 报文段来主动重置连接:

  • TCPSender 超时重传的次数过多时,表明通信链路存在故障;
  • TCPConnect 对象被释放但是 TCP 仍然处于连接状态的时候;

和 Lab3 中类似,TCPConnection 通过外部定期调用 tick() 函数来得知过了多长时间,在 tick() 函数里还得处理超时等待的情况:

//! \param[in] ms_since_last_tick number of milliseconds since the last call to this method
void TCPConnection::tick(const size_t ms_since_last_tick) {
    _sender.tick(ms_since_last_tick);

    // 重传次数太多时需要断开连接
    if (_sender.consecutive_retransmissions() > _cfg.MAX_RETX_ATTEMPTS) {
        return send_rst_segment();
    }

    // 重传数据包
    send_segments();

    _last_segment_time += ms_since_last_tick;

    //  TIME_WAIT 超时等待状态转移到 CLOSED 状态
    if (TCPState::state_summary(_receiver) == TCPReceiverStateSummary::FIN_RECV &&
        TCPState::state_summary(_sender) == TCPSenderStateSummary::FIN_ACKED &&
        _last_segment_time >= 10 * _cfg.rt_timeout) {
        _linger_after_streams_finish = false;
        _is_active = false;
    }
}

TCPConnection::~TCPConnection() {
    try {
        if (active()) {
            cerr << "Warning: Unclean shutdown of TCPConnection\n";

            // Your code here: need to send a RST segment to the peer
            send_rst_segment();
        }
    } catch (const exception &e) {
        std::cerr << "Exception destructing TCP FSM: " << e.what() << std::endl;
    }
}

void TCPConnection::send_rst_segment() {
    abort();
    TCPSegment seg;
    seg.header().rst = true;
    _segments_out.push(seg);
}

void TCPConnection::abort() {
    _is_active = false;
    _sender.stream_in().set_error();
    _receiver.stream_out().set_error();
}

接收报文段

外部通过 TCPConnection::segment_received() 将接收到的报文段传给它,在这个函数内部,需要将确认应答号和接收窗口大小告诉 TCPSender,好让他接着填满发送窗口。接着还需要把报文段传给 TCPReceiver 来重组数据,并更新确认应答号和自己的接收窗口大小。然后 TCPSender 需要根据收到的包类型进行状态转移,并决定发送含有有效数据的报文段还是空 ACK 给对方。

为什么即使没有新的数据要发送也要回复一个空 ACK 呢?因为如果不这么做,对方会以为刚刚发的包丢掉了而一直重传。

void TCPConnection::segment_received(const TCPSegment &seg) {
    if (!active())
        return;

    _last_segment_time = 0;

    // 是否需要发送空包回复 ACK,比如没有数据的时候收到 SYN/ACK 也要回一个 ACK
    bool need_empty_ack = seg.length_in_sequence_space();

    auto &header = seg.header();

    // 处理 RST 标志位
    if (header.rst)
        return abort();

    // 将包交给发送者
    if (header.ack) {
        need_empty_ack |= !_sender.ack_received(header.ackno, header.win);

        // 队列中已经有数据报文段了就不需要专门的空包回复 ACK
        if (!_sender.segments_out().empty())
            need_empty_ack = false;
    }

    // 将包交给接受者
    need_empty_ack |= !_receiver.segment_received(seg);

    // 被动连接
    if (TCPState::state_summary(_receiver) == TCPReceiverStateSummary::SYN_RECV &&
        TCPState::state_summary(_sender) == TCPSenderStateSummary::CLOSED)
        return connect();

    // 被动关闭
    if (TCPState::state_summary(_receiver) == TCPReceiverStateSummary::FIN_RECV &&
        TCPState::state_summary(_sender) == TCPSenderStateSummary::SYN_ACKED)
        _linger_after_streams_finish = false;

    // LAST_ACK 状态转移到 CLOSED
    if (TCPState::state_summary(_receiver) == TCPReceiverStateSummary::FIN_RECV &&
        TCPState::state_summary(_sender) == TCPSenderStateSummary::FIN_ACKED && !_linger_after_streams_finish) {
        _is_active = false;
        return;
    }

    if (need_empty_ack && TCPState::state_summary(_receiver) != TCPReceiverStateSummary::LISTEN)
        _sender.send_empty_segment();

    // 发送其余报文段
    send_segments();
}

测试

在终端中输入 make check_lab4 就能运行所有测试用例,测试结果如下:

CS144 计算机网络 Lab4:TCP Connection

发现有几个 txrx.sh 的测试用例失败了,但是单独运行这些测试用例却又可以通过,就很奇怪:

CS144 计算机网络 Lab4:TCP Connection

接着测试一下吞吐量(请确保构建类型是 Release 而不是 Debug),感觉还行, 0.71Gbit/s,超过了实验指导书要求的 0.1Gbit/s。但是实际上还可以优化一下 ByteStream 类,将内部数据类型换成 BufferList,这样在写入数据的时候就不用一个字符一个字符插入队列了,可以大大提高效率。

CS144 计算机网络 Lab4:TCP Connection

最后将 Lab0 中 webget 使用的 TCPSocket 换成 CS144TCPSocket,重新编译并运行 webegt,发现能够正确得到响应结果,说明我们实现的这个 CS144TCPSocket 已经能和别的操作系统实现的 Socket 进行交流了:

CS144 计算机网络 Lab4:TCP Connection

后记

至此,CS144 的 TCP 实验部分已全部完成,可以说是比较有挑战性的一次实验了,尤其是 Lab4 部分,各种奇奇怪怪的 bug,编码一晚上,调试时长两天半(约等于一坤天),调试的时候断点还总是失效,最后发现是优化搞的鬼,需要将 etc/cflags.cmake 第 18 行改为 set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -ggdb3 -O0") 才行。以上~~文章来源地址https://www.toymoban.com/news/detail-435157.html

到了这里,关于CS144 计算机网络 Lab4:TCP Connection的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 《计算机网络自顶向下》Wireshark实验 Lab4 TCP

    《计算机网络自顶向下》Wireshark Lab + 套接字编程作业 + 杂项实验室编程作业 全实验博客链接 各位好 啊 学计算机想要学好真的还是挺难的 说实话 科班学计算机如果想要花大量的时间去学 只能把平时的课程大部分时间不去上 如果班级管理严格或者说 各种因素让你不得不去上

    2023年04月09日
    浏览(76)
  • Wireshark TCP实验—Wireshark Lab: TCP v7.0(计算机网络自顶向下第七版)

    What is the IP address and TCP port number used by the client computer (source) that is transferring the file to gaia.cs.umass.edu? 根据数据包中的 tcp-ethereal-trace-1 ,其源 IP 地址为 192.168.1.102 192.168.1.102 192.168.1.102 ,端口号为 1162 1162 1162 。 What is the IP address of gaia.cs.umass.edu? On what port number is it sending and re

    2023年04月09日
    浏览(45)
  • CS 144 Lab Four -- the TCP connection

    对应课程视频: 【计算机网络】 斯坦福大学CS144课程 Lab Four 对应的PDF: Lab Checkpoint 4: down the stack (the network interface) TCPConnection 需要将 TCPSender 和 TCPReceiver 结合,实现成一个 TCP 终端,同时收发数据。 TCPConnection 有几个规则需要遵守: 对于 接收数据段 而言: 如果接收到的数据包

    2024年02月13日
    浏览(33)
  • CS 144 Lab Four 收尾 -- 网络交互全流程解析

    对应课程视频: 【计算机网络】 斯坦福大学CS144课程 本节作为Lab Four的收尾,主要带领各位来看看网络交互的整体流程是怎样的。 这里以tcp_ipv4.cc文件为起点,来探究一下cs144是如何实现整个协议栈的。 首先,项目根路径中的 tun.sh 会使用 ip tuntap 技术创建虚拟 Tun/Tap 网络设备

    2024年02月04日
    浏览(42)
  • 北京大学计算机网络lab1——MyFTP

    目录 Lab目标 一、知识补充 二、具体实现 1.数据报文格式和字符串处理 2.open函数 3.auth 4.ls 5.get和put 三、总结 ps:本人靠着计网lab几乎就足够在就业行情并不好的23年找到自己满意的工作了,计网lab的教程也非常给力,对我这种恐惧写lab的菜狗都非常友好(本人写lab3确实比较

    2024年02月07日
    浏览(54)
  • Wireshark HTTP实验—Wireshark Lab: HTTP v7.0(计算机网络自顶向下第七版)

    Is your browser running HTTP version 1.0 or 1.1? What version of HTTP is the server running? 浏览器与服务器的版本均为 H T T P / 1.1 HTTP/1.1 H TTP /1.1 。 What languages (if any) does your browser indicate that it can accept to the server? 能接受简体中文以及英文。 What is the IP address of your computer? Of the gaia.cs.umass.edu serv

    2024年02月08日
    浏览(40)
  • Wireshark IP实验—Wireshark Lab: IP v7.0(计算机网络自顶向下第七版)

    修改发送数据包的大小 跟踪的地址为 www.ustc.edu.cn text{www.ustc.edu.cn} www.ustc.edu.cn 由于自己抓的包比较凌乱,分析起来比较复杂,所以使用作者的数据包进行分析 Select the first ICMP Echo Request message sent by your computer, and expand the Internet Protocol part of the packet in the packet details window.Wh

    2024年02月04日
    浏览(38)
  • 计算机网络—TCP

    源端口号和目标端口号:16位字段,用于标识TCP连接的源和目标端口号。 序列号(Sequence Number):32位字段,用于标识发送的数据字节流中的第一个字节的序号。 确认号(Acknowledgment Number):32位字段,确认收到的字节序号,即期望接收的下一个字节的序号。 数据偏移:4位字

    2024年02月13日
    浏览(64)
  • 计算机网络-TCP协议

    TCP被称为面向连接的,因为在应用程序开始互传数据之前,TCP会先建立一个连接,该连接的建立涉及到 三次“握手 ”。 TCP的连接不是一条真实存在的电路,而是一条逻辑链接 ,其共同状态仅保留在两个通信端系统的TCP程序中。 TCP连接也是点对点的,即TCP连接只能存在于一

    2024年02月08日
    浏览(56)
  • 【计算机网络】TCP协议

    实验目的 应用所学知识: 1. 熟悉 TCP 的协议格式。 2. 理解 TCP 对序列号和确认号的使用。 3. 理解 TCP 的流量控制算法和拥塞控制算法。 实验步骤与结果 1.任务一: 将Alice.txt上传到服务器: 使用wireshark捕获数据包,看到计算机和gaia.cs.umass.edu之间的一系列 TCP 和 HTTP 通信,包

    2023年04月20日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包