OSI的”实现“:TCP/IP

TCP/IP四层概念模型是目前较为常用的一种网络模型,相较于OSI七层网络模型,只是对比OSI将七层减少为四层。

image-20210328105034916

TCP/IP

先自上而下,后自下而上处理数据头部

img

网络传输过程

数据在网络传输的过程中,本质是一个封装和解封装的过程。例如:用户使用浏览器向百度服务器发送请求过程中,就是用户的数据从应用层一路封装到物理层,服务器得到物理层的比特流后,一路解封装得到对应数据。同理,服务器响应给用户的数据,也是服务器先一路封装,然后用户再一路解封。

image-20210328105214204

定义

  • TCP/IP 是供已连接因特网的计算机进行通信的通信协议。
  • TCP/IP 指传输控制协议/网际协议 (Transmission Control Protocol / Internet Protocol)。
  • TCP/IP 定义了电子设备(比如计算机)如何连入因特网,以及数据如何在它们之间传输的标准

TCP协议概述

TCP协议:传输控制协议,就是对数据的传输进行一定的控制。关于TCP数据报文格式如图所示,关于TCP头部信息详细数据可以参考链接:(传输层)TCP协议。

TCP三次握手

传输控制协议TCP简介

  • 面向连接的、可靠的、基于字节流的传输层通信协议
  • 将应用层的数据流分割成版文段并发送给目标节点的TCP层
  • 数据包都有序号,对方收到则发送ACK确认,未收到则重传
  • 使用校验和来检验数据在传输过程中是否有误

TCP报文头

img

各个Field说明:

  • 源端口(Source Port):长度为16 bits(2个字节)。源端口。
  • 目的端口(Destination Port):长度为16 bits(2个字节)。目的端口。
  • 序列号(Sequence Number):长度为32 bits(4个字节)。指定了当前数据分片中分配给第一字节数据的序列号。在TCP传输流中每一个字节为一个序号。如果TCP报文中flags标志位为SYN,该序列号表示初始化序列号(ISN),此时第一个数据应该是从序列号ISN+1开始。
  • **确认序列号(Acknowledgment Number)**:长度为32bits(4个字节)。表示TCP发送者期望接受下一个数据分片的序列号。该序号在TCP分片中Flags标志位为ACK时生效。序列号分片的方向和流的方向同方向,而确认序列号分片方向和流方向反方向。
  • 数据偏移或首部长度(Data Offset/Header Length): 长度为4bits。数据偏移也叫首部长度。因为首部长度实际也说明了数据区在分片中的起始偏移值。它表示TCP头包含了多少个32-bit的words。因为4bits在十进制中能表示的最大值为15,32bits表示4个字节,那么Data Offset的最大可表示15*4=60个字节。所以TCP报头长度最大为60字节。如果options fields为0的话,报文头长度为20个字节。
  • 预留字段(Reserved field):长度为6bits。值全为零。预留给以后使用。
  • 标志位(Flags): 长度为6bits。表示TCP包特定的连接状态。一个标签位占一个bit,从低位到高位值依次为:FIN,SYN,RST,PSH,ACK,URG。新定义的TCP头还扩展了ECE,CWR,NS.
  • **窗口(Window)**: 长度16bits(2个字节)。表示滑动窗口的大小,用来告诉发送端接收端的buffer space的大小。接收端buffer大小用来控制发送端的发送数据数率,从而达到流量控制。最大值为65535.
  • 校验和(Checksum):长度16bits(2个字节)。用来检查TCP头在传输中是否被修改。
  • 紧急指针(Urgent pointer):长度为16bits(2个字节)。表示TCP片中第一个紧急数据字节的指针。只有当URG标志置1时紧急指针才有效。
  • 选项和填充(Option和pading):可变长度。表示TCP可选选项以及填充位。当选项不足32bits时,填充字段加入额外的0填充。
  • 数据(Data):长度可变。用来存储上层协议的数据信息。可以为空。比如在连接建立和连接中止时。

TCP Flags

  • URG:紧急指针是否有效。为1,表示某一位需要被优先处理
  • ACK:确认号是否有效,一般置为1
  • PSH:push标志,提示接收端应用程序立即从TCP缓冲区把数据读走
  • RST:重置连接标志,对方要求重新建立连接,复位
  • SYN:请求建立连接,并在其序列号的字段进行序列号的初始值设定。建立连接,设置为1
  • FIN:finish标志,用于释放连接

”握手“是为了建立连接,TCP三次握手的流程图

image-20210312172351191

在TCP/IP协议中,TCP协提供可靠的连接服务,采用三次握手建立一个连接:

  • 第一次握手:客户端将TCP报文标志位SYN置为1,随机产生一个序号值seq=x,保存在TCP首部的序列号(Sequence Number)字段里,指明客户端打算连接的服务器的端口,并将该数据包发送给服务器端,发送完毕后,客户端进入SYN_SENT状态,等待服务器端确认。
  • 第二次握手:服务器端收到数据包后由标志位SYN=1知道客户端请求建立连接,服务器端将TCP报文标志位SYN和ACK都置为1,ack=x+1,随机产生一个序号值seq=y,并将该数据包发送给客户端以确认连接请求,服务器端进入SYN_RCVD状态。
  • 第三次握手:客户端收到确认后,检查ack是否为x+1,ACK是否为1,如果正确则将标志位ACK置为1(此时不用SYN),Acknowledgment Number头部确认序号 ack=y+1,并将该数据包发送给服务器端,服务器端检查ack是否为y+1,ACK是否为1,如果正确则连接建立成功,客户端和服务器端进入ESTABLISHED状态,完成三次握手,随后客户端与服务器端之间可以开始传输数据了。

注意:我们上面写的ack和ACK,不是同一个概念:

  • ack:小写的ack代表的是头部的确认号Acknowledge number,缩写ack,是对上一个包的序号进行确认的号,ack=seq+1。
  • ACK:大写的ACK,则是我们上面说的TCP首部的标志位,用于标志的TCP包是否对上一个包进行了确认操作,如果确认了,则把ACK标志位设置成1。

现实例子理解TCP三次握手

image-20210328112015955

TCP三次握手:即为TCP连接的建立。如果将TCP三次握手类比为打电话,可以如图所示。

image-20210328111923078

为什么需要三次握手才能建立起连接?

  1. 为了初始化 TCP首部的序列号(Sequence Number)的初始值。
  2. 保证C/S两端都能正常接收发送数据。

在只有两次”握手”的情形下,假设Client想跟Server建立连接,但是却因为中途连接请求的数据报丢失了,故Client端不得不重新发送一遍;这个时候Server端仅收到一个连接请求,因此可以正常的建立连接。但是,有时候Client端重新发送请求不是因为数据报丢失了,而是有可能数据传输过程因为网络并发量很大在某结点被阻塞了,这种情形下Server端将先后收到2次请求,并持续等待两个Client请求向他发送数据…问题就在这里,Cient端实际上只有一次请求,而Server端却有2个响应,极端的情况可能由于Client端多次重新发送请求数据而导致Server端最后建立了N多个响应在等待,因而造成极大的资源浪费!所以,”三次握手”很有必要!

首次握手的隐患——SYN超时

问题起因分析

  • Server收到Client的SYN,回复SYN-ACK的时候未收到ACK确认
  • Server不断重试直至超时,Linux默认等待63秒才断开连接

针对SYN Flood的防护措施

  • SYN队列满后,通过tcp_syncookies参数回发SYN Cookie
  • 若为正常连接则Client会回发SYN Cookie,直接建立连接

建立连接后,Client出现故障怎么办

保活机制

  • 向对方发送保活探测报文,如果未收到响应则继续发送
  • 尝试次数达到保活探测数仍未收到响应则中断连接

TCP的四次挥手

所谓四次挥手(Four-Way Wavehand)即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发。

“挥手”是为了终止连接,TCP四次挥手的流程图

image-20210312172322929

  • 其中MSL:最大报文存活时间。确保有足够的时间让接收方收到ACK包。
  • 由于TCP连接是全双工的,因此,每个方向都必须要单独进行关闭。这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭,上图描述的即是如此。

TCP采用四次挥手来释放连接:

  • 第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态;
  • 第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态;
  • 第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态;
  • 第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。

上面是一方主动关闭,另一方被动关闭的情况,实际中还会出现同时发起主动关闭的情况,具体流程如下图:

image-20210313113229770

现实例子理解TCP四次挥手

TCP四次握手:即为TCP连接的释放。如果将TCP四次挥手类比为学生和老师之间的对话,如图所示。

image-20210328112459603

为什么会有TIME_WAIT状态和四次挥手(等待2MSL)

原因:

  • 确保有足够的时间让对方收到ACK包
  • 避免新旧连接混淆

为什么需要四次挥手才能断开连接

因为全双工,发送方和接收方都需要FIN报文和ACK报文

假如现在你是客户端你想断开跟Server的所有连接该怎么做?第一步,你自己先停止向Server端发送数据,并等待Server的回复。但事情还没有完,虽然你自身不往Server发送数据了,但是因为你们之前已经建立好平等的连接了,所以此时他也有主动权向你发送数据;故Server端还得终止主动向你发送数据,并等待你的确认。其实,说白了就是保证双方的一个合约的完整执行!

服务出现大量CLOSE_WAIT状态原因

对方关闭socket连接,我方忙于读或写,没有及时关闭连接

  • 检查代码,特别是释放资源的代码
  • 检查配置,特别是处理请求的线程配置

Linux使用命令查看状态及数量

1
[root@lvjing lvjing]# netstat -n | awk '/^tcp/{++S[$NF]}END{for(a in S) print a,S[a]}'

TCP如何保障传输的可靠性?

TCP保障传输可靠性主要方式有:

  1. 校验和
  2. 序列号
  3. 确认应答
  4. 超时重传
  5. 连接管理
  6. 流量控制
  7. 拥塞控制

区分HTTP TCP/IP UDP

  • TCP/IP是互联网相关的各类协议族的总称,如TCP,UDP,IP,FTP,HTTP等都属于TCP/IP族内的协议。
  • TPC/IP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据。
  • HTTP(HyperText Transfer Protocal 超文本传输协议)是利用TCP在两台电脑(通常是Web服务器和客户端)之间传输信息的协议。客户端使用Web浏览器发起HTTP请求给Web服务器,Web服务器发送被请求的信息给客户端。
  • TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
    • 建立TCP连接(三次握手)
    • 终止TCP连接(四次挥手)
  • UDP (User Datagram Protocol)协议,即用户数据报协议。
    • 是OSI(Open System Interconnection,开放式系统互联)参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。UDP与TCP协议一样用于处理数据包,但UDP是一种无连接的协议,不保证可靠性。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。UDP 用来支持那些需要在计算机之间传输数据的网络应用。

TCP协议与UDP协议对比

根据是否连接传输可靠性应用场景速度这几个特点,TCP协议和UDP协议有以下几点不同。

image-20210328112713413

常见的应用层协议中,基于TCP协议的有:HTTP、HTTPS、FTP基于UDP协议的有:DNS(域名解析协议)等。

UDP

UDP用户数据报协议,是面向无连接的通讯协议,UDP数据包括目的端口号和源端口号信息,由于通讯不需要连接,所以可以实现广播发送。

UDP协议概述

UDP(User Datagram Protocol):用户数据报协议。是一种无连接传输层协议不会创建连接,所以是一种不可靠的协议。它的优点为速度快,缺点为会丢包、会出错。关于UDP数据报文的格式,可以参考博客:UDP协议的详细解析

由于UDP协议的特点,所以UPD协议的使用场景如下:

  • 不需要可靠机制,只需要速度快。
  • 流媒体、多媒体游戏、IP电话
  • 资源消耗小

UDP报文结构

image-20210312171000959

UDP特点

  • 面向非连接
  • 不维护连接状态,支持同时向多个客户端传输相同的消息
  • 数据包报头只有8个字节,额外开销较小
  • 吞吐量只受限于数据生成速率、传输速率以及机器性能
  • 尽量大努力交付,不保证可靠交付,不需要维持复杂的链接状态表
  • 面向报文,不对应用程序提交的报文信息进行拆分或者合并

TCP和UDP的区别

  • 连接:TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的(不需要三次握手四次挥手),即发送数据之前不需要建立连接。
  • 可靠:TCP提供可靠的服务。UDP尽最大努力交付,即不保证可靠交付。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达,TCP通过校验和,重传控制,序号标识,滑动窗口、确认应答等机制实现可靠传输。如丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。
  • 效率:UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。游戏、广播、在线视频。
  • 一对多:每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信。
  • 资源:TCP对系统资源要求较多,UDP对系统资源要求较少。

TCP滑动窗口

  • TCP通过在发送数据时设置一个重传定时器来监控数据的丢失状态,如果重传定时器溢出时还没收到确认信号ACK,则重传该数据。这就是建立重传机制的原因。

  • 下面解释两个名词:

    • RTT(Round Trip Time):一个连接的往返时间,即数据发送时刻到接收到确认的时刻的差值;发送一个数据包到收到对应的ACK,所花费的时间
    • RTO(Retransmission Time Out):重传超时时间,即从数据发送时刻算起,超过这个时间便执行重传;重传时间间隔
    • RTT和RTO 的关系是:由于网络波动的不确定性,每个RTT都是动态变化的,所以RTO也应随着RTT动态变化。

    意义

  • TCP可靠性的保证之一就是流量控制(Flow Control)

  • TCP需要知道现在网络的数据处理速度,才能更好防止丢包,而流量控制就是为了测量现在的网络数据处理速度的。

  • TCP报头有一个字段:窗口,该字段是接收端告知发送端自己的缓冲空间,防止发送端发送太快缓冲区溢出。

TCP使用滑动窗口做流量控制与乱序重排

  • 保证TCP的可靠性
  • 保证TCP的流控特性

窗口数据的计算过程

image-20210312172235870

  • AdvertisedWindow=MaxRcvBuffer - (LastByteRcvd - LastByteRead)
  • EffectiveWindow=AdvertisedWindow - (LastByteSent - LastByteAcked)
  • 其实类似java的NIO中的ByteBuffer。
  • 发送端:
    • LastByteWritten:上层应用可写入的位置
    • LastByteSent:正在发送的位置
    • LastByteAcked:已经收到ACK的位置
    • LastByteAcked ~ LastByteSent区间:表示已经发送但是未收到ACK的数据
    • LastByteSent ~ LastByteWritten区间:表示未发送出去的数据
  • 接收端:
    • LastByteRead:TCP缓冲区中读到的位置
    • NextByteExpected:收到的连续包的最后一个位置
    • LastByteRcved:收到的包的最后一个位置
    • NextByteExpected ~ LastByteRcved区间:未到达的数据区间
    • LastByteRead ~ NextByteExpected区间:已收到的数据区间
  • 接收端回复 剩余可发放数据大小AdvertisedWindow :
    • AdvertisedWindow = MaxRcvBuffer(最大接受数量) – (LastByteRcvd - LastByteRead)接收方把 AdvertisedWindow 告知发送方,发送方 LastByteSent - LastByteAcked 不能大于 AdvertisedWindow 接收方还能接收的量。
  • 发送方窗口 EffectiveWindow 发送方窗口内剩余可发送的大小
    • EffectiveWindow = AdvertisedWindow - (LastByteSent - LastByteAcked) 保证接收方可以处理数据LastByteSent - LastByteAcked 发送方可以发送的数据减去已经确认好可发送的数据就是发送方将要发送的数据,这个数据不能大于接收方还能够接收的数据量。
  • 发送方会根据窗口来控制发送数据的大小,以保证接收方可以处理。
  • 发送方窗口内剩余可发送的大小 EffectiveWindow = AdvertisedWindow - (LastByteSent - LastByteAcked) 保证接收方可以处理数据

滑动窗口基本原理

滑动窗口:已经发送还未ACK确认的,还没发送但是缓冲区有空间还能发送的。

TCP会话的发送方

image-20210313141119395

  • #1:已收到ACK确认的数据
  • #2:已发送到未收到ACK确认的数据
  • #3:在窗口中未发出的(接收方还有空间)
  • #4:窗口以外的数据(接收方没有空间)
  • 其中#2 + #3的黑框就是滑动窗口

发送方滑动后的滑动窗口示意图:

image-20210313142115515

  • 红色是已经新收到的ACK的数据
  • 绿色是新加入滑动窗口的数据

TCP会话的接收方

image-20210313141958113