%# -*- coding: utf-8-unix -*-

\section{TCP与拥塞控制}
\label{sec:c:wireshark:s:tcp}

\subsection{实验目的}
\label{subsec:c:wireshark:s:tcp_object}

TCP(Transmission Control Protocol传输控制协议)是一种面向连接的、
可靠的、基于字节流的传输层通信协议。
本实验通过运用Wireshark对网络活动进行抓包分析，
观察TCP协议报文，分析通信时序，理解TCP的工作过程，
掌握TCP工作原理与实现；
学会运用Wireshark分析TCP连接管理、流量控制和拥塞控制的过程，发现TCP的性能问题。

\subsection{实验内容}
\label{subsec:c:wireshark:s:tcp_content}

启动Wireshark，捕捉网络活动中的TCP报文并按要求分析。
\begin{enumerate}
  \item 连接管理：观察正常TCP连接中的三次握手与四次挥手报文，
  绘制出时序图，并标出双方TCP状态变化。
  \item 异常情况分析：观察分析TCP连接建立过程的异常
  (例如，尝试连接未存活的主机或未监听端口或客户端发送了第一个SYN连接请求而服务端无响应)；
  观察SYN洪泛影响；观察分析TCP通信过程中的各类异常报文(例如数据超时、乱序)，
  了解其触发机制与含义。
  \item 流量控制(进阶)：运行一组TCP连接客户端/服务器程序(Python代码见节后附件)，
  制造收发不平衡场景，观察收发报文中通告窗口的变化，分析与窗口机制相关的类型报文，
  了解滑动窗口工作原理。
  \item 拥塞控制(进阶)：改变带宽、时延、丢包率等网络参数，观察大文件传输过程，
  分析识别TCP的慢启动、拥塞避免、快速恢复等拥塞控制阶段；
  在构建的网络试验环境下运行iperf3进行网络性能测试，比较不同拥塞控制策略的性能表现。
\end{enumerate}

\subsection{实验原理、方法和手段}
\label{subsec:c:wireshark:s:tcp_principle}

\subsubsection{TCP协议}

作为TCP/IP协议簇中的骨干，
TCP协议基于“尽力而为”的网络层为应用层提供可靠的进程间通信服务，
具体地说是可靠的全双工的端对端字节流传输服务。
在TCP的协议传输单元中(TCP报文段，TCP Segment)，
收发双方以字节为单位使用序号(Sequence Number)明确收发的数据，
使用ACK反馈(Acknowledgment)机制，实现端对端的可靠传输控制。

\paragraph{TCP报文段(Segment)}~{}
\\

TCP 报文段结构如图\ref{fig:c:wireshark_TCP-structure}所示，
采用20字节的报文段头并有最长40字节的可选项。

\small
\begin{figure}[!ht]
  \centering
  \begin{code}[text]
    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |          Source Port          |       Destination Port        |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                        Sequence Number                        |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                    Acknowledgment Number                      |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |  Data |           |U|A|P|R|S|F|                               |
    | Offset| Reserved  |R|C|S|S|Y|I|            Window             |
    |       |           |G|K|H|T|N|N|                               |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |           Checksum            |         Urgent Pointer        |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                    Options                    |    Padding    |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                             data                              |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  \end{code}
% \includegraphics[width=10cm]{TCP-structure}
  \caption{TCP报文段结构标意图}
  \label{fig:c:wireshark_TCP-structure}
\end{figure}

主要字段如下：

\begin{enumerate}
  \item \textbf{源端口号(Source Port)：}16位的源端口，
  与源IP地址一起标识发送该TCP报文段的通信进程。端口号范围0-65535。
  \item \textbf{目的端口号(Destionation Port)：}16位目的端口，
  与目的IP地址一起标识接收该TCP报文段的通信进程。
  \item \textbf{序号(Sequence Number)：}该TCP报文段中第一个数据字节的序号，占4个字节。在TCP连接建立时，通常生成一个随机数作为字节序号的初始值(ISN)。
  \item \textbf{确认号(Acknowledgement Number)：}
  表示期望收到对方下一个报文段的字节序号，占4个字节。
  \item \textbf{标志位(TCP Flags)：}
  \begin{enumerate}
    \item 确认ACK(Acknowledgement)：置1表示确认号字段有效。 
    \item 推送PSH(Push)：置1表示该报文段优先级高，
    接收方TCP应尽快推送给接收应用程序。
    \item 复位RST(Reset)：置1表示需要释放TCP连接并重新建立连接。
    一般称带RST标志的TCP报文段为“复位报文段”。
    \item 同步SYN(Synchronization)：置1表示这是TCP请求连接报文段。
    一般称带SYN标志的TCP报文段为“同步报文段”。
    \item 终止FIN(Finish)：置l表示发送方的数据已经发送完毕，
    并要求释放TCP连接。
  \end{enumerate}
  \item \textbf{窗口大小(Window)：}表示接收缓存大小。
  最早TCP协议首部只设置了16位的窗口大小，允许的最大缓存大小不超过64KB；
  而RFC1323打破此限定，设置了TCP窗口缩放因子(Window size scaling factor)，
  使窗口大小等于二者的乘积。
\end{enumerate}

\paragraph{TCP连接管理}~{}
\\

为维护一个可靠的端对端传输，TCP设计实现了完整的连接管理，
重点是三次握手的连接建立和四次挥手的连接释放过程，
如图\ref{fig:c:wireshark_TCP-time-line}。

\begin{figure}[!ht]
  \centering
  \includegraphics[width=9cm]{TCP-time-line}
  \caption{TCP连接示意时序}
  \label{fig:c:wireshark_TCP-time-line}
\end{figure}

\textbf{建立过程：}TCP是面向连接的，数据传输之前必须在双方之间建立一条连接，
并通过三次握手过程完成。
其主要目的是，同步连接双方的序号和确认号，并交换TCP窗口大小等控制信息。
一般地，客户端主动向服务器端发起连接请求，具体过程如下：

\begin{enumerate}
  \item 第一次握手：建立连接。客户端发送连接请求报文段，
  将SYN位置为1，Sequence Number为x；
  然后，客户端进入SYN\_SEND状态，等待服务器的确认；
  \item 第二次握手：服务器收到SYN报文段。服务器收到客户端的SYN报文段，
  需要对这个SYN报文段进行确认，设置确认号为x+1；
  同时，将SYN位置为1，系列号为y。
  服务器端将上述信息放入SYN+ACK报文段中，
  一起发送给客户端，此时服务器进入SYN\_RECV状态；
  \item 第三次握手：客户端收到服务器的SYN+ACK报文段之后，
  将确认号设置为y+1，向服务器发送ACK报文段。
  这个报文段发送完毕以后，客户端和服务器端都进入ESTABLISHED状态，
  完成TCP三次握手。
\end{enumerate}

\textbf{释放连接(四次挥手)：}客户端没有新数据要发送时，就会释放TCP连接，
发送一个报文段(FIN=1，LEN=0)，进入FIN-WAIT1状态。服务器端收到后，
返回客户端一个ACK报文段，进入CLOSE-WAIT状态。
此时客户端进入FIN-WAIT2状态，不能再发送信息，但仍可接收信息。
服务器端完成数据发送之后，也发出FIN=1的报文请求释放连接，
并进入 LAST-ACK状态，直至客户端返回确认（第四次挥手）才关闭TCP连接。
客户端收到服务器的FIN报文后，则进入TIME\_WAIT状态，
并等待2MSL(最大存活时间- Maximum Segment Lifetime)时间之后，完全关闭TCP连接。
(注：释放连接也可由服务器端先发起)

\paragraph{TCP流量控制}~{}
\\

流量控制的目的是让发送方的数据发送速率不要过快，以便接收方能及时接收。
TCP利用滑动窗口(Sliding Window)机制实施流量控制，
其基本原理是用TCP报文段中的窗口大小字段来控制数据发送速率，
即发送方的发送窗口应小于接收方回应报文中的通告窗口值。
为了提高信道利用率，TCP采用了连续ARQ(Automatic Repeat reQuest)，
TCP两端都可以连续发出若干个分组然后等待确认，
也都设有发送/接收窗口和发送/接收缓存。
其中发送窗口可以用3个指针表示，
而且发送窗口随着TCP报文段中窗口(Window)字段的数值动态变化。
该窗口字段是告知对方自己还能接收多少字节的数据。
发送方只发送序号在发送窗口之内的数据，避免发送过快，
从而实现流量控制。具体的缓存、窗口和指针变化过程请参阅教材相关内容。

若接收方没有空余缓存，就会发送零窗口大小(Window=0)的报文段，
即要求发送端停止数据发送。
然而，当接收方释放出足够缓存后，
发送方往往无法及时恢复数据发送，这就产生了死锁问题。
为解决零窗口(Zero-Window)问题，
TCP为每一个连接设置一个持续计时器(persistence timer)。
只要TCP的一方收到对方的零窗口报文段，就启动该计时器，
并周期性的发送一个长度为1字节的探测报文段(周期亦会增加)。
收到探测报文段的一方，在返回ACK报文段时，
将使用Window字段发送其最新缓存大小；
若最新缓存足够大，数据发送恢复，解决死锁问题。

\paragraph{TCP拥塞控制}~{}
\\

计算机网络中的带宽、节点(路由器和主机)中的缓存和处理机等，
都属于网络的资源，在某段时间，
若对网络中某一资源的需求超出该资源所能提供的可用部分，
网络的性能就会变坏，这种情况就叫做拥塞。
TCP拥塞控制的目的是避免过多的数据注入网络引发路由器或链路资源过载。
拥塞控制是一个全局性的系统化的工程，将报文段丢失视作网络拥塞发生的信号，
通过调整拥塞控制窗口(cwnd - Congestion Window)和利用ACK反馈机制来控制数据发送速率。

TCP Tahoe是TCP的最早版本，主要用三种算法去控制数据流和拥塞窗口：
慢启动(Slow Start)、拥塞避免(Congestion Avoidance)、快重传(Fast Retransmit)。
TCP事先并不知道网络资源的状况，首先采用慢启动算法开始发送数据：
cwnd初始值为1个MSS(Maximum Segment Size)；
每收到一个有效的ACK确认，cwnd+1，
直至出现网络丢包超时或cwnd达到阀值ssthresh（slow start thresh）。
当cwnd等于或大于ssthresh，采用拥塞避免算法，
由AIMD(加性增、积性减)策略控制发送速率。
若TCP报文段发送后RTO (Retransmission Time Out)时间仍未得到确认或收到三个确认号重复的ACK报文段，
则启用快重传算法，并将cwnd重置为1，ssthresh减半。

TCP拥塞控制算法一直处在不断的改进之中，围绕对网络环境因素感知和拥塞避免的控制，
涌现出新的策略算法。TCP Reno继承Tahoe的三个算法并增加了快速恢复(Fast Recovery)算法。
收到三个重复的ACK后，Reno会把当前的ssthresh的值设置为当前cwnd的一半，
并将cwnd更新为ssthresh+3MSS；然后每收到一个重复ACK则cwnd+1，
直至收到新确认号的ACK则将cwnd更新为ssthresh。
TCP NewReno则进一步改进了快速恢复算法。

随着网络速度增长，传统拥塞控制算法的cwnd增长速度影响了TCP的性能，
CUBIC\footnote{\href{https://www.researchgate.net/publication/220623913}{CUBIC: A New TCP-Friendly High-Speed TCP Variant}}
应运而生。
CUBIC的关键特征是：cwnd窗口的增长依赖两次丢包的时间。
2016年，谷歌提出了
BBR\footnote{\href{https://queue.acm.org/detail.cfm?id=3022184}{BBR: Congestion-Based Congestion Control}}
拥塞控制算法，它不再基于丢包感知来调整cwnd，
而是利用估算的带宽和延迟直接推测拥塞程度进而确定发送窗口。

\subsubsection{实验方法和手段}

两台实验机本地相互连接(如图\ref{fig:c:wireshark_TCP-topo})，
在实验机中仿真不同的网络条件，以便观察TCP的各种控制现象。
方案一使用虚拟机：VMware Player中通过“虚拟机设置->硬件->网络适配器->高级”（如图\ref{fig:c:wireshark_VM-advance-setup}）
设置虚拟机的网卡传入/传出带宽、数据包丢失率、延迟等；
方案二使用物理机：使用tc进行流量控制场景仿真，使用wondershaper对网卡进行限速。
本实验需要使用的命令和工具，如表\ref{tab:c:wireshark_tools-command}所列。
常用的Linux命令还包括：echo、cat、sysctl、ping、ftp等。

\begin{figure}[!ht]
  \centering
  \includegraphics[width=9cm]{TCP-topo}
  \caption{实验拓扑图}
  \label{fig:c:wireshark_TCP-topo}
\end{figure}

\begin{figure}[!ht]
  \centering
  \includegraphics[width=6cm]{VM-advance-setup}
  \caption{虚拟机网络适配器高级设置}
  \label{fig:c:wireshark_VM-advance-setup}
\end{figure}

\renewcommand{\arraystretch}{1.5}
\begin{table}[!ht]
  \small
  \centering
  \caption{主要工具及命令列表}
  \label{tab:c:wireshark_tools-command}
  \begin{tabular}{|m{2.5cm}<{\centering}|m{3cm}<{\centering}|m{8.5cm}|} 
    \hline
    \heiti 命令 & \heiti 作用 & \multicolumn{1}{c|}{\heiti 参考}\\ \hline
    \texttt{ifconfig} & 配置网络 & \url{https://man.linuxde.net/ifconfig}\\ \hline
    \texttt{nmap} & 网络扫描 & \url{https://nmap.org/man/zh/index.html}\\ \hline
    \texttt{curl} & 文本浏览器 & \url{https://man.linuxde.net/curl}\\ \hline
    \texttt{wget} & 下载Web文件 & \texttt{wget <IP>/<PathAndFileName>}\\ \hline
    \texttt{tc} & 流量控制 & \href{https://man7.org/linux/man-pages/man8/tc.8.html}{tc命令手册} \\ \hline
    \texttt{iptables} & 防火墙配置 & \url{https://man.linuxde.net/iptables}\\ \hline
    \texttt{netwox} & 网络工具 & \url{https://sourceforge.net/projects/ntwox/}\\ \hline
    \texttt{ss} & Socket状态 & \texttt{ss –atn}\\ \hline
    \texttt{netstat} & 显示网络状态 & \texttt{netstat –atn}\\ \hline
    \texttt{wondershaper} & 网卡限速工具 & \texttt{wondershaper [网口] [下载速率] [上行速率]   wondershaper clear [网口]}\\ \hline    
    \texttt{iperf3} & 网络性能分析 & \url{https://iperf.fr/}\\ \hline
  \end{tabular}
\end{table}

\subsection{实验条件}
\label{subsec:c:wireshark:s:tcp_requirement}

\begin{enumerate}
  \item 硬件：处于同一局域网的两台PC机(可使用虚拟机也可使用物理机)。
  \item 软件：Ubuntu系统(18.04版)，
  预装wireshark、curl、vsftp、netwox、telnet、nmap和iperf3。
  \item 环境准备：分别以PC1、PC2作为TCP的客户端与服务端；
  启动两台实验机后，可使用ping进行连接性测试，
  也可使用nmap扫一下对方打开的端口，
  确保实验环境正常 
  (指导书中PC2的IP为192.168.100.144，实验中应替换为实际的IP) 。
\end{enumerate}
参考资料：

\begin{itemize}
  \item \href{https://tools.ietf.org/html/rfc793}{TCP协议RFC}
  \item \href{http://www-net.cs.umass.edu/wireshark-labs/Wireshark_TCP_v7.0.pdf}
  {计算机网络--自顶向下方法：Wireshark实验指导书--TCP}
  \item \href{https://www.semanticscholar.org/paper/Congestion-Control-in-Linux-TCP-Sarolahti-Kuznetsov/0e9c968d09ab2e53e24c4dca5b2d67c7f7140f8e}
  {Congestion Control in Linux TCP}
\end{itemize}

\subsection{实验步骤}
\label{subsec:c:wireshark:s:tcp_procedure}

\subsubsection{TCP正常连接观察}

\begin{enumerate}
  \item 利用python自带的SimpleHTTPServer模块，
  在PC2上启动一个简易的web服务器。
  终端上运行\texttt{echo "TCP lab test" > index.html}创建index.html文件为测试站首页，
  运行\texttt{sudo python -m SimpleHTTPServer 80}启动一个简易web服务器；
  打开新终端，键入\texttt{ss -tln}查看当前主机打开的TCP连接，确认80端口处理监听状态。
  \item 在PC1上打开一个终端，键入\texttt{sudo wireshark}启动抓包软件；
  再打开一个新终端，键入 curl <PC2的IP>  ；
  停止抓包，在wireshark过滤出TCP类型报文。
  观察首个TCP报文头，并分析各段值代表的意义。
  如果想要关闭相对序号/确认号，
  可以选择Wireshark菜单栏中的
  Edit$\rightarrow$Preference$\rightarrow$protocols$\rightarrow$TCP，
  去掉Relative sequence number勾选项。
  使用Wireshark内置的绘制流功能，选择菜单栏中的Statistics$\rightarrow$Flow Graph，
  Flow Type选择TCP flows可以直观地显示TCP序号和确认号是如何工作的。
  \item 观察TCP三次握手与四次挥手报文，注意报文收发过程中，双方TCP状态的变化。
  以本次抓得报文为据，分别画出本次TCP连接三次握手与四次挥手的时序图，
  结合TCP状态机，在双方各阶段标出对应的TCP状态。选择其中一个TCP报文，
  配合Wireshark截图，分析该报文TCP首部各字段的定义、值及其含义。
\end{enumerate}

\subsubsection{异常传输观察分析}

\begin{enumerate}
  \item 尝试连接未存活的主机或对未监听端口。
  \begin{enumerate}
    \item 用curl访问一个不存在的主机IP，抓包观察共发送了几次SYN报文。
    根据每次时间间隔变化，估算RTO（重传超时）。
    \item 查看Linux主机的系统的TCP参数SYN重传设定：
    
    \texttt{cat /proc/sys/net/ipv4/tcp\_syn\_retries}
    \item 更改SYN重传次数为3：
    
    \texttt{echo "3" > /proc/sys/net/ipv4/tcp\_syn\_retries}
    \item 再次curl访问，观察抓包内容。
    \item 关闭服务器端的SimpleHTTPServer(ctrl+C中断，或关闭所在终端)，
    客户端curl访问服务器80端口，观察应答报文。
    \item 运行\texttt{nmap -sS <PC2的IP>}扫描服务器，并抓包。
    \item 在报告中总结以上观察结果，解释SYN扫描原理。
  \end{enumerate}
  \item 客户端发送了第一个SYN连接请求，服务器无响应的情景。
  \begin{enumerate}
    \item 服务器开启telnet或ssh服务，客户端先尝试连接服务器，
    连接成功后，在双方键入 ss -tan 查看所有TCP连接状态。
    我们看到的TCP连接建立过程同1中的HTTP访问类似。
    在客户端，利用iptables拦截服务器回应的SYN ACK包，
    命令如下：

    \texttt{sudo iptables -I INPUT -s 192.168.100.144 -p tcp -m tcp --tcp-flags ALL SYN, ACK -j DROP} 
    \item 再次尝试连接并启动wireshark抓包，并在双方多次用ss -tan观察TCP状态。
    \item 观察TCP的状态变化，分析wireshark抓到的TCP异常报文。
    \item 服务端的SYN-RECV 状态何时释放？
    \item SYN ACK重传了几次，时间间隔有何变化？
    \item 参考1中的操作，在服务端修改SYN ACK重传次数(tcp\_synack\_retries)，
    再次观察，此任务结束后清空防火墙规则(iptables -F)。
  \end{enumerate}
  \item SYN洪泛。
  在服务器端\texttt{sudo echo "0">/proc/sys/net/ipv4/tcp\_syncookies}禁用syncookies，
  通过\texttt{sudo sysctl -w net.ipv4.tcp\_max\_syn\_backlog = 6}
  指定所能接受SYN同步包的最大客户端数量为6；
  在客户端运用netwox工具对服务器监听的端口产生大量SYN连接请求
  (如\texttt{sudo netwox 76 -i 192.168.100.144 -p 23})，
  再使用正常的连接工具(如telnet)连接，观察现象(特别是服务器端的TCP连接状态)，
  抓包总结分析，解释SYN泛洪攻击原理与对策。
  \item 异常报文分析。在服务器端产生一个100M的大文件，
  利用1中的web服务器，在客户端上用wget下载它。
  参考命令：
  \begin{enumerate}
    \item 产生一个100M文件：
    
    \texttt{dd if=/dev/zero of=100M.file bs=1M count=100} 
    \item 模拟网络延迟、包重复、包乱序、包损坏：
    
    \texttt{tc qdisc add dev ens33 root netem delay 70ms 10ms 30\% duplicate 1\% reorder 5\% 10\% corrupt 0.1\%}

    (调整上述命令中的数值，以达到期望效果；将此行命令的add改为change/del即修改/删除此规则。)
    \item 下载服务器上的大文件：\texttt{wget 192.168.100.144/100M.file}
  \end{enumerate}

  抓包记录以上过程，分析黑色标签错误报文，结合TCP实现机制，
  分析这些报文产生的原因。此类报文也可以从现实网络行为抓取获得，
  请结合实际抓得报文分析，报文附件随报告提交。
  包括但不限于以下几种类型报文：
  [Duplicate ACK]、[TCP Retransmission]、[Fast  Retransmission]、
  [TCP Spurious Retransmission]、[TCP Out-Of-Order]、
  [TCP Previous segment not captured]。
\end{enumerate}

\subsubsection{流量控制}

\begin{enumerate}
  \item 编写一对简单的TCP连接程序，
  也可以直接运行指导书提供的Python程序
  (源代码见本节最后的\nameref{subsec:c:wireshark:s:tcp_additionalprg})。
  在客户端快速发送数据给服务端，而服务端则有意缓慢地接收数据，
  观察TCP如何用窗口大小值进行流量控制。虚拟机两端分别运行
  \texttt{python3 server.py}和\texttt{python3 client.py}。
  \item 抓取两端通信报文数据，分析报文中的Win值变化，联系上下报文，
  解释为什么出现[TCP Windows Full]、[TCP ZeroWindows]、[TCP Keep-Alive]
  等和窗口大小相关的流量控制报文。
  抓取的原始报文存成附件随实验报告提交。
\end{enumerate}

\subsubsection{拥塞控制}
\begin{enumerate}
  \item 任一端限制网卡传入/传出带宽为10Mbps以下：使用虚拟机作为实验机，可
  在VMWare Player中的虚拟机设置$\rightarrow$网络适配器$\rightarrow$高级中设置；
  物理机可使用wondershaper命令进行限速。
  再启动应用(可以是http wget，也可以ftp下载/上传)传输大文件观察。
  \item Wireshark抓取全部传输过程数据，找出该网络活动的拥塞点，
  并结合Analyze$\rightarrow$Expert Information、Statistic$\rightarrow$IO Graphs、
  Statistic$\rightarrow$TCP Stream Graphs(如图\ref{fig:c:wireshark_io-graphs})，
  分析此传输过程中的慢启动、拥塞避免、快速恢复等阶段。

  \begin{figure}[!ht]
    \centering
    \includegraphics[width=8cm]{io-graphs}
    \caption{IO Graphs}
    \label{fig:c:wireshark_io-graphs}
  \end{figure}
  
  \item TCP竞争观察：类似以上试验，我们在一个大文件传输过程中，
  迅速启动另一应用，双向进行大文件传输，观察两路（或两路以上）TCP连接的速率变化。
  \item TCP拥塞控制算法比较：运用iperf3性能分析工具，
  设置ubuntu系统的tcp\_congestion\_control策略，
  分析比较不同网络环境下、不同拥塞控制算法的表现。
  参考如下命令（灵活组合使用）：
  \begin{enumerate}
    \item 可用策略：
    
    \texttt{cat /proc/sys/net/ipv4/tcp\_available\_congestion\_control}
    \item 修改策略：
    
    \texttt{sysctl -w net.ipv4.tcp\_congestion\_control=bbr}
    \item iperf3服务端启动： \texttt{iperf3 -s -p 5555}
    \item iperf3客户端启动： \texttt{iperf3 -c 192.168.100.144 -p 5555}
    \item 设置链路带宽、时延、丢包率可以使用虚拟网卡高级属性配置，
    也可以参考前面使用tc命令配置。
  \end{enumerate}
\end{enumerate}

\subsection{思考题}
\label{subsec:c:wireshark:s:tcp_rethink}

\begin{enumerate}
  \item TCP在不可靠的IP层上建立了可靠的端对端连接，
  如果要在不可靠的UDP上建立可靠的端对端传输系统，需要考虑哪些方面？
  \item TCP连接建立过程中，存在哪些等待队列？
  这些队列是否可能出现溢出状况？该如何避免？
  \item 本次实验观察了Linux环境下的TCP实现，在Windows、macOS环境下，
  操作系统又是如何实现TCP的呢？
  类似Linux TCP参数，在不同系统环境下如何查看或设置，
  请尝试通过抓包其通信过程发现其实现异同。
  \item 在TCP状态机(图\ref{fig:c:wireshark_TCP-status-machine})中，
  有些状态停留时间较长，易观察到，有些状态很短暂不易观察到。
  试列出不易观察到的状态，并考虑观察到它们的可能方法。
  \item TCP是封装单元为MSS，可是我们在抓包过程中常发现远大于此值的TCP包，
  为什么TCP可以提交如此大的报文呢？
  此类型的包远超出链路层的MTU，它是如何被处理的呢？请从两端同时抓包观察比对。

  \begin{figure}[!ht]
    \centering
    \includegraphics[width=9cm]{TCP-status-machine}
    \caption{TCP连接管理状态机}
    \label{fig:c:wireshark_TCP-status-machine}
  \end{figure}

\end{enumerate}

\subsection{注意事项及有关说明}
\label{subsec:c:wireshark:s:tcp_notice}

\begin{enumerate}
  \item Linux上运行wireshark抓包需要root权限，
  请先在终端中通过su命令切换为root后，再键入\texttt{wireshark \&}启动；
  或是打开个终端键入\texttt{sudo wireshark。netwox/netwag}同样需要root权限。
  \item Ubuntu中装有python2和python3两种类型版本，
  实验中用了python2启动简易Web服务器，附件中的程序用Python3运行。
  \item 环境还原：前面操作的iptables、tc遗留规则可能会影响后面的操作效果，
  \texttt{iptables --list}查看核对一下当前的规则，
  \texttt{iptables -F}清空当前规则；
  同样，使用\texttt{tc qdisc del dev eth0 root RULE}清除网卡eth0队列规则；
  wondershaper限速使用clear参数清除。
  使用虚拟机的快照功能是更原始、更彻底的还原方式。
  \item 批量网络扫描是危害网络行为，仅在实验室环境下进行试验学习，
  不得用于运营网络。
\end{enumerate}

\subsection{考核方法}
\label{subsec:c:wireshark:s:tcp_criterion}

完成本次实验，并提交一份实验报告和一组Wireshark数据存储文件。
报告内容应当包括以下部分，相关的分析解释都对应有截图证明，并与数据存储文件吻合。
\begin{enumerate}
  \item (20分) 正确绘制出了三次握手报文与四次挥手报文(须结合捕获的报文)，
  并正确标识出了各阶段TCP状态；
  \item (20分) 观察传输异常现象，并进行分析；
  \item (20分) 完成流量控制操作要求，结合上下分析报文窗口变化，
  解释说明相关的类型报文成因。
  \item (20分) 完成拥塞控制操作要求，
  成功从抓得数据分析出了一次完整TCP流的各阶段（慢启动、拥塞控制、快速恢复）。
  \item (10分)完成任2道思考题。
  \item (10分)记录自己在本次实验中所遇到的问题，以及心得感悟实验总结。
\end{enumerate}

\subsection{附件}
\label{subsec:c:wireshark:s:tcp_additionalprg}

为了方便进行实验，提供一份Python3套接字通信作为附件，具体代码如下：
\begin{itemize}
  \item 服务端 server.py
\begin{code}[python]
  import socket
  import time
  def recv_data(sock,length):
      data=b''
      while len(data) < length:
          more = sock.recv(length - len(data))
          if not more:
              pass
          data += more
      return data         
  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  # 设置接收缓冲区大小为1024
  s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1024)
  s.bind(('0.0.0.0', 9999))
  s.listen(1)
  print("Sever listening on 9999...")
  try:
      sc, addr = s.accept()
      while True:
          rcvdata = recv_data(sc,16)
          time.sleep(0.01)
          pass
  finally:
      sc.close()
      s.close() 
\end{code}

\item 客户端 client.py
\begin{code}[python]
  import socket
  import time
  ip_port = ("192.168.100.144", 9999)
  data = "0123456789\n"*100
  c = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  try:
      c.connect(ip_port)
      print("Connected.")
      time.sleep(1)  
      i=0
      while i<10:
          print("Send:"+data)
          c.send(data.encode())
          i=i+1
  finally:
      print("Sent. Waiting....")
  while True:
      time.sleep(0.001)  
\end{code}

\end{itemize}
