Java|TCP的三次握手和四次挥手以及TCP和UDP协议的区别

介绍了TCP协议的三次握手和四次挥手,TCP和UDP协议的基本概念和首部信息,以及TCP字节流和UDP数据报的区别!
前面我们简单的介绍了TCP/IP协议分层模型以及数据的封装和分用。
下面我们简单的学习TCP和UDP协议的相关知识点,包括TCP和UDP基本概念和首部信息,以及TCP协议的三次握手和四次挥手,以及TCP字节流和UDP数据报的区别!

文章目录
  • 1 TCP协议
    • 1.1 单播和全双工
    • 1.2 TCP首部
    • 1.3 TCP三次握手
      • 1.3.1 三次握手的原因
    • 1.4 TCP四次挥手
      • 1.4.1 四次挥手的原因
      • 1.4.2 等待2MSL的原因
    • 1.5 TCP滑动窗口和流量控制
    • 1.6 TCP拥塞控制
  • 2 UDP协议
  • 3 UDP和TCP协议的区别
  • 4 字节流和数据报的区别

1 TCP协议 按层次分,TCP 位于传输层,TCP协议全称是(Transmission Control Protocol)传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。
TCP的特点:
  1. 面向连接:使用TCP通信的双方必须先建立起连接,然后才能开始数据的读写。建立连接后双方的系统内核会为该连接分配必要的系统资源,用来管理连接的状态和传输在连接上的数据。
  2. 可靠的:
    1. TCP采用发送应答机制,即发送端发送的每个TCP报文段都必须得到接收方的应答后才认为TCP报文段传输成功。序号也保证了传送到接收端实体的包的按序接收。
    2. TCP还具有超时重传机制,发送端在发出一个TCP报文段之后启动定时器,若在超时时间范围后还没收到接收端的应答信号,它将重发该数据报文。一个报文段从发送再到接收到确认所经过的时间称为往返时间 RTT,加权平均往返时间 RTTs,超时时间 RTO 应该略大于 RTTs。
    3. TCP报文段最终是以IP数据报发送的,IP数据报到达接收端可能是重复/乱序的。所以TCP发送模块还会为每个包指定一个序号, TCP接收模块对接收到的TCP报文段重排、整理,再交付应用程序。
  3. TCP还提供流量控制和拥塞控制,防止过多的数据注入到网路中,缓解压力。
1.1 单播和全双工 每条TCP传输连接只能有两个端点,只能进行点对点(一对一)的单播数据传输,以基于广播和多播(目标是多个主机地址)的应用程序不能使用TCP服务。
TCP还是一个双向对称的全双工(full-duplex)协议,一个TCP连接存在双向的读写通道,可以使数据在两个方向上同时进行传送操作,在发送数据的同时也能够接收数据,两者同步进行,就像平时打电话一样,说话的同时也能够听到对方的声音。半双工指一个虽然数据可以在一个信号载体的两个方向上传输,但是不能同时传输,时间段内只有一个动作发生。单工就是说数据传输是单向的,通信双方中,一方固定为发送端,一方则固定为接收端。信息只能沿一个方向传输。
【Java|TCP的三次握手和四次挥手以及TCP和UDP协议的区别】全双工TCP通信的每个方向上都有独立的SN,在关闭时,每一个方向都必须单独关闭,这就是四次挥手!
1.2 TCP首部 TCP报文格式图如下(主要是TCP首部信息):
Java|TCP的三次握手和四次挥手以及TCP和UDP协议的区别
文章图片

TCP首部的最小长度为20字节,最大为60字节,主要参数如下:
  1. 16位TCP源端口(Source Port):指明发送方应用程序对应的端口。
  2. 16位TCP目的端口(Destination port):定义传输的目的地,即传给哪个上层协议或应用程序。
  3. 32位TCP序列号(Sequence Number): 简称seq。一次TCP通讯(从TCP连接的建立到断开)整个过程中,一个传输方向上的字节流的每一个报文的编号。例如主机A和主机B进行TCP通讯,A发送给B的第一个TCP报文中,序号值就被系统设置为某个随机值x(ISN, Initial Sequence Number),在该传输方向(A->B)的后续TCP报文的序号将被系统设置为x加上该报文所携带的第一个字节在整个字节流的偏移。假设某个TCP报文段传输的数据是整个字节流中的第1024~2048字节,那么该报文的seq = x+1025,下一个报文的seq = x+2049。
  4. 32位确认序号(Acknowledgment Number):简称ack。用作对另一方发送来的TCP报文段的响应,其值是收到的TCP报文段的seq序号值加1。
    1. 假设主机A和主机B进行TCP通信,那么A发送出的TCP报文段不仅携带自己的序号,而且包含对B发送来的TCP报文段的确认号。反之,B发送出的TCP报文段也同时携带自己的序号和对A发送来的报文段的确认号。若确认号为N,则表明到序号N-1为止的所有数据都已正确收到。
  5. 对于上面说的seq和ack,简单的说,seq是某一端发送的数据包本身的序列号(两端都有自己的序列号),ack则是确认收到的数据包的seq + 1。
  6. 4位首部长度(Header Length):标识该TCP首部有多少个32bit字(4字节)。因为4位最大能标识15,所以TCP首部最长是60字节,最短20字节。
  7. 6位保留(Reserved):为了将来定义新的用途所保留,目前这些位都是0。
  8. 6位标志位(Code Bits)):共6个,即URG、ACK、PSH、RST、SYN、FIN:
    1. URG(Urgent):紧急指针(urgent pointer)是否有效(目前已经很少使用)。
    2. ACK(Acknowledgment):ack确认号是否有效(携带ACK标志的TCP报文段称为确认报文段)。取值1代表ack(Acknowledgment Number)字段有效,这是一个确认的TCP包,取值0则不是确认包。TCP 规定,在连接建立后所有传送的报文段都必须把 ACK 置 1。
    3. PSH(Push):一般是表示发送端缓存中已经没有待发送的数据,提示接收端应用程序应尽快从TCP接收缓冲区读走数据。(若应用程序不读走数据,数据会一直留在TCP模块的接收缓冲区)
    4. RST(Reset):要求对方重新建立连接(携带RST标志的TCP报文段为复位报文段)。通常在发生异常或者错误的时候会触发复位TCP连接。
    5. SYN(Synchronize):请求建立一个新的连接(携带SYN标志的TCP报文段为同步报文段)。在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使SYN=1和ACK=1,因此SYN置为1就表示这是一个连接请求或连接接受报文。因此,该标志仅在三次握手建立TCP连接时的前两次有效。
    6. FIN(Finish):释放一个连接。当FIN=1时,表明发送此报文段的发送端的数据已发送完毕,并要求释放连接。
  9. 16位窗口大小(Window Size):TCP流量控制的一个手段。该值用于告诉对方本端的TCP接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度。
  10. 16位校验和(Checksum):发送端基于数据内容计算一个数值并填充,接收端要与发送端数值结果完全一样,才能证明数据的有效性。接收端checksum校验失败的时候会直接丢掉这个数据包。CheckSum是根据伪头+TCP头+TCP数据三部分进行计算的。这是TCP可靠传输的一个重要保障。
  11. 16位紧急指针(Urgent Pointer):指向后面是优先数据的字节,在URG标志为1时才有效。存放着一个正的偏移值,该值加上当前报文的序号将得到紧急指针,紧急指针处存放的是紧急数据,是发送端向接收端发送紧急数据的方法。
  12. 选项(Option):长度不定,但长度必须以是32bits的整数倍。这部分最多包含40字节,因为TCP首部最长是60字节(其中还包含前面讨论的20字节的固定部分),常见的选项包括、SACK、Timestamp等等。MSS表示最大分段大小,一个TCP包最大可传输的字节数一般是1500-20-20=1460字节。
需要注意的是:
  1. 不要将确认序号ack与标志位中的ACK搞混了。
  2. 确认方ack=发起方seq+1,两端配对。
1.3 TCP三次握手 当一台计算机想要与另一台计算机通讯时,两台计算机之间的通信需要畅通且可靠,这样才能保证正确收发数据。
用 TCP 协议把数据包送出去后,TCP不会对传送后的情况置之不理,它一定会向对方确认是否成功送达。为了准确无误地将数据送达目标处,TCP 协议在发送数据包之前采用了三次握手(three-way handshaking)策略。
所谓三次握手(Three-Way Handshake)即建立TCP连接,就是指建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立。在socket编程中,这一过程由客户端执行connect来触发,整个流程如下图所示:
Java|TCP的三次握手和四次挥手以及TCP和UDP协议的区别
文章图片

  1. 最开始客户端和服务器都是出于closed状态。 tcp服务端进程先创建传输控制块TCB,时刻准备接收客户端的连接请求,此时服务器进入Listen(监听)状态tcp客户进程也是先创建TCB传输控制块,然后主动向服务器发出链接请求报文。
  2. 第一次握手:客户端设置SYN = 1,随机产生一个自己的初始化序列号值seq = x,并将该数据包发送给服务器,客户端进入SYN_SENT状态,等待服务器确认。
  3. 第二次握手:服务器收到数据包后由标志位SYN=1知道客户端请求建立连接,服务器如果确认可以连接,那么发送确认报文,将标志位SYN和ACK都置为1,ack = x+1,产生一个自己的初始化序列号值seq = k,并将该数据包发送给客户端以确认连接请求,服务器进入SYN_RCVD状态。
  4. 第三次握手:客户端收到确认后,检查ack是否为x+1,ACK是否为1,如果正确则将标志位ACK置为1,ack = y+1,seq=x+1,并将该数据包发送给服务器,服务器检查ack是否为y+1,ACK是否为1,如果正确则TCP连接建立成功,客户端和服务器进入ESTABLISHED状态,完成三次握手,随后客户端与服务器之间可以开始正式传输数据了。
  5. 若在握手过程中某个阶段莫名中断, TCP 协议会再次以相同的顺序发送相同的数据包。
三次握手阶段的一些参数变化:
Java|TCP的三次握手和四次挥手以及TCP和UDP协议的区别
文章图片

简单的说,握手过程中主要使用了 TCP 的标志(flag)——SYN(synchronize) 和ACK(Acknowledgement)。发送端首先发送一个带 SYN 标志的数据包给对方。接收端收到后,回传一个带有 SYN和ACK 标志的数据包以示传达确认信息。最后,发送端再回传一个带 ACK 标志的数据包,代表“握手”结束。若在握手过程中某个阶段莫名中断,TCP 协议会再次以相同的顺序发送相同的数据包。
1.3.1 三次握手的原因
第三次握手是为了防止失效的连接请求到达服务器,让服务器错误打开多个无效连接。
客户端发送的连接请求如果在网络中滞留,那么就会隔很长一段时间才能收到服务器端发回的连接确认。客户端等待一个超时重传时间之后,就会重新请求连接。但是这个滞留的连接请求最后还是会到达服务器。
如果不进行三次握手,而是两次就可以了,那么在服务器收到并返回响应之后服务器就会打开连接,此时服务器的响应可能并没有即时送达客户端,导致客户端重发连接请求,导致服务器端收到两个连接。假设每次发送的数据一直在丢失,客户端一直SYN,服务器就会产生多个无效连接,占用资源,这个时候服务器可能会挂掉。这个现象就是我们听过的“SYN的洪水攻击”。
如果有第三次握手,客户端会忽略服务器之后发送的对滞留连接请求的连接确认,不进行第三次握手,因此就不会再次打开连接。
1.4 TCP四次挥手 所谓四次挥手(Four-Way Wavehand)即终止TCP连接,就是指断开一个TCP连接,当一方完成它的数据发送任务后就能通过发送一个FIN来终止这个方向的连接,但是由于全双工机制,收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。TCP全双工的每个方向都必须单独进行关闭,因此需要客户端和服务端总共发送4个包以确认连接的断开。
在socket编程中,这一过程由客户端或服务端任一方执行close来触发,首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭,整个流程如下图所示,以客户端主动关闭为例:
Java|TCP的三次握手和四次挥手以及TCP和UDP协议的区别
文章图片

  1. 第一次挥手:客户端首先发送一个FIN =1 ,seq = u(等于前面客户端已经传送的数据的最后一个字节的序号加1(TCP规定,FIN报文段即使不携带数据,也要消耗一个seq序号),用来表示关闭客户端到服务器的数据传送,客户端进入FIN_WAIT_1状态。
  2. 第二次挥手:服务器收到FIN后,发送一个ACK = 1给客户端,ack = u+1,seq = V(等于前面服务器已经传送的数据的最后一个字节的序号加1),此后服务器进入CLOSE_WAIT状态。客户端收到之后则进入FIN-WAIT-2状态,等待服务器进一步发送连接释放报文(在这之间还是需要接并处理受服务器发送的最后的数据)
  3. 第三次挥手:服务器发送一个FIN = 1,seq = w(在第二次和第三次挥手过程中服务器可能又发送了一些数据,因此假定为N),ack = u+1(TCP规定,ACK报文段可以携带数据,但是如果不携带数据则不消耗序号),ACK = 1,用来关闭服务器到客户端的数据传送,服务器进入LAST_ACK状态,等待客户端最后确认。
  4. 第四次挥手:客户端收到FIN后,接着发送一个ACK = 1给服务器,ack = w+1,seq = u+1,客户端进入TIME_WAIT状态,服务器在收到确认之后进入CLOSED状态,完成四次挥手。
  5. 客户端的TIME_WAIT状态会持续 2*MSL(Maximum Segment Lifetime,最大段生存期,指报文段在网络中生存的时间,超时会被抛弃)时间,若该时间段内没有服务器的重发请求的话,客户端就进入 CLOSED 状态。
1.4.1 四次挥手的原因
关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了,但是还能接收数据,而己方也未必将全部数据都发送给对方了,所以己方收到这个FIN报文之后处于CLOSE-WAIT状态,此时还可以发送一些数据给对方,发送完毕之后再发送FIN报文给对方来表示同意现在关闭连接,这就是前三次回挥手。
而最后还需要客户端一次确认,也是因为需要保证客户端收到了在第二、三次挥手间传递的数据,以及表示收到服务器的FIN请求,完成挥手。
1.4.2 等待2MSL的原因
客户端接收到服务器端的 FIN 报文并发送确认报文后进入TIME_WAIT状态,此时并不是直接进入 CLOSED 状态,还需要等待一个时间计时器设置的时间 2MSL。MSL,即Maximum Segment Lifetime,最大段生存期,是指报文段在网络中生存的最大时间,超时会被抛弃。
为什么客户端最后要等待2*MSL时间呢?
  1. 保证客户端发送的最后一个ACK确认报文能够到达服务器,因为这个ACK报文可能丢失,站在服务器的角度看来,我已经发送了FIN报文请求断开了,客户端还没有给我回应,应该是我发送的请求断开报文它没有收到,于是服务器又会重新发送一次,而客户端就能在这个2MSL时间段内收到这个重传的报文,接着给出回应报文,并且会重启2MSL计时器。
  2. 等待2MSL是为了让本连接持续时间内所产生的所有报文都从网络中消失,使得下一个新的连接不会出现旧连接的请求报文,防止数据包发生混淆。防止类似与“三次握手”中提到了的“已经失效的连接请求报文段”出现在本连接中。
1.5 TCP滑动窗口和流量控制 窗口是缓存的一部分,用来暂时存放字节流。发送方和接收方各有一个窗口,接收方通过 TCP 报文段中的窗口字段告诉发送方自己的窗口大小,发送方根据这个值和其它信息设置自己的窗口大小。
发送窗口内的字节都允许被发送,接收窗口内的字节都允许被接收。如果发送窗口左部的字节已经发送并且收到了确认,那么就将发送窗口向右滑动一定距离,直到左部第一个字节不是已发送并且已确认的状态;接收窗口的滑动类似,接收窗口左部字节已经发送确认并交付主机,就向右滑动接收窗口。
接收窗口只会对窗口内最后一个按序到达的字节进行确认,例如接收窗口已经收到的字节为 {31, 34, 35},其中 {31} 按序到达,而 {34, 35} 就不是,因此只对字节 31 进行确认。发送方得到一个字节的确认之后,就知道这个字节之前的所有字节都已经被接收。
Java|TCP的三次握手和四次挥手以及TCP和UDP协议的区别
文章图片

TCP 利用滑动窗口实现流量控制。流量控制是为了控制发送方发送速率,保证接收方来得及接收。
接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能再发送数据。
1.6 TCP拥塞控制 在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏。这种情况就叫拥塞。如果网络出现拥塞,分组将会丢失,此时发送方会继续重传,从而导致网络拥塞程度更高。
因此当出现拥塞时,应当控制发送方的速率。这一点和流量控制很像,但是出发点不同。流量控制是为了让接收方能来得及接收,而拥塞控制是为了降低整个网络的拥塞程度。
拥塞控制就是为了防止过多的数据注入到网络中,这样就可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机,所有的路由器,以及与降低网络传输性能有关的所有因素。相反,流量控制往往是点对点通信量的控制,是个端到端的问题。流量控制所要做到的就是抑制发送端发送数据的速率,以便使接收端来得及接收。
为了进行拥塞控制,TCP 发送方要维持一个 拥塞窗口(cwnd) 的状态变量。拥塞控制窗口的大小取决于网络的拥塞程度,并且动态变化。发送方让自己的发送窗口取为拥塞窗口和接收方的接受窗口中较小的一个。
TCP 主要通过四个算法来进行拥塞控制:慢开始、拥塞避免、快重传、快恢复。
  1. 慢开始: 慢开始算法的思路是当主机开始发送数据时,如果立即把大量数据字节注入到网络,那么可能会引起网络阻塞,因为现在还不知道网络的符合情况。经验表明,较好的方法是先探测一下,即由小到大逐渐增大发送窗口,也就是由小到大逐渐增大拥塞窗口数值。cwnd 初始值为 1,每经过一个传播轮次,cwnd 加倍。
  2. 拥塞避免: 拥塞避免算法的思路是让拥塞窗口 cwnd 缓慢增大,即每经过一个往返时间 RTT 就把发送放的 cwnd 加 1.
  3. 快重传与快恢复: 在 TCP/IP 中,快速重传和恢复(fast retransmit and recovery,FRR)是一种拥塞控制算法,它能快速恢复丢失的数据包。没有 FRR,如果数据包丢失了,TCP 将会使用定时器来要求传输暂停。在暂停的这段时间内,没有新的或复制的数据包被发送。有了 FRR,如果接收机接收到一个不按顺序的数据段,它会立即给发送机发送一个重复确认。如果发送机接收到三个重复确认,它会假定确认件指出的数据段丢失了,并立即重传这些丢失的数据段。有了 FRR,就不会因为重传时要求的暂停被耽误。当有单独的数据包丢失时,快速重传和恢复(FRR)能最有效地工作。当有多个数据信息包在某一段很短的时间内丢失时,它则不能很有效地工作。
2 UDP协议 UDP(User Datagram Protocol)协议全称是用户数据报协议,是无连接协议,也称透明协议,也位于传输层。常见的对讲机、视频会议、VNC等许多都是UDP协议传输,对数据准确性要求高,速度可以相对较慢的,可以选用TCP协议。
  1. 面向无连接: 不需要和TCP一样在发送数据前进行三次握手建立连接的,想发数据就可以开始发送了。并且也只是数据报文的搬运工,不会对数据报文进行任何拆分和拼接操作。
    1. 在发送端,应用层将数据传递给传输层的 UDP 协议,UDP 只会给数据增加一个 UDP 头标识下是 UDP 协议,然后就传递给网络层了。
    2. 在接收端,网络层将数据传递给传输层,UDP 只去除 IP 报文头就传递给应用层,不会任何拼接操作。
  2. UDP 支持一对一、一对多、多对多、多对一的传输方式,也就是说 UDP 提供了单播、多播、广播的功能。
  3. 面向报文的: UDP对应用程序提交下来的报文,既不合并,也不拆分,而是保留这些报文的边界,将其直接封装成一个UDP数据报。因此,应用程序必须选择合适大小的报文。
  4. 不可靠: 由于面向无连接,收到什么数据就传递什么数据,发送数据也不会关心对方是否已经正确接收到数据。
  5. 发送端和接收端都没有缓冲区,接收端必须及时针对每一个发送的UDP数据报执行等次数的读操作,否则会造成丢包。UDP 因为没有拥塞控制,一直会以恒定的速度发送数据。即使网络条件不好,也不会对发送速率进行调整。
  6. UDP首部数据比较小,只有八字节,相比TCP首部数据(最小20直接,最大60直接),少了十几个、二十几个直接,在传输数据报文时是很高效的。
UDP首部数据如下:
Java|TCP的三次握手和四次挥手以及TCP和UDP协议的区别
文章图片

3 UDP和TCP协议的区别
UDP TCP
是否连接 无连接 面向连接
是否可靠 不可靠传输,不使用流量控制和拥塞控制 可靠传输,拥有重传机制、流量控制、拥塞控制,报文拆分和顺序合并等手段
连接对象个数 支持一对一,一对多,多对一和多对多交互通信 只能是一对一通信
传输方式 面向报文,一次发送必须一次接收,可能导致报文丢失,不可靠 面向字节流,拥有缓冲区,发送和接收次数不需要相同,可靠
首部开销 首部开销小,仅8字节 首部最小20字节,最大60字节
适用场景 适用于实时应用(IP电话、视频会议、直播等) 适用于要求可靠传输的应用,例如文件传输
4 字节流和数据报的区别 最简单的解释,TCP的字节流和UDP的数据报,最主要的区别是通信双方是否必须执行相同次数的读、写操作。
TCP实际上也是传输的报文断,这里的“字节流”方式指的是仅把传输中的报文看作是一个字节序列,在字节流服务中,用户进程在某一时刻可以读或写任意数量的字节。由于没有报文边界,发送端执行的写操作次数和接收端执行的读操作次数之间没有任何数量关系,可以类比“水流”,等流动的没有边界的事务,这就是字节流的由来!
因为TCP是面向连接的,并且只能是单播连接,也就是说,在连接持续的过程中,socket中收到的数据都是由同一台主机发出的,因此,知道保证数据是有序的到达就行了,至于读、写数据的次数,每次读写多少数据可以自己看着办。
另外,TCP有一个发送缓冲区和接收缓冲区,当应用程序传送的数据块太长,TCP就可以把它划分短一些再传送,如果应用程序一次只发送一个字节,TCP也可以等待积累有足够多的字节后再构成报文段发送出去。比如写100个字节数据时,可以调用一次write写100个字节,也可以调用100次write,每次写一个字节;读100个字节数据时,也完全不需要考虑写的时候是怎么写的,既可以一次read 100个字节,也可以一次read一个字节,重复100次。
UDP的数据报是网络传输的数据的基本单元,包含一个报头和数据本身,其中报头描述了数据的目的地以及和其它数据之间的关系。在接收的时候,每次最多只能读取一个报文,报文和报文是不会合并的,如果缓冲区小于报文长度,则多出的部分会被丢弃。也就是说发送端调用了几次write,接收端必须用相同次数的read读完。
因为UDP是面向无连接的协议,并且支持广播和多播模式也就是说,只要知道接收端的IP和端口,且网络是可达的,任何主机都可以向接收端发送数据,接收端可以接收来自不同主机的消息。这时候,如果一次能读取超过一个报文的数据,则可能会读取到两个不同来源的混合消息,这样的数据是错误且没有意义的。
参考:
  1. 《图解HTTP》
  2. 《计算机网络》
  3. 计算机网络
如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!

    推荐阅读