CS144 lab3 tcp_sender

发布于:2025-08-30 ⋅ 阅读:(20) ⋅ 点赞:(0)

0. 前言

这个实验做了挺久的,刚开始做的时候官方的代码库还是开着的。

调着调着代码官方把仓库给删掉了,又去找别人的代码仓库调发现不

对都打算放弃了,过了几天发现了一个start-code的库

再合进去简直完美。这个实验花的时间应该是前四个里面花的时间

最久的了,写完这个实验后准备重新去写2021 sponge版本的项目了。

到目前为止的实验版本都是2025winter-minnow版本。

1. 实验构建

一开始用的别人写好的仓库sevetis/minnow,先是make_parrallel.sh的权限问题。

给了权限后,又出现找不到Makefile

后面找到一个starter-code的库,HT4w5/minnow-winter-2025

用这个库的代码合进去解决的问题。

2. TCPSender需求

TCPSender需要负责做的事:

  • 维护接收端的窗口(TCPReceiverMessage , ackno,window size
  • 尽量的填充发送窗口,从Bytestream里面读取,创建TCPSegment(可能 包含SYN,FIN);除非窗口满了或者bytestream中没有内容了。
  • 跟踪那些发送了但还没有确认的报文,这些报文叫Outstanding Segments
  • 重传那些Outstanding Segment,如果超时了
2.1 TCPSender怎么知道报文丢失?

TCPSender会发送一串TCPSenderMessage

每个都是从Bytestream里面读取的子串(可能为空),用一个

序列号来标识在流中的位置。在流开头时会加上SYN,而在流

的结局会加上FIN

除了发送为些报文之外,TCPSender还会跟踪这些发送出去的报文所

占据的序列号有没有被完全确认。TCPSender的调用者会周期性地

调用TCPSendertick方法,看过了多少时间了。

TCPSender负责检查所有发出的TCPSenderMessages,

来决定最早发送的报文是不是太久没有确认了。

(这个报文段占据的所有序列号是否都得到了确认)。

如果不是,就需要重传这个报文了。

  • 每过若干msTCPSendertick函数就会被调用,它用来告诉你,自上一次调用到现在过了多久。使用这个方法需要维护一个标识,表示TCPSender累计过了多久。记住不要调用任何时间相关的函数,比如clock time等。
  • TCPSender创建时有一个初始的RTO重传时间,RTO会随着时间进行而改变,但初始值不变为initial_RTO_ms
  • 你需要实现一个重传定时器:在到达RTO时,定时器触发;这里的时间是调用TCPSendertick方法里的时间,而不是一天中的具体时间。
  • 每次包含数据(序列空间中长度非0)的报文段发送时,如果定时器没有运行,那就启动它,让它在RTO时间到达时触发。
  • 所有发出的数据被确认后,关闭重传定时器
  • 如果tick调用且重传定时器超时
    • 重传最早未完全确认的报文。为了能重传报文你需要选择数据结构把报文给存下来。
    • 如果窗口大小非0
      • 跟踪连续重传次数,每次连续重传增加它。TCPConnection会根据这个信息判断是不是该主动关闭连接了。
      • 加倍RTO重传时间。减少重传包可能对网络造成的影响。
      • 重置重传定时器,启动它。注意更新RTO时间。
  • 当接收者通过ackno告诉我们,它们收到了新数据(新ackno大于之前所有的ackno
    • RTO时间置为初始值initial_RTO
    • 如果发送者还有未确认的报文段,重启重传定时器
    • 将连续重传数设为0

如果你想把重传定时器用一个单独的类来写,那么请加到已有的文件中(tcp_sender.hh,tcp_sender.cc)。

2.2 实现TCPSender

我们已经基本知道TCPSender要做什么了。

Bytestream里读取子串,将它们分成报文段发送给接收者。

如果在一段时间后还没有收到确认消息,就重传。

而且我们也已经讨论了什么时候一个发送了的报文段需要重传。

下面我们就看具体的接口类了。

void push(const TransmitFunction & transmit);

TCPSenderBytestream里面获取子串来填充窗口;

从流中读取子串并尽可能多地发送TCPSenderMessage

只要Bytestream中有字节读且接收方窗口还有空间。

它们通过调用transmit函数来发送。

你需要保证每个发送的TCPSenderMessage全部都在接收方的窗口中。

要让每个独立的报文尽可能的大,但不要超过TCPConfig::MAX_PAYLOAD_SIZE

你可以使用TCPSenderMessage::sequence_length()来计算一个报文段

中占据了多少个序列号。

注意SYNFIN也会占据窗口中的一个序列号。

特别处理:

应该如何处理接收窗口为0的情况呢?

如果接收方声称它的窗口为0push函数中应该假装它的

窗口大小为1一样。发送方可能以发送单个字节被接收方拒绝

而结束,也可能 接收方发送新的确认表示它的窗口有更多的空间了。

如果不这样做的话,发送方永远不知道什么时候允许开始发送了。

但要注意的是这只是你处理0大小窗口的特殊情况。

TCPSender实际上不需要记住错误的大小窗口为1,这只是针对push

函数的特殊处理。同样需要注意,窗口大小可能 为1 20 200

但窗口满了。一个满窗口和一个零窗口是两个不同的概念。

void receive(const TCPReceiverMessage &msg);

msg是从接收方接收的,

传来了新的左端ackno和右端ackno +window_size

TCPSender需要检查所有的outstanding segments

移除那些已经收到的。也就是小于ackno的。

void tick(uint64_t ms_since_last_tick, 
	const TransmitFunction & transmit);

一个计时函数,自上次发送过了多久。

发送方可能重传outstanding segments

它们可以通过transmit来发送。

(请不要使用clock gettimeofday这些函数,

唯一的对时间的引用就是ms_since_last_tick)。

TCPSenderMessage make_empty_message() const;

TCPSende应该可以正确的产生和发送一个序列号长度为0的报文。

这对对端想要发送TCPReceiverMessage

并且需要产生TCPSenderMessage是有用的。

2.3 FAQs和特殊情况
  • Q1: 在接收到对端通知之前,发送端默认的接收端窗口大小是多少?
    A1: 1

  • Q2: 如果一个段的报文只有部分确认了,我需要剪切掉已经确认的部分吗?
    A2: 真正的TCP可以那样做。但对于我们的学习来说没必要,我们以一个完全的段确认来处理。

  • Q3: 如果我发送了三个独立的段"a" "b" "c",但都没有收到确认,我可不可以在重传的时候合成一个大的段"abc"? 或者是独立的重传?
    A3: 和上一个问题类似,真正的TCP可以那样做。但对我们的实验来说没必要,独立跟踪每个段,超时了就重传最早的段。

  • Q4: 我需要存储空的段吗?并在必要时重传。
    A4: 没有必要,只有包含数据的才需要,SYN、FIN、Payload,除此之外无需重传。

3. 实现思路及bug之路

这个实验我感觉自己完全是在面向test-case编程。

一开始我都不知道怎么发包!

后面去读了TransmitFunction的函数声明才知道怎么发,原来

它带的参数就是你构建发的包。。。

using TransmitFunction = 
std::function<void( const TCPSenderMessage& )>;

一开始真的一点思路都没有。

我是一步步摸索出来的。。。,先把重传的部分实现完全放在一边的。

先从序列号入手的,同样需要一个流的标识stream_idx_

甚至忘记了绝对序列号和相对序列号转化的逻辑,

又跑回去看tcp_receiver的实现。

由于可能需要重传发出去的包,因此选择用queue<TCPSenderMessage>

去维护这一信息。

当然我们需要知道有没有连接同步,用一个is_SYN_维护这一信息。

刚开始写的时候就忘记把syn的包给推进队列,

然后transmit(q.front())就报错

runtime_erro: load of value 190,which is not a valid value of type "bool"

在那里排查半天。。。

同样不知道SYN FIN要不要算在窗口大小里面,后面看函数发现是要算进去的。

还需要维护的是发送方自己的一个窗口,有哪些序列号是发送出去了的。

在写代码时,忘记更新这个窗口也错了几次。

make_empty_massage()感觉完全是test-driven写出来的了。

再有一些bug就是一些corner case了,比如0窗口的情况。

发的第一个包就是SYN+FIN的情况,第一个包带payload的情况。

FIN最后一个单独发的情况。

还有处理零窗口的定时器的情况。

max_recv_这个变量是根据接收方发来的信息而确定的接收方窗口的一

个最大大小。

还有RST相关的代码也感觉是test-driven出来的。。。

代码虽然是屎山,但它还是跑起来了。下面列下代码吧。

还有个小插曲是,代码case全跑过后,自己手贱把is_FIN初始化成

true,在那里找了半天的错误。

  • tcp_sender.hh
#pragma once

#include "byte_stream.hh"
#include "tcp_receiver_message.hh"
#include "tcp_sender_message.hh"

#include <functional>
#include <queue>

class TCPSender
{
public:
  /* Construct TCP sender with given default Retransmission Timeout and possible ISN */
  TCPSender( ByteStream&& input, Wrap32 isn, uint64_t initial_RTO_ms )
    : input_( std::move( input ) ), isn_( isn ), initial_RTO_ms_( initial_RTO_ms )
  {}

  /* Generate an empty TCPSenderMessage */
  TCPSenderMessage make_empty_message() const;

  /* Receive and process a TCPReceiverMessage from the peer's receiver */
  void receive( const TCPReceiverMessage& msg );

  /* Type of the `transmit` function that the push and tick methods can use to send messages */
  using TransmitFunction = std::function<void( const TCPSenderMessage& )>;

  /* Push bytes from the outbound stream */
  void push( const TransmitFunction& transmit );

  /* Time has passed by the given # of milliseconds since the last time the tick() method was called */
  void tick( uint64_t ms_since_last_tick, const TransmitFunction& transmit );

  // Accessors
  uint64_t sequence_numbers_in_flight() const;  // For testing: how many sequence numbers are outstanding?
  uint64_t consecutive_retransmissions() const; // For testing: how many consecutive retransmissions have happened?
  const Writer& writer() const { return input_.writer(); }
  const Reader& reader() const { return input_.reader(); }
  Writer& writer() { return input_.writer(); }

private:
  Reader& reader() { return input_.reader(); }

  void send_tcp_segment_and_update(const TransmitFunction &transmit, const TCPSenderMessage &msg);

  struct retr_timer {

    // void start(uint64_t abs_ms, uint64_t init_rto_);
    // void restart(uint64_t abs_ms);
    // bool expired(uint64_t abs_ms);

    void start(uint64_t abs_ms, uint64_t init_rto_) 
    {
      is_started_ = true;
      init_ms_ = abs_ms;  
      timeout_ = init_rto_;
      con_retr_cnts_ = 0;
    }
    bool expired(uint64_t abs_ms)
    {
      return abs_ms >= init_ms_ + timeout_;
    }
    void restart(uint64_t abs_ms)
    {
      con_retr_cnts_++;
      init_ms_ = abs_ms;
      timeout_ <<= 1;
    }
    void close()
    {
      is_started_ = false;
    }

    bool is_started_{};
    uint64_t con_retr_cnts_{};
    uint64_t timeout_{};
    uint64_t init_ms_{};
  };

  bool is_SYN_{};
  bool is_FIN_{};
  bool is_zero_recv_wnd_{};

  uint16_t max_recv_{ 1 };


  uint64_t snd_win_l{};
  uint64_t snd_win_r{};


  ByteStream input_;
  Wrap32 isn_;
  uint64_t initial_RTO_ms_;
  uint64_t abs_ms_passed_{};

  retr_timer timer_{};

  std::queue<TCPSenderMessage> outgoing_sndmsgs_{};
};


  • tcp_sender.cc
#include "tcp_sender.hh"
#include "debug.hh"
#include "tcp_config.hh"

using namespace std;

// This function is for testing only; don't add extra state to support it.
uint64_t TCPSender::sequence_numbers_in_flight() const
{
  // debug( "unimplemented sequence_numbers_in_flight() called" );
  return snd_win_r - snd_win_l;
}

// This function is for testing only; don't add extra state to support it.
uint64_t TCPSender::consecutive_retransmissions() const
{
//  debug( "unimplemented consecutive_retransmissions() called" );
  return timer_.con_retr_cnts_;
}

void TCPSender::push( const TransmitFunction& transmit )
{
  if (not is_SYN_) {
      TCPSenderMessage syn_msg{};
      syn_msg.SYN = true;
      syn_msg.seqno = isn_;

      if ( reader().bytes_buffered() != 0 && max_recv_ > 1) {
        read(reader(), 
        std::min(static_cast<uint64_t>(TCPConfig::MAX_PAYLOAD_SIZE),
                 static_cast<uint64_t>(max_recv_ - 1) ), 
        syn_msg.payload);
      } 
      if ( max_recv_ > syn_msg.sequence_length() &&  reader().is_finished()) {
        syn_msg.FIN = true;
        is_FIN_ = true;
      }

      send_tcp_segment_and_update(transmit, syn_msg);
      is_SYN_ = true;
    return ;
  }

  if ( is_zero_recv_wnd_ && outgoing_sndmsgs_.empty() ) {
    max_recv_ = 1;
  }

  if ( input_.has_error() && max_recv_ != 0) {
    TCPSenderMessage rst_msg{};
    rst_msg.RST = true;
    rst_msg.seqno = Wrap32::wrap( snd_win_r, isn_);

    send_tcp_segment_and_update( transmit, rst_msg);
    return ;
  }

  while ( reader().bytes_buffered() != 0 && max_recv_ != 0) {
    TCPSenderMessage snd_msg{};
    
    snd_msg.seqno = Wrap32::wrap( snd_win_r, isn_);
    read(reader(), 
        std::min(static_cast<uint64_t>(TCPConfig::MAX_PAYLOAD_SIZE),
                 static_cast<uint64_t>(max_recv_) ), 
        snd_msg.payload);
    
    auto seq_len_no_fin = snd_msg.sequence_length();
    // debug("seq_len_no_fin: {}, recv_wnd_: {}", seq_len_no_fin, max_recv_);

    if (max_recv_ > seq_len_no_fin  && reader().is_finished()) {
      snd_msg.FIN = true;
      is_FIN_ = true;
    }

    send_tcp_segment_and_update(transmit, snd_msg);
    // transmit(snd_msg);
    // outgoing_sndmsgs_.push(snd_msg);
    // snd_win_r += snd_msg.sequence_length();
    // recv_wnd_ -= snd_msg.payload.size();
  }

 //  debug("recv_wnd_: {}, is_FIN_: {}, is_finished: {}", max_recv_, is_FIN_, reader().is_finished());

  if (not is_FIN_ && reader().is_finished() && max_recv_ > 0) {
    is_FIN_ = true;
    TCPSenderMessage snd_msg;
    snd_msg.seqno = Wrap32::wrap( snd_win_r, isn_);
    snd_msg.FIN = true;

    send_tcp_segment_and_update( transmit, snd_msg);
    // transmit(snd_msg);
    // outgoing_sndmsgs_.push(snd_msg);
    // snd_win_r += snd_msg.sequence_length();
  }
}

TCPSenderMessage TCPSender::make_empty_message() const
{
  // debug( "unimplemented make_empty_message() called" );
  // return {};
  TCPSenderMessage msg{};
  msg.seqno = Wrap32::wrap( snd_win_r, isn_);
  if (input_.has_error())
    msg.RST = true;
  

  return msg;
}

void TCPSender::receive( const TCPReceiverMessage& msg )
{
  // debug( "unimplemented receive() called" );
  // (void)msg;

  if ( msg.window_size == 0) {
    is_zero_recv_wnd_ = true;
  }
  else {
    is_zero_recv_wnd_ = false;
  }

  
  if ( msg.ackno.has_value()) {
        auto ack_stream_idx = msg.ackno.value().unwrap( this->isn_, snd_win_l);

        //debug("ack_stream_idx: {}, snd_win_l: {}, snd_win_r: {}\n", ack_stream_idx, snd_win_l, snd_win_r);
        if ( ack_stream_idx > snd_win_l && ack_stream_idx <= snd_win_r) {
          while (!outgoing_sndmsgs_.empty()) {
              auto hd = outgoing_sndmsgs_.front();

              auto sg_l = hd.seqno.unwrap( this->isn_, snd_win_l);
              auto sg_r = sg_l + hd.sequence_length();
              //debug("sg_l: {}, sg_r: {}\n", sg_l, sg_r);

              if (sg_r > ack_stream_idx) {
                  snd_win_l = sg_l;
                  break;
              }
              
              snd_win_l = sg_r;
              outgoing_sndmsgs_.pop();
              timer_.close();
          }
          if (!outgoing_sndmsgs_.empty()) {
            timer_.start(abs_ms_passed_, initial_RTO_ms_);
          }
        }

        if (ack_stream_idx + msg.window_size > snd_win_r)
          max_recv_ = ack_stream_idx + msg.window_size - snd_win_r;
  }
  else {
    max_recv_ = msg.window_size;
  }
  
  if ( msg.RST)
    input_.set_error();
  
}

void TCPSender::tick( uint64_t ms_since_last_tick, const TransmitFunction& transmit )
{
  // debug( "unimplemented tick({}, ...) called", ms_since_last_tick );
  // (void)transmit;
  abs_ms_passed_ += ms_since_last_tick;

  if ( timer_.expired(abs_ms_passed_) ) {
    if (timer_.con_retr_cnts_ <= TCPConfig::MAX_RETX_ATTEMPTS) {

      if (not is_zero_recv_wnd_)
        timer_.restart(abs_ms_passed_);
      else 
        timer_.start(abs_ms_passed_,initial_RTO_ms_);

      if (!outgoing_sndmsgs_.empty()) {
        transmit(outgoing_sndmsgs_.front());
      }
    }
  }
}



void TCPSender::send_tcp_segment_and_update(const TransmitFunction &transmit, const TCPSenderMessage &msg)
{
    auto seq_len = msg.sequence_length();
    max_recv_ -= seq_len;
    snd_win_r += seq_len;
    transmit( msg );

    // if (msg.FIN) {
    //   debug("send packets contain FIN flag!");
    // }
    if (outgoing_sndmsgs_.empty() && not timer_.is_started_) {
      timer_.start( abs_ms_passed_, initial_RTO_ms_);
    }
    outgoing_sndmsgs_.emplace( msg );
}

4. 动手实践

我们给你了一个客户端程序./build/apps/tcp_ipv4,它使用你写的TCPSenderTCPReceiver基于IPTCP与互联网进行通信。

我们同样给了你一个相似的使用linux TCPSocket的程序./build/apps/tcp_native

一个大的问题是: 你自己写的TCP可以和LinuxTCP互相之间进行通信吗?(tcp_ipv4tcp_native)。

4.1 linux的tcp可以和自身通信吗?
  • 首先我们需要保证linux的tcp可以和自己通信,为此我们让服务端启动
./build/apps/tcp_native -l 0 9090
  • 其次我们启动linux的tcp服务端
./build/apps/tcp_native 169.255.144.1 9090
  • 如果一切无误服务端就会显示DEBUG: New connection from 169.254.144.1:36568
    端会显示类似DEBUG: Connecting to 169.254.144.1:9090... DEBUG: Successfully connected to 169.254.144.1:9090

  • 这个时候在两个窗口分别输入一些字符,观察对端有没有正确的显示呢?

  • 可以通过输入CTRL+D来关闭各自写的Bytestream,如果实现正确,你就会看到Outbound stream...finished 。如果是对端关闭,你将会看到Inbound stream...finished 。注意每方的流都是独立的,如果自己方关闭了写入,不会影响对端的接收。

  • 现在关闭第二个方向的流。如果实现正确,两个程序都会正常退出。

4.2 你的tcp实现可以与linux的通信吗?

重复上面的操作,但使用你的tcp去连接linux的tcp。

首先运行

sudo ./scripts/tun.sh start 144

让你的tcp实现有权限在非root情况下发送数据包。

每次重启系统后都需要运行下这个命令。

回到实验,你需要用tcp_ipv4替换掉上面的一个程序(服务端或者客户端)

连接如往常一样建立连接了吗?在窗口中打字对面能收到吗?

如果可以的话,那真是恭喜你了。如果不行,那你可以开始debug了。

你可以用下面的命令抓包进行分析。

sudo rm -f /tmp/cap.raw 
sudo tcpdump -n -w /tmp/cap.raw -i tun144 --print --packet-bufferd

当你在每个方向输入了字时,尝试关闭一端并在另一端继续输入。

观察有没有显示正确呢?

当两端都CTRL+D关闭流时,程序有没有干净地退出呢?

正确的实现应该是可以干净地退出的,即使你可能看到tcp_ipv4

等了一会再退出,这主要是为了减少两军问题的发生。

什么时候需要等待呢?(是先close还是后close ?)

4.3 尝试通过1MB挑战

一旦你完成了上面的基本通信,

尝试在tcp_ipv4tcp_native传送一下文件。

你可以通过下面的命令创建一个大小为12345B的文件/tmp/big.txt

如果是从服务端向客户端发,可以使用下面的命令

  • server
./build/apps/tcp_native -l 0 9090 < /tmp/big.txt
  • client
</dev/null ./build/apps/tcp_ipv4 169.254.144.1 9090 > /tmp/big_r.txt

如果是客户端向服务端发,可以使用下面的命令

  • server
</dev/null ./build/apps/tcp_native -l 0 9090 > /tmp/big_r.txt
  • client
./build/apps/tcp_ipv4 169.255.144.1 9090 < /tmp/big.txt

传输完成后,你可以通过sha-256的哈希值判断这两个文件是否相同

sha256sum /tmp/big.txt
sha256sum /tmp/big_r.txt

你可以尝试不同的文件大小: 12B 65534B 65537B 200KB 1MB

如果都通过了,那么就恭喜你了。

如果没有通过,继续debug吧!

写了脚本,贴出来吧。。。

#!/bin/bash
# make a file size is k named /tmp/"test_"${k}".txt"
# correspond received file name is /tmp/"test_${k}_r".txt


function cmp_two_file_shasum()
{
    [ $# -ne 2 ] && echo "usage: ./cmp_file_shasum <f1> <f2>" && exit
    
    if [ ! -f "$1" ]; then
        echo "$1 not exists" && exit 1
    fi
    if [ ! -f "$2" ]; then
        echo "$2 not exits " && exit 1
    fi

    hash1=$(sha256sum $1  | awk ' {print $1}' )
    hash2=$(sha256sum $2  | awk ' {print $1}' )

    if [ "$hash1" = "$hash2" ]; then
        return 0
    else
        return 1
    fi

}
function get_file_nm_and_create()
{
    local fn="/tmp/test_$1.txt"
    [ ! -f ${fn} ] && (dd if=/dev/random bs=$1 count=1 of=${fn})
    echo ${fn}
}
function get_file_rcv_nm()
{
    local fn="/tmp/test_$1r.txt"
    echo ${fn}
}

function cln_test_files()
{
    rm -f /tmp/test_*.txt
}

function test_conn()
{
file_sz=$1
is_native_server=$2
is_server_send=$3

fn=$(get_file_nm_and_create ${file_sz})
rfn=$(get_file_rcv_nm ${file_sz})

NATIVE_SOCKET="$(pwd)/build/apps/tcp_native"
MINNOW_SOCKET="$(pwd)/build/apps/tcp_ipv4"


INPUT_FILE="< \"$fn\""
OUTPUT_FILE="> \"$rfn\""
INBOUND_CLOSED="</dev/null"




if [ "$is_native_server" = "1" ]; then
    SERVER_IP="169.254.144.1"
    SERVER_PORT="9090"
    srv_proc=$NATIVE_SOCKET
    cln_proc=$MINNOW_SOCKET
    mode_str="native_server"
else
    SERVER_IP="169.254.144.2"
    SERVER_PORT="9090"
    srv_proc=$MINNOW_SOCKET
    cln_proc=$NATIVE_SOCKET
    mode_str="minnow_server"
fi

if [ "$is_server_send" = "1" ]; then
    srv_cmd="${srv_proc} -l ${SERVER_IP} ${SERVER_PORT} ${INPUT_FILE}"
    cln_cmd="${INBOUND_CLOSED} ${cln_proc} ${SERVER_IP} ${SERVER_PORT} ${OUTPUT_FILE}"
    send_str="ssend"
else
    srv_cmd="${INBOUND_CLOSED} ${srv_proc} -l ${SERVER_IP} ${SERVER_PORT} ${OUTPUT_FILE}"
    cln_cmd="${cln_proc} ${SERVER_IP} ${SERVER_PORT} ${INPUT_FILE}"
    send_str="csend"
fi


bash -c "${srv_cmd}" &
srv_pid=$!

# make sure server is started!!!

sleep 3



bash -c "${cln_cmd}" &
cln_pid=$!

wait $cln_pid
wait $srv_pid

cmp_two_file_shasum "${fn}" "${rfn}"

if [ $? -ne 0 ];then
    echo "${file_sz}_${mode_str}_${send_str} test failed"
else 
    echo "${file_sz}_${mode_str}_${send_str} test passed"
fi
}

sz_arrs=(12 1000 65534 65537 200000 1000000)

cln_test_files

for check_sz in ${sz_arrs[@]};
do
native_server=1
minnow_server=0

server_send=1
client_send=0

test_conn $check_sz $native_server $client_send

wait $!

test_conn $check_sz $minnow_server $client_send
wait $!

test_conn $check_sz $native_server $server_send
wait $!

test_conn $check_sz $minnow_server $server_send
wait $!
echo "----------"
done
# choose mode
#  is native_server ?
    # true
    # server_cmd = ./build/app/tcp_native
    # client_cmd = ./build/app/tcp_ipv4

    #false
    # server_cmd = ./build/app/tcp_ipv4
    # client_cmd = ./build/app/tcp_native

# server process start
# client process start

# wait server_pid terminate
# wait client_pid terminate

# compare f and rf
4.4 webget重写
  • tcp_minnow_socket.hh替换socket.hh
  • CS144TCPSocket替换TCPSocket
  • getURL()最后加上socket.wait_until_close()

这部分比较简单,就不叙述更多了。

5. 成果

单独贴下结果吧

check3

在这里插入图片描述

自己写的脚本测试

12_native_server_csend test passed
12_minnow_server_csend test passed
12_native_server_ssend test passed
12_minnow_server_ssend test passed
----------
1000_native_server_csend test passed
1000_minnow_server_csend test passed
1000_native_server_ssend test passed
1000_minnow_server_ssend test passed
----------
65534_native_server_csend test passed
65534_minnow_server_csend test passed
65534_native_server_ssend test passed
65534_minnow_server_ssend test passed
----------
65537_native_server_csend test passed
65537_minnow_server_csend test passed
65537_native_server_ssend test passed
65537_minnow_server_ssend test passed
----------
200000_native_server_csend test passed
200000_minnow_server_csend test passed
200000_native_server_ssend test passed
200000_minnow_server_ssend test passed
----------
1000000_native_server_csend test passed
1000000_minnow_server_csend test passed
1000000_native_server_ssend test passed
1000000_minnow_server_ssend test passed
----------

check_webget

这个需要进入build目录,再make check

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到