计算机网络:TCP与UDP

本文详细介绍了TCP/IP分层模型,重点解析了TCP的三次握手和四次挥手过程,探讨了为何需要第三次握手的原因。此外,还讲解了TCP的可靠性实现,包括序列号、确认应答、滑动窗口、流量控制和拥塞控制。同时,对比介绍了UDP的无连接、不可靠特性及其应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

TCP/IP分层模型

应用层HTTP、SMTP、FTP、SSH、DNS、HTML向用户提供应用服务时的通信活动
传输层TCP、UDP在该层确定数据包应该转发给哪一个应用,也就是将数据包发送到对应的端口号上。
网络层IP、ARP、ICMP在该层确定数据包应该转发给网络上的哪一个主机,也就是将数据包发送到IP地址所对应的主机上。
数据链路层

TCP介绍

    TCP是一种面向连接的、可靠的传输层协议。
    面向连接是指,当两个应用程序使用TCP协议进行数据传输时,需要通过“三次握手”建立一条双方专有的、虚拟的通信线路,
在这条线路上进行数据传输,当双方要停止数据交互时,需用用“四次挥手”的方式来断开连接。
    可靠是指TCP协议中包含了数据传输时的丢包控制,以及对乱序的数据包进行顺序控制等功能。

TCP三次握手

在介绍TCP三次握手之前,先来看看TCP首部的格式。如下图:
在这里插入图片描述
  源端口号:记录了发送该数据包的端口
  目标端口号:记录了该数据包的目的端口
  序列号:每发送一次数据,就对该部分进行一次累加,序列号的初值是在建立连接时由计算机随机生成的。
  确认应答号:表示下一次应该收到的数据的序列号。
  接下来主要看一下TCP首部中的控制位部分,如下图所示:
在这里插入图片描述
  只需要知道ACK、SYN、FIN等控制位的作用,就大致能弄明白三次握手和四次挥手了。
  ACK:如果该位为1,确认应答的字段变为有效。
  SYN:用于建立连接。SYN为1表示希望能够建立连接。
  FIN:该位为1,表示之后不会再有数据进行交互,希望断开连接。
  
  TCP的三次握手流程如下图所示(图片来源
在这里插入图片描述
  图中SYN以及大写的ACK是控制位,seq就是TCP首部中的序列号,小写的ack是TCP首部中的确认应答号。

  1. 首先客户端会向服务器发送一个请求,这个请求数据包中,SYN控制位被置为1,表示客户端现在希望与服务器建立TCP连接,此时客户端会随机生成一个x作为seq发送给服务端。
  2. 当服务器监听到客户端发来的建立连接的请求后,服务端会向客户端发送一个数据包,其中SYN=1,表示服务端也想建立链接,ACK=1表示这是对刚刚客户端发来请求的一个应答,此时服务器也会随机生成一个y作为自己的序列号发送给客户端,并将ack=x+1,表示我(服务器)想接收到的客户端下一个请求的序列号是x+1。
  3. 最后,客户端会再给服务端一个回应,ACK=1,这一次客户端要发送给服务器的数据包中的seq=x+1,也就对应了步骤2中服务端希望的下一个请求序列号,ack=y+1,表示我(客户端)想接收到的下一个服务端所发来的数据的序列号是y+1。至此,TCP连接已经建立,客户端和服务器可以在连接上进行数据交互。

为什么需要第三次握手

可以看到,在前两次握手中,客户端和服务器都通过将SYN=1来表达了自己想要建立连接的想法。那么第三次握手存在的意义是什么呢?

原因如下:
在网络传输的过程中,存在着很多的不确定因素,就算源地址和目的地址是相同的,每次数据传输的延时都会有所不同。
现在想想如果进行两次握手就建立连接可能会出现什么情况

  1. 步骤1还是正正常常,客户端向服务器发送想建立连接的请求。
  2. 服务器收到请求,向客户端发出一个“我也想建立请求的回应”。
  3. 这时,网络中出现了一些错误,导致步骤2中服务器对客户端的回应久久没有到达客户端。然而如果是两次握手,这时服务端就会进入连接已建立的状态,对客户端保持监听。
  4. 客户端久久没有收到服务器的回应,于是客户端并不会进入连接已建立的状态,从而不会给服务器发送任何数据,不会与服务器进行交流,更不可能给服务器发送关闭连接的请求。
  5. 服务器不会收到客户端任何数据,但又不会收到客户端的关闭连接的请求,它会一直保持对该客户端的监听,然而它并不会得到任何数据,这就导致了资源的严重浪费。
  6. 因此,需要有第三次握手,来防止上述情况的发生,在第三次握手后,服务器才进入连接已建立的状态。

TCP的四次挥手

流程图如下所示(图片来自该博客):
在这里插入图片描述

  1. 客户端和服务器都处于连接已建立状态,客户端首先向服务器发送一个请求,将FIN=1,表示客户端想要中断连接,seq=u,是这个数据的序列号(注:这个序列号就不是随机生成的了,是由连接建立之初的那个序列号慢慢自增得到的),按我的理解,这里ack也应该是有值的。
  2. 服务器收到客户端的请求后,会给客户端一个响应,但这一次响应,服务器并不会表达自己想要断开连接,why???因为这时,服务端可能会有一些正在传输的数据没有传输完毕,因此在这次响应之后,服务器会继续把没传完的数据传输完毕。这也就解释了,为什么这次回应没有FIN=1。
  3. 在服务器将数据传输完毕之后,会再次给客户端发送一个数据包,这个数据包中FIN=1,表示服务器想断开连接,ack=u+1和步骤二中的ack一样,这是因为在传输未传输完毕的数据时,客户端并不会对服务器有任何回应,因此ack并不会有变化。
  4. 客户端接收到服务器发来的FIN=1的数据包后,会给服务端一个回应ACK=1,表示我已经知道了你向断开连接,现在,你(服务器)可以断开连接了。
  5. 四次挥手就此完毕,客户端在步骤4后,会等待2MSL,然后再关闭连接。注:MSL是Maximum Segment Lifetime,最长报文寿命,是一个时间量。

为什么四次挥手后客户端要等待2MSL再关闭连接

  1. 还是因为网络传输的不确定性,在第四次挥手客户端给服务器发送应答包时,这个数据包有可能会出现丢失的情况,这时服务器就收不到这个应答包,不会关闭连接。
  2. 服务器在等待一段时间后,若还是未收到客户端发送的这个应答包,会给客户端再次发送一个请求(请求重发),于是在这2MSL内,客户端不关闭连接,还是可以收到这个重发的请求,然后再次对服务器进行回应。
  3. 这样就防止了应答包丢失而造成的服务器一直不会关闭的情况。

可靠信的实现

序列号与确认应答

在上一节中讲到,TCP首部有序列号字段(seq),对数据发送端来说,该字段可以指定数据已经发送到哪里了,对数据接收端来说,该字段可用于ack(确认应答信号)的值,也就是下一个接受到的数据的序列号应该是多少。
在接收端,如果接收到的数据序列号无误,那么就会给发送端一个应答响应(ACK=1),来告诉发送端数据接收成功,并且告诉发送端下一个数据的序列号应该是多少(ack=?)
这里补充一下,seq字段初值是随机的,每次发送数据都会进行叠加,每一次增加本次发送数据的字节数
在这里插入图片描述
如果发送端发送的数据丢失了,接收端并没有接受到,那么接收端不会对发送端做出响应,发送端在等待一定的时间后,如果还未收到接收端的应答响应,那么会把数据进行重发。
在这里插入图片描述
如果接受端已接收到数据,但向发送端返回的应答响应出现了丢失,接收端也不会收到应答响应,同样的等待一段时间后,接收端会进行数据重发。当然,此时接收端会把已经接收到的数据丢弃(如何判断数据是否已接收?就是通过数据的接受到的数据序列号seq,和想要的数据序列号ack是否一致),再次给发送端一个应答响应。
在这里插入图片描述
注意,上述图中的序列号都是从1开始,这只是为了方便讲解,真正的序列号是从一个随机数开始的!

最大消息长度(MMS)

TCP在传输数据时,是用分段的方式来进行传输的,每次数据的发送都是以段为单位,每个段落的大小,就是MMS(Maximum Segment Size,最大消息长度)。
MMS是在三次握手时被确定的,客户端和服务器会在TCP首部中写入自己适用的MMS大小。然后选择一个较小的值作为本次连接的MMS。

拆包

当一次发送的数据量过大而超过了MMS时,会将该数据拆分成多个数据包进行发送。

黏包

当在短时间内发送很多数据量很小的包时,会根据一定的算法把一些小包合成一个大包进行发送。
这是对网络通信的一种优化,如果发送数据量太小,那么进行一次网络传输的性价比很低。
但是这也会导致一些数据不能立刻进行发送,造成一些延时。
如果要禁止黏包,调用socket的setTcpNoDelay(boolean on)方法即可。

滑动窗口

如果以1个段为单位进行数据发送,每发送一个段就进行应答处理,这样会使通信的性能很低。
在这里插入图片描述
为了解决这个问题,TCP使用滑动窗口来发送数据
在这里插入图片描述
滑动窗口的大小是指无需等待应答而可以继续发送数据的最大值。如上图所示,滑动窗口的大小就是4个段。
在这里插入图片描述
上图可以看出滑动窗口的工作过程。

  1. 在①中,滑动窗口处于1001~5001的位置上,此时这个范围内的数据可以在没收到应答相应的情况下进行发送。
  2. 在②中,主机A已经将1001~2000的数据发送给了主机B,并且收到了主机B的应答响应(ACK),并知道了主机B下一个所需的数据seq应该是2001。
  3. 在③中,主机A根据主机B的ack=2001,将滑动窗口的首端移到2001,接着发送数据。

不可避免的还是会出现数据丢失的情况。
有可能出现接收方的确认应答丢失的情况,从图中可见,采用滑动窗口的方式进行数据发送,可以对这个问题有很大的优化,比如图中主机B对1001的应答丢失了,但是它下一个应答并没有丢失,主机A不需要去重发1~1000的数据段。
在这里插入图片描述
还有可能出现发送数据丢失的情况。
如下图所示,主机A的1001~2000数据段出现了丢失的情况,主机B接收到了序列号为1000的数据后,下一次接受到的数据的序列号是2001,而不是1001,此时,接收端会连续三次向发送端法出ack=1001的应答信号。接收端连续3次收到同样的应答信号后,就会认为该数据段出现了丢失的情况,会将该数据段重新发送给接收端。
在这里插入图片描述

流量控制

接收端的接收缓冲区是有大小限制的,如果接收端在其他任务上有了时间的消耗,可能没有时间处理接收缓冲区的数据,因此接收缓冲区可能会越来越小,甚至没有剩余空间了。
在这种情况下,如果接收端继续向发送端发送信息,可能会出现大量的数据丢失现象,从而触发重发机制,导致网络流量的浪费。
于是,流量控制机制应运而生。
接收端主机会在TCP首部中向发送端一个窗口大小,表示现在的接收端缓冲区的大小。
如下图所示,最右边的3000、2000、1000、0等数据就是窗口的大小。
在这里插入图片描述

拥塞控制

cwnd:拥塞窗口
ssthresh:慢启动门限
如果cwnd < ssthresh 使用慢起动算法
如果cwnd > ssthresh 使用避免拥塞算法
慢起动:按指数规律增长
拥塞避免:每次加1。
发送窗口:发送窗口由拥塞窗口和接收端缓冲窗口大小较小的那一个决定。
如果出现网络拥塞的情况(即超时后还是没有收到确认应答),则把ssthresh设置为发送窗口的一半,并将此时的拥塞窗口设置为1,重复cwnd > ssthresh 和 cwnd < ssthresh 所做的操作。
在这里插入图片描述

UDP介绍

UDP是一个无连接的、不可靠的传输协议。
UDP没有复杂的控制机制,传输过程中即使出现丢包的情况,也不会进行重发。
UDP首部如下(原图地址
在这里插入图片描述
可见,UDP首部结构非常简单,没有TCP首部那么多的字段,它仅仅需要保存源端口和目的端口位置,还有该数据包中数据的长度以及校验和即可。
UDP面向无连接,可以随时进行数据发送。而且UDP本身的处理简单高效,经常用于以下方面:

  1. 包总量较少的通信(如DNS)
  2. 视频、音频的通信

参考资料

图解TCP/IP第5版
http://blog.youkuaiyun.com/xieyutian1990
http://blog.youkuaiyun.com/qzcsu

最后,我是一个菜鸟,如果有讲的不对的地方,请大家指出~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值