TCP与UDP挖掘机(一)初识

作者: 进击的阿黑 | 来源:发表于2019-11-16 09:23 被阅读0次

    面试官偶尔会问到TCP相关的知识点,在最早之前我是一脸懵逼答非所问的,故整理了一下关于TCP的相关知识点,希望对大家有所收获!

    为了更进一步了解网络层面的知识,先晒出一张网络体系结构图,加深理解

    计算机网络体系结构图

    all.png

    TCP UDP的区别

    TCP UDP
    面向连接的协议。基于这种连接方式, 通信设备应在传输数据前建立连接,并应在传输数据后关闭连接 面向数据报的协议。意味着打开、维护、终止连接不会有开销。UDP对于广播和多播类型的网络传输是有效的
    点对点通信,连接两端的socket
    面向字节流。TCP把传输的各种数据当做无结构的字节流来用
    可靠性。它能够保证向目标路由器的数据传输 不可靠性。不能保证向目的地传送数据
    错误检测机制。TCP提供了广泛的错误检查机制,这是因为它提供流量控制和数据确认。 UDP只有使用校验和的基本错误检查机制
    数据排序。数据包能够按照顺序到达接收器 没有数据排序。若有需求,则需要再应用程序层进行管理
    速度较慢。相对UDP而言速度较慢。 快、简单、高效
    重传机制。支持重传丢失的数据包 无重传机制
    标头大小为20个字节 标头大小为8个字节
    重量级 轻量级
    用于HTTP,HTTP,FTP,SMTP和Telnet 用于DNS,DHCP,TFTP,SNMP,RIP和VoIP

    注:本文所指的Client 均为发送方,Server为接收方

    TCP 三次握手、四次挥手

    三次握手

    建立一个TCP连接时,需要Client和Server总共发送3个包。

    三次握手的目的是连接服务器指定端口,建立 TCP 连接,并同步连接双方的序列号和确认号,交换 TCP 窗口大小信息。在 socket 编程中,客户端执行 connect() 时。将触发三次握手。

    3-way-handshake_1.jpg
    • Step 1(SYN).

      Client 端要和Server端 建立连接,所以要发一个SYN(即同步序列号)的包,初始序号x,保存在包头的序列号(Sequence Number)字段里,指明打算连接的Service port,。

      用于告知Server:我(Client)可能要与你开始通讯了,现在发给你一个我(Client)启动段的序列号。

      此时Client进入SYN_SEND状态

    • Step 2(SYN+ACK).
      Server 端 接收到数据包(通知)后

      使用一个SYN-ACK信号位设置,来响应Client 端的请求。

      即发送了自己的序列号(SVN),初始序号为y,和确认号(ACK,即Client发来的序列号递增1, 即x + 1)。

      此时Server进入 SYN_RCVD 状态

    • Step 3(ACK).

      Client接收到Server端的响应后

      发送确认包(ACK,即Server 发来的序列号递增1, 即y + 1)来确认收到响应,此时Client 进入 ESTABLISHED 状态,当Server 接收到该ACK包后,也进入 ESTABLISHED 状态。

    四次挥手

    TCP 的连接的拆除需要发送四个包,因此称为四次挥手(Four-way handshake)。

    需要四个包的原因是是因为TCP的半关闭引起的

    客户端或服务器均可主动发起挥手动作,在 socket 编程中,任何一方执行 close() 操作即可产生挥手操作。

    下面假设Client主动发起挥手动作

    4-way-connect-termination.jpg
    • Step 1(FIN).

      Client 端(发起方)要关闭TCP连接,所以要发一个FIN包,序号为x

      发送完毕后,此时Client进入 FIN_WAIT_1 状态(此时表明无数据可发送,但仍可接受数据)

    • Step 2(ACK).

      当Server 端 接收到FIN包后,立即向Client发送确认包(即Client发来的FIN包的序号递增1,x + 1)。

      发送完毕后,此时Server 进入 CLOSE_WAIT 状态(此时表明接收到了Client的关闭,但还没做好“思想准备“关闭连接)

      当Client 端 接收到ACK包后,进入 FIN_WAIT_2 状态

    • Step 3(FIN).

      Server 端 发送ACK包一段时间(这段时间它有一些关闭过程)后,开始发送FIN包,序号为y

      发送完毕后,此时Server 进入 LAST_ACK 状态

    • Step 4(ACK).

      当Client 端 接收到FIN包,即关闭请求后,发送一个确认包(即Server发来的FIN包的序号递增1,y + 1

      发送完毕后,此时Client 进入 TIME_WAIT 状态,目的在于在时间周期n内,允许Client 在发送的ACK包丢失的情况下重新发

      当Server 端接收到ACK包后,连接正式关闭,此时Server进入 CLOSED 状态。Client资源(包括端口号、缓冲区数据)都被释放

      当Client在时间周期n结束后,仍没收到Server 发的ACK包,则认为已正常关闭连接,此时Client 也进入 CLOSE 状态

    TCP协议如何保证可靠传输

    从上面的体系图可以看到,TCP(即运输层)的报文信息最终会交付到网际层。而网际层不会提供可靠的服务。所以还是要TCP来保证可靠的传输,才能最终保证数据服务的可靠

    原理

    ​ 宏观上看,从TCP的特性可以得知,它有自己的错误检测机制、数据按序传输、确认应答+序列号、支持重传的功能。然后具体的内部处理是怎样的呢?主要有以下两点

    • 停止等待协议

      这是最简单的保证可靠传输的协议

      以下会发生两种情况

      • 无差错

        停止等待协助-无差错.jpg

      可以看到Client 在发送分组M1(即数据单元)后,暂停,等到Server发回确认后,继续发送下一个分组...

      这是理想条件下的无差错情况

      • 有差错

        停止等待协助-超时重传.jpg

    当Client 发送 分组M1(会先设置一个计时器,在此计时器内M1仍存在,以便重传)时,可能会遇到数据无法到达Server,或者Server 检测出问题并丢弃了它,在指定时间内Client 如果未收到来自Server 的确认,则会重传M1,即人们常说的超时重传

    超时重传会有以下情况

    • 确认丢失(发回延迟)

      Client发送分组M1,Server收到M1并发送确认分组,而在指定的时间内Client没有收到确认,后会重传M1.

      而由于Server 已经收到过M1了, 所以此时它需要 丢弃M1分组, 发送确认分组

    • 确认迟到(发送延迟)

      由于网络延迟等原因,Client发送的分组M1,在指定时间后才到Server, 此时Client 还没来得及收到确认,再次发送分组M1

      而由于Server 刚好收到了M1,所以此时它需要 丢弃M1分组, 发送确认分组

      Client 收到>=2个以上的确认,会执行丢弃操作,并且停止发送

    • 连续ARQ协议

      由于停止等待协议对信道的利用率太低,故可以采用流水线的方式来传输,即连续ARQ协议。

      这里需要提到一个发送窗口的概念。发送窗口支持滑动,所以也有滑动窗口这么一个概念

      ​ Client 会维护一个发送窗口,一个窗口内可以有多个连续分组进行发送,而不必等待对方的确认一条条分组发。

      ​ Server 亦不会对每个分组进行回传确认,而是在按需发送到达的最后一个分组到达之后,发送确认,代表这个窗口的分组已经发送成功

    具体实现

    1. 使用滑动窗口

    ​ 窗口主要分为接收窗口和发送窗口

    • 接收窗口

      接收窗口.png

    “接收窗口”大小取决于应用(比如说tomcat:8080端口的监听进程)、系统、硬件的限制。图中,接收窗口是31~50,大小为20。

    在接收窗口中,黑色的表示已收到的数据,白色的表示未收到的数据。

    当收到窗口左边的数据,如27,则丢弃,因为这部分已经交付给主机;

    当收到窗口右边的数据,如52,则丢弃,因为还没轮到它;

    当收到已收到的窗口中的数据,如32,丢弃;

    当收到未收到的窗口中的数据,如35,缓存在窗口中。

    • 发送窗口
    发送窗口.png

    ​ 发送窗口的大小swnd=min(rwnd,cwnd)。rwnd是接收窗口,cwnd用于拥塞控制,暂时可以理解swnd= rwnd =20。

    图中分为四个区段,其中P1到P3是发送窗口。

    tips:发送窗口以字节为单位。为了方便画图,图中展示得像以报文为单位一样。但这不影响理解。

    2. 重传与确认

    • 确认

      这里主要是通过累计确认的方式

    • 重传

      这里主要是上面说的超时重传,每一个报文都会有超时计数器,当超过指定时间后,Client(发送方)会触发重传报文

    3. 流量控制(基于滑动窗口)

    ​ 流量即发送方发送的报文流量。当接收方来不及处理数据时,通过滑动窗口,告诉发送方能够接受的单位字节是多少,以降低发送的频率,防止包丢失

    流量控制.png
    • 在建立连接时,接收方(B),告诉了发送方(A):我的接收窗口是400(单位字节).

    • 图中的ACK为TCP首部的ACK字段,ack为首部的确认号字段.

    • 流量控制体现在:rwnd=300, rwnd=100, rwnd=0.在确认报文的窗口字段设定了发送方能够发出的数据多少,从而控制流量.注意只有到首部的ACK字段值为1,窗口字段的值才有效.

    • 假设在B发送了rwnd=0之后,过段时间由于自己又希望接收到数据,于是发出rwnd=400的报文,但是该报文丢失了,这样A依然无法发送数据,B希望接收但接收不到数据.

    ​ 为解决该问题,TCP为每个链接都设有一个持续计时器.只要接收到对方窗口为0的通知,就启动持续计时器.在计时器到期后,就发送探测报文,对方可以在该报文的确认中告知当前的窗口值.若窗口任然为0,那么就重新设定计时器,若不为0,那么上述的问题就解决了.

    4. 拥塞控制

    ​ 拥塞是指对网络某一资源(带宽,缓存等)的需求超过了可提供的部分,从而使网络中传送的数据不能按时到达,网络性能变差的情况.

    拥塞控制就是防止过多的数据注入到网络中,这样网络中的资源压力就小了.

    流量控制和拥塞控制似乎很相似,但是他们不同.前者立足于接收和发送者双方的情况;而后者注重的是数据量对网络环境的影响

    TCP 粘包、拆包

    由于TCP 是一个面向字节流的协议,这也决定了它的数据是无结构的。所以TCP无法得知应用层对于这快数据的定义,而是基于自身缓冲区的实际情况进行数据包的拆分,或者将多个数据包进行合并来发送。

    参考下图,在不同的条件下,会发生多种现象

    • Server 分别接收P1,P2,没有发生粘包、拆包
    • Server 一次接收P1+P2两个报文,发生了粘包
    • Server 先接收P2, 再分别接收了P1_1, P1_2,发生了拆包
    • Server 先接收了P2+P1_2,再接收了P1_1,发生了粘包、拆包
    • 另一种极端情况,当窗口非常小,恰逢P1又很大时,可能会发生多次对P1进行拆包
    粘包与拆包.jpg

    首先我们要知道,发送的数据会先传入发送缓冲区,再通过网络传输发送到接收端的缓冲区

    以上现象发生的原因主要是

    • 发送的字节 大于 TCP发送缓冲区的大小,会发生拆包
    • 发送的报文 大于 MSS(最大报文长度),会发生拆包
    • 发送的字节 小于 TCP发送缓冲区的大小,会将多次写入缓冲区的报文一并发送,即发生粘包

    解决方案,需要上层应用程序做对应的处理

    • 规定报文长度。例如设定每条报文固定长度为200字节,当不够时,用空格填充
    • 报文末尾添加回车换行符。例如FTP协议
    • 将报文分为header and body,在头部中声明报文长度,然后根据这个长度来获取报文

    我们常用的Netty 已经帮我们处理好这些问题,我们仅需调用特定的方法即可。这个在后续的Netty挖掘机系列文章会提到栗子。

    比如有:

    • LineBasedFrameDecoder 基于换行符解决
    • DelimiterBasedFrameDecoder 基于分隔符解决
    • FixedLengthFrameDecoder 指定长度解决

    参考链接:【读】这一次,让我们再深入一点 - TCP协议

    参考链接:什么是 TCP 拆、粘包?如何解决?

    相关文章

      网友评论

        本文标题:TCP与UDP挖掘机(一)初识

        本文链接:https://www.haomeiwen.com/subject/dqneictx.html