网络-TCP协议详解自学笔记(例题、代码、实战)

目录

简介

TCP的服务与缓存

服务

缓存

TCP头部

TCP的建立与终止

三次握手

四次挥手

例题

有限状态机

可靠传输

校验

滑动窗口

确认与重传

累计确认与超时重传

冗余确认与快速重传

流量控制

例题

拥塞控制

慢开始与拥塞避免

快重传与快恢复

例题

TCP和UDP的区别

实战

抓包

三次握手

参考


简介

TCP协议又叫传输控制协议(Transport Control Protocal),是面向连接的可靠的字节流服务。它的连接是虚连接,连接的端点是套接字(SOCKET)。它的可靠性体现在:3次握手建立连接滑动窗口机制,一定的拥塞避免算法,流量控制,以及一定的超时重传机制。基于TCP的协议有HTTP、FTP、SMTP、TELNET、SSH、MQTT等。

TCP的服务与缓存

服务

谈TCP服务之前先说一下计时器,很多服务需要计时器的支持。

  • 2MSL计时器测量一个连接处于TIME_WAIT状态的时间,四次挥手(连接释放)时有用到。
  • 重传计时器使用于当希望收到另一端的确认。超时重传、拥塞避免时有用到。
  • 坚持计时器使窗口大小信息保持不断流动,即接收端发送了接收窗口为0的报文后启动。流量控制时有用到。
  • 保活计时器可检测到一个空闲连接的另一端何时崩溃或重启。

接下来是TCP提供的服务:

数据块分割:应用数据被分割成TCP认为最适合发送的数据块,由TCP传递给IP的信息单位称为报文段或段(segment)。

重新排序:既然TCP报文段作为IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段的到达也可能会失序。如果必要,TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层。

全双工(Full Duplex)通信:又称为双向同时通信,即通信的双方可以同时发送和接收信息的信息交互方式。

可靠传输:通过校验和、序号、确认与重传进行可靠的传输。

流量控制:通过滑动窗口算法,设置接收窗口大小来进行流量控制。

拥塞控制:通过慢开始&拥塞避免快重传&快恢复算法进行拥塞控制。

不提供广播或多播的服务。

缓存

发送缓存:准备发送的数据和已发送但未收到确认的数据

网络-TCP协议详解自学笔记(例题、代码、实战)_第1张图片 发送缓存图解

图中红色部分为已发送但未收到确认的数据,发送缓存中的其余部分为准备发送的数据。

接收缓存:按序到达但未接受应用程序读取的数据和不按序到达的数据

网络-TCP协议详解自学笔记(例题、代码、实战)_第2张图片 接收缓存图解

图中接收缓存中标识“已收到”的表示按序到达但未接受应用程序读取的数据,接收窗口中的红色部分是不按序到达的数据。

TCP头部

网络-TCP协议详解自学笔记(例题、代码、实战)_第3张图片 TCP首部格式
  • 源端口和目的端口:各占 2 字节。端口是传输层与应用层的服务接口.传输层的复用和分用功能都要通过端口才能实现
  • 序号seq:占4字节。数据中首个字节在发送缓存中的序号。
  • 确认序号ack:占4字节。为N时表明序号N-1及之前的报文段都已经收到,期待接收序号为N的报文段。
  • 首部长度/数据偏移: 占 4 位。它指出 TCP 报文段的数据起始处距离 TCP 报文段的起始处有多远.“数据偏移”的单位是 32 位字(以 4 字节为计算单位)。
  • 紧急位URG: 为1时,紧急指针有效,紧急数据优先级更高,优先发送。
  • 确认位ACK: 为1时,确认序号ack有效,连接建立后,传送的报文段都必须把ACK置为1。
  • 推送位PSH: 为1时,接收方应该尽快将这个报文段交给应用层,不用等缓存填满,优先级更高,报文段优先交给应用层。
  • 复位RST: 为1时,表明TCP连接中出现严重差错,需要释放连接后,重建连接。
  • 同步位SYN: 为1时,用来发起/确认一个连接。
  • 终止位FIN: 为1时,表明发端完成发送任务,要求释放连接。
  • 窗口:接收窗口,即允许对方发送的数据量。
  • 检验和:检验首部+数据
  • 紧急指针:URG为1时有效,指出本报文段中紧急数据的字节数(从数据的第一个字节开始)。
  • 选项:最大报文段长度MSS(Maximum Segment Size):发送方告诉接收方,自身缓存所能接收的报文段的数据字段的最大长度是 MSS 个字节。

       窗口扩大:占 3 字节,其中有一个字节表示移位值 S.新的窗口值等于TCP 首部中的窗口位数增大到(16 + S),相当于把窗口值向左移动 S 位后获得实际的窗口大小

       时间戳:占10 字节,其中最主要的字段时间戳值字段(4字节)和时间戳回送回答字段(4字节)

       选择确认:接收方收到了和前面的字节流不连续的两字节。如果这些字节的序号都在接收窗口之内,那么接收方就先收下这些数据,但要把这些信息准确地告诉发送方,使发送方不要再重复发送这些已收到的数据。

  • 填充:这是为了使整个首部长度是 4 字节的整数倍。

TCP的建立与终止

TCP连接的建立采用客户/服务器方式,主动发起连接建立的应用进程叫做客户,而被动等待连接建立的应用进程叫服务器

TCP连接四元组(sip,sport,dip,dport),源ip、源端口号、目的ip、目的端口号。

三次握手

网络-TCP协议详解自学笔记(例题、代码、实战)_第4张图片 TCP三次握手

 客户端发送连接请求报文段,无应用层数据。同步位SYN=1,表示请求连接。序号seq=x(随机)。确认序号无效,ACK=0,因为客户端未收到服务器端的报文段。

  1. 服务器为该TCP连接分配缓存和变量,向客户端返回确认报文段,允许连接,无应用层数据。同步位SYN=1,表示确认连接。序号seq=y(随机),确认位ACK=1,确认序号有效,确认序号ack=x+1,即x+1之前的已经收到,期待接收x+1开始的报文段。
  2. 客户端为该TCP连接分配缓存和变量,并向服务器端返回确认报文段,可以携带数据。同步位SYN=0,表示连接建立完毕,以后的也是0。序号seq=x+1。确认位ACK=1,确认序号ack=y+1,即y+1之前的已经收到,期待接收y+1开始的报文段。

CLOSED: 表示初始关闭状态。

LISTEN(服务器): 这个也是非常容易理解的一个状态,表示服务器端的某个SOCKET处于监听状态,可以接受连接了。

SYN-SENT: 这个状态与SYN-RCVD呼应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即它会进入到了SYN-SENT状态,并等待服务端的发送三次握手中的第2个报文。SYN-SENT状态表示客户端已发送SYN报文。

SYN-RCVD(服务器): 这个状态表示接受到了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂,基本上很难看到这种状态的,除非你特意写了一个客户端测试程序,故意将三次TCP握手过程中最后一个ACK报文不予发送。当收到客户端的ACK报文后,它会进入到ESTABLISHED状态。

ESTABLISHED:这个容易理解了,表示连接已经建立了,可以发送数据。

SYN洪泛攻击:攻击者向服务器发送大量TCP连接请求,服务器确认后,攻击者不确认,造成服务器处于半连接状态,消耗了过多的资源(CPU、内存等),造成死机等问题,无法为正常用户提供服务。可使用SYN Cookie进行防御。

四次挥手

客户端和服务器端都可以终止该连接,连接结束后,主机中的资源被释放。

以客户端主动终止连接为例。

网络-TCP协议详解自学笔记(例题、代码、实战)_第5张图片 TCP四次挥手
  1. 客户端发送连接释放报文段,停止发送数据。结束位FIN=1,表明要释放报文段,序号seq=u,假设上一个报文段序号为u-1。
  2. 服务器端回复一个确认报文段,客户到服务器这个方向的连接就释放了——半关闭状态。确认位ACK=1,序号seq=v,假设上一个报文段序号为v-1,确认序号ack=u+1。
  3. 服务器端发完数据,就发出连接释放报文段,主动关闭TCP连接。结束位FIN=1,确认位ACK=1,序号seq=w(服务器端上次发送的数据序号为v~w-1),确认序号ack=u+1(因为客户端未发送数据)。
  4. 客户端回复一个确认报文段。确认位ACK=1,序号seq=u+1,确认序号ack=w+1。

ESTABLISHED:建立连接成功,通信中。

CLOSE-WAIT: 表示被动关闭一方在等待关闭。当主动关闭连接的一方关闭SOCKET后发送FIN报文给被动关闭一方,被动关闭一方回应一个ACK报文给对方,此时被动关闭一方则进入到CLOSE-WAIT状态

FIN-WAIT-1:是当Socket在已经连接的状态时主动关闭连接,向对方发送了FIN报文,此时该Socket进入到FIN-WAIT-1状态。而当对方回应ACK报文后,则进入到FIN-WAIT-2状态

FIN-WAIT-2表示半连接,挥了两次手的状态等待对方的Fin报文

TIM-WAIT:TCP协议中主动关闭连接的一方要处于TIME-WAIT状态,等待两个MSL(maximum segment lifetime)的时间后才能回到CLOSED状态,在TIME-WAIT期间仍然不能再次监听同样的server端口。

LAST-ACK: 被动关闭一方在发送FIN报 文后,最后等待对方的ACK报文。当收到ACK报文后进入CLOSED状态。

CLOSED:已经完全关闭.

服务器端收到确认报文段后关闭TCP连接。客户端等待两倍的MSL(Maximum Segment Lifetime,报文段最长寿命)时间后关闭连接,因为如果服务器端没有收到客户端回复的确认报文段会再次发出连接释放报文段,2MSL后没有收到服务器端再次发出的连接释放报文段,说明服务器端已关闭。

例题

[例题]主机甲与主机乙之间已建立一个TCP连接,主机甲向主机乙发送了两个连续的TCP段,分别包含300字节和500字节的有效载荷,第一个段的序列号为200,主机乙正确接收到两个段后,发送给主机甲的确认序列号是

A.500 B.700 C.800 D.1000

[解析]确认序号即期待的下一个报文段的开始字节,由于主机乙正确接收了两个段,所以乙收到了200~999(200+800-1)字节,期待第1000字节,所以选择D选项。

有限状态机

网络-TCP协议详解自学笔记(例题、代码、实战)_第6张图片 TCP有限状态机

粗实线为客户端三次握手与四次挥手的状态转移,粗虚线为服务器端三次握手与四次挥手的状态转移。

可靠传输

校验

增加伪首部

滑动窗口

字节为单位的滑动窗口,大小可变。

接收窗口rwnd(recevier window):接收方根据接收缓存设置的值通知发送方,反映接收方容量。

拥塞窗口cwnd(congestion window):发送方根据自己估算的网络拥塞程度而设置的窗口值,反应网络当前容量。初始值IW根据SMSS(SENDER MAXIMUM SEGMENT SIZE,发送方最大报文段大小,不包含TCP头部)来设定。

发送窗口 = Min{接收窗口rwnd,拥塞窗口cwnd}

确认与重传

累计确认与超时重传

超时重传:TCP发送方在规定的时间(重传时间,使用重传计时器)内没有收到确认就要重传已发送的报文段。【重传时间:采用自适应算法,动态改变重传时间RTTs(加权平均往返时间)。RTT(round-trip time)为数据完全发送完(交给发送方IP层)到收到确认信号的时间。】

累计确认:收到多个确认一次。接收方“痴心不改”,例如,收到序号1、2、3、6的报文段,到了时间会发送确认序号ack=4的报文段,即使后续再收到了7、5,也依然会通知发送方“小老弟,我还是想要序号为4的报文段”。

超时重传解决的是传输可靠性问题,累计确认是为了更快速的传输。

冗余确认与快速重传

冗余确认:TCP接收方已经发送过的确认报文段。由于收到了比确认报文段所期待的报文段序号更大的报文段,而未收到确认报文段所期待的报文段,则再次发送确认报文段。如前面的序号4。【冗余确认是原因可能是乱序到达和丢包,两次的话可能是乱序到达造成的,三次的话绝大部分是丢包了,需要快速重传。】

快速重传:TCP发送方在未达到重传时间时收到接收方的冗余确认(三个重复的)就要重传已发送的报文段。

流量控制

目的:接收方希望发送方发慢一点,好来得及接收。

方法:利用滑动窗口机制实现流量控制。

在通信过程中,接收方根据自己接收缓存的大小,动态地调整发送方的发送窗口大小,即接收窗口rwnd(接收方设置确认报文段的窗口字段来将rwnd通知给发送方),发送方的发送窗口取接收窗口rwnd和拥塞窗口cwnd的最小值

举例:

开始时,B通知A“老弟,我的接收窗口是400”。

网络-TCP协议详解自学笔记(例题、代码、实战)_第7张图片 流量控制-滑动窗口

注:填充黄色的为发送方A的发送窗口,绿色字体代表已发送的数据(接收方不一定接收,看ack)。

最后,B通知A接收窗口为0,这时,B可能在忙着给应用层传输数据。那么,什么时候A可以再给B发送数据呢?需要B再次给A发送一个报文段,通知A“老弟,你可以发送数据了,rwnd=xxx”。

但是,假如B发送的这个报文,A没有收到,这时A等待B通知,B等待A发送数据,就会死锁。为了解决这个问题,需要使用坚持计时器

在发送方收到接收方的rwnd=0时,坚持计时器启动。坚持计时器到期,发送方发送零窗口探测报文段(仅1字节),询问接收方“老哥,你还不接收数据吗?”,如果B给A发送过了通知,这时接收到发送方的探测报文段,就知道它的通知丢失了,A没有收到,就会再发送通知,A接收到后继续发送数据,双方正常通信。

注:TCP规定,即使设置为零窗口,也必须接收以下几种报文段:零窗口探测报文段、确认报文段和携带紧急数据的报文段。

例题

[例题]主机甲和主机乙之间已建立了一个TCP连接,TCP最大段长度为1000字节。若主机甲的当前拥塞窗口为4000字节,在主机甲向主机乙连续发送两个最大段后,成功收到主机乙发送的第一个段的确认段,确认段中通告的接收窗口大小为2000字节,则此时主机甲还可以向主机乙发送的最大字节数是
A.1000 B.2000 C.3000 D.4000

[解析]发送窗口 = Min{rwnd,cwnd} = Min{2000,4000}=2000。第一个1000字节已确认,窗口滑动,第一个1000字节不在发送窗口中。由于第二个1000字节没有被主机乙确认,可能丢失,存在需要重传的可能性,这1000字节还在发送窗口中,因此,还可以发送的最大字节数是2000-1000=1000字节。之后主机甲等待主机乙确认或超时重传。

拥塞控制

出现条件:资源需求总和>可用资源。

目的:防止过多的数据注入到网络中。

假设:数据单方向传送,另一方向只传送确认,发送方接到确认后增加拥塞窗口大小。接收方有足够大的缓存空间,发送窗口取决于拥塞程度,即发送窗口=拥塞窗口

为了讨论方便,纵坐标的单位为最大报文段,长度为MSS。横纵标单位为发送了一批报文段并收到他们确认的时间,即往返时延RTT

下面的算法,可查看RFC 5681

慢开始与拥塞避免

ssthresh(slow start thresh):慢开始阈值,达到后“加法增大”。

乘法减小:新的ssthresh为上次拥塞状态下拥塞窗口的一半,RFC 5681中写的是ssthresh = max (FlightSize / 2, 2*SMSS),我们这里就直接采用FlightSize / 2了。

慢开始:起始值特别低,例如1,倍增,超时后回到起始值。

拥塞避免:达到ssthresh后,加法增大。达到网络拥塞状态后,拥塞窗口回归到慢开始状态即初始值。

网络-TCP协议详解自学笔记(例题、代码、实战)_第8张图片 慢开始和拥塞避免

开始值为1,未达到ssthresh前,执行慢开始算法,倍增,1、2、4、8、16;达到ssthresh,执行拥塞避免算法,加法增大,17、18、19、...、24,超时,新的ssthresh=24/2=12,回到1,后续类似。

快重传与快恢复

快速重传:TCP发送方在未达到重传时间时收到接收方的冗余确认(三个重复的,加上之前收到的,共4个一样的ack)就要重传已发送的报文段。

快恢复:达到网络拥塞状态后,拥塞窗口不是回归到初始状态,而是从新的ssthresh(原来的减半)开始,加法增大

网络-TCP协议详解自学笔记(例题、代码、实战)_第9张图片 快重传和快恢复

开始值为1,未达到ssthresh前,执行慢开始算法,倍增,1、2、4、8、16;达到ssthresh,执行拥塞避免算法,加法增大,17、18、19、...、24,收到3个重复的确认,新的ssthresh=24/2=12,回到新的ssthresh即12,后续类似。

例题

[例题] 一个TCP连接总是以1KB的最大段长发送TCP段,发送方有足够多的数据要发送。当拥塞窗口为16KB时发生了超时,如果接下来的4个RTT时间内的TCP段的传输都是成功的,那么当第四个RTT时间内发送的所有TCP段都得到肯定应答时,拥塞窗口大小是

A. 7KB B.8KB C.9KB D.16KB

[解析]由题可知是超时而不是收到3个重复确认,所以采用的是慢开始与拥塞避免算法。超时后进行下一轮,新的ssthresh=16/2=8。4个RTT拥塞窗口分别为1、2、4、8,此时达到新的ssthresh,所以拥塞窗口采用加法增大,应该是9KB,选择C选项。

[例题]主机甲和主机乙已建立了TCP连接,甲始终以MSS= 1KB大小的段发送数据,并一直有数据发送;乙每收到一个数据段都会发出一个接收窗口为10KB的确认段。若甲在t时刻发生超时时拥塞窗口为8KB,则从t时刻起,不再发生超时的情况下,经过10个RTT后,甲的发送窗口

A.10KB B.12KB C.14KB D.15KB

[解析]超时后,新的ssthresh=8/2=4。拥塞窗口慢开始阶段变化为1、2、4,之后进入拥塞避免阶段,5、6、7、8、9、10、11,之后拥塞窗口是12。注意,题目问的是发送窗口,发送窗口大小为Min{10,12}=10KB,选择选项A。

TCP和UDP的区别

TCP和UDP的区别
比较项 TCP UDP
面向连接
可靠/安全
速度
面向 字节流 报文
应用范围 大量数据 少量数据

实战

自己抓包,分析一下各个字段。以百度为例。

抓包

cmd下,ping命令,获取百度ip

网络-TCP协议详解自学笔记(例题、代码、实战)_第10张图片 百度ip

打开wireshark,设置过滤器tcp&&ip,.addr==61.135.169.121,运行wireshark,浏览器百度一下lady_killer。

网络-TCP协议详解自学笔记(例题、代码、实战)_第11张图片 抓包结果

三次握手

网络-TCP协议详解自学笔记(例题、代码、实战)_第12张图片 第一次握手
网络-TCP协议详解自学笔记(例题、代码、实战)_第13张图片 标志位
网络-TCP协议详解自学笔记(例题、代码、实战)_第14张图片 选项

  • 源端口和目的端口:源端口号32371(7e 73)、目的端口号433(01 bb),各占 2 字节。
  • 序号seq:序号4181209650(f9 38 32 32),占4字节。
  • 确认序号ack:确认序号0(00 00 00 00),占4字节。
  • 头部长度: 80,代表头部是32字节,占一个字节。
  • 标志位:标志位中同步位SYN=1(02),占1个字节。
  • 窗口:接收窗口64240(fa f0),占2个字节。
  • 检验和:检验和b8 e1,占2个字节。
  • 紧急指针:紧急指针0(00 00),占2个字节。
  • 选项:最大报文段长度MSS(Maximum Segment Size):1460(02 04 05 b4)字节,占4个字节。

                  窗口扩大:03 03 08,占3个字节。

                  选择确认:04 02,占2个字节。

网络-TCP协议详解自学笔记(例题、代码、实战)_第15张图片 第二次握手
网络-TCP协议详解自学笔记(例题、代码、实战)_第16张图片 标志位

网络-TCP协议详解自学笔记(例题、代码、实战)_第17张图片 选项
  • 源端口和目的端口:源端口号433(01 bb),目的端口号32371(7e 73),各占 2 字节。
  • 序号seq:序号1900885209(71 4d 34 d9),占4字节。
  • 确认序号ack:确认序号4181209651(f9 38 32 33),占4字节。
  • 头部长度: 80,代表头部是32字节,占一个字节。
  • 标志位:标志位中确认位ACK=1、同步位SYN=1(12),占1个字节。
  • 窗口:接收窗口8192(20 00),占2个字节。
  • 检验和:检验和44 9b,占2个字节。
  • 紧急指针:紧急指针0(00 00),占2个字节。
  • 选项:最大报文段长度MSS(Maximum Segment Size):1440(02 04 05 a0)字节,占4个字节。

                  窗口扩大:03 03 05,占3个字节。

                  选择确认:04 02,占2个字节。

网络-TCP协议详解自学笔记(例题、代码、实战)_第18张图片 第三次握手
网络-TCP协议详解自学笔记(例题、代码、实战)_第19张图片 标志位

  • 源端口和目的端口:源端口号32371(7e 73)、目的端口号433(01 bb),各占 2 字节。
  • 序号seq:序号4181209651(f9 38 32 33),占4字节。
  • 确认序号ack:确认序号1900885210(71 4d 34 da),占4字节。
  • 头部长度: 50,代表头部是20字节,占一个字节。
  • 标志位:标志位中确认位ACK=1(10),占1个字节。
  • 窗口:接收窗口517(02 05),占2个字节。
  • 检验和:检验和a3 52,占2个字节。
  • 紧急指针:紧急指针0(00 00),占2个字节。
  • 选项

之后的数据传输涉及到TLS协议,不再本篇文章探讨内了。

自己实现TCP客户端和服务器端可以查看文章:python-网络编程之socket

参考

《TCP/IP详解卷1》第17-24章

《计算机网络(谢希仁)第七版》5.3-5.9

RFC 5681

RFC 2581

(传输层)TCP协议 - kzangv - 博客园

你可能感兴趣的:(网络安全,网络,TCP)