TCP和UDP总结及常见面试题

(7条消息) [计算机网络] TCP和UDP总结及常见面试题_努力变得不菜的菜鸡的博客-CSDN博客

1 TCP和UDP区别

  • TCP是面向连接的,UDP是面向无连接的
  • TCP是可靠的,UDP不是可靠的(也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付)
  • TCP是面向字节流的,UDP是面向报文的
  • TCP只支持点对点通信,UDP支持一对一,一对多,多对一,多对多的通信模式
  • TCP首部开销(20字节)比UDP的首部开销(8个字节)要大
  • TCP保证数据正确性,UDP可能丢包
  • TCP保证数据顺序,UDP不保证

2 UDP
TCP 和 UDP 是传输层的两个协议

我们来看一下 UDP 的包头

 

 

 

由上图可以看出,UDP 除了端口号,基本啥都没有了。如果没有这两个端口号,数据就不知道该发给哪个应用。

所以 UDP 就像一个小孩子,特别简单,有如下三个特点

UDP 的特点

  • 沟通简单,不需要大量的数据结构,处理逻辑和包头字段
  • 轻信他人。它不会建立连接,但是会监听这个地方,谁都可以传给它数据,它也可以传给任何人数据,甚至可以同时传给多个人数据。
  • 愣头青,做事不懂变通。不会根据网络的情况进行拥塞控制,无论是否丢包,它该怎么发还是怎么发
  • 因为 UDP 是"小孩子",所以处理的是一些没那么难的项目,并且就算失败的也能接收。基于这些特点的话,UDP 可以使用在如下场景中

UDP 的主要应用场景

  • 需要资源少,网络情况稳定的内网,或者对于丢包不敏感的应用,比如 DHCP 就是基于 UDP 协议的。
  • 不需要一对一沟通,建立连接,而是可以广播的应用。因为它不面向连接,所以可以做到一对多,承担广播或者多播的协议。
  • 需要处理速度快,可以容忍丢包,但是即使网络拥塞,也毫不退缩,一往无前的时候

基于 UDP 的几个例子

  • 直播。直播对实时性的要求比较高,宁可丢包,也不要卡顿的,所以很多直播应用都基于 UDP 实现了自己的视频传输协议
  • 实时游戏。游戏的特点也是实时性比较高,在这种情况下,采用自定义的可靠的 UDP 协议,自定义重传策略,能够把产生的延迟降到最低,减少网络问题对游戏造成的影响
  • 物联网。一方面,物联网领域中断资源少,很可能知识个很小的嵌入式系统,而维护 TCP 协议的代价太大了;另一方面,物联网对实时性的要求也特别高。比如 Google 旗下的 Nest 简历 Thread Group,推出了物联网通信协议 Thread,就是基于 UDP 协议的。

 

3 TCP

首先是 TCP 的包头格式

TCP 的包头有哪些内容,分别有什么用

  • 首先,源端口和目标端口是不可少的。
  • 接下来是包的序号。主要是为了解决乱序问题。不编好号怎么知道哪个先来,哪个后到
  • 确认序号。发出去的包应该有确认,这样能知道对方是否收到,如果没收到就应该重新发送,这个解决的是不丢包的问题
  • 状态位。SYN 是发起一个链接,ACK 是回复,RST 是重新连接,FIN 是结束连接。因为 TCP 是面向连接的,因此需要双方维护连接的状态,这些状态位的包会引起双方的状态变更
  • 窗口大小,TCP 要做流量控制,需要通信双方各声明一个窗口,标识自己当前的处理能力。

通过对 TCP 头的解析,我们知道要掌握 TCP 协议,应该重点关注以下问题:

  • 顺序问题
  • 丢包问题
  • 连接维护
  • 流量控制
  • 拥塞控制

4 TCP 的三次握手
所有的问题,首先都要建立连接,所以首先是连接维护的问题

TCP 的建立连接称为三次握手,可以简单理解为下面这种情况:

A:您好,我是 A
B:您好 A,我是 B
A:您好 B

至于为什么是三次握手,总结的话就是通信双方全都有来有回

对于 A 来说它发出请求,并收到了 B 的响应,对于 B 来说它响应了 A 的请求,并且也接收到了响应。

TCP 的三次握手除了建立连接外,主要还是为了沟通 TCP 包的序号问题。

 

 

 

 

 

 

 

 

 

客户端等待完2MSL之后,结束TIME-WAIT阶段,进入CLOSED阶段,由此完成“四次挥手”。

后“两次挥手”既让客户端知道了服务器端准备好释放连接了,也让服务器端知道了客户端了解了自己准备好释放连接了。
于是,可以确认关闭服务器端到客户端方向上的连接了,由此完成“四次挥手”。

与“三次挥手”一样,在客户端与服务器端传输的TCP报文中,双方的确认号Ack和序号Seq的值,都是在彼此Ack和Seq值的基础上进行计算的,这样做保证了TCP报文传输的连贯性,一旦出现某一方发出的TCP报文丢失,便无法继续"挥手",以此确保了"四次挥手"的顺利完成。

6 累计确认
TCP如何实现可靠传输?

首先为了保证时序性,每个包都有一个ID。在建立连接的时候会商定起始ID是什么,然后按照ID一个个发送,为了保证不丢包,需要对发送的包都要进行应答,当然,这个应答不是一个一个来的,而是会应答某个之前的ID,表示都收到了,这种模式称为累计应答或累计确认。

为了记录所有发送的包和接收的包,TCP需要发送端和接收段分别缓存这些记录,
发送端的缓存里是按照包的ID一个个排序,根据处理的情况分为四个部分:

(1)发送并且确认的
(2)发送尚未确认的
(3)没有发送等待发送的
(4)没有发送并且暂时不会发送的
这里的第三部分和第四部分就属于流量控制的内容
在TCP里,接收端会给发送端报一个窗口大小,叫Advertised window。这个窗口应该等于上面的第一部分加第二部分,超过这个窗口,接收端就接收不过来了,就不能发送了
于是,发送端要保持下面的数据结构

对于接收端,它的里面的内容要简单一些

(1)接收并且确认过的
(2)还没接收,但是马上 就能接收的
(3)还没接收,但也无法接收的
接收端对应的数据结构:

 

 

8 拥塞控制的问题
拥塞控制也是通过窗口的大小来控制的,但是检测网络满不满是个挺难的事情,所以TCP发送包经常被比喻成往水管里灌水,所以拥塞控制就是在不堵塞,不丢包的情况下尽可能的发挥带宽。

水管有粗细,网络有带宽,即每秒钟能发送多少数据;水管有长度,端到端有时延。
理想情况下,水管里面的水 = 水管粗细 * 水管长度,
对于网络上,通道的容量 = 带宽 * 往返时延。

如果我们设置发送窗口,使得发送但未确认的包为通道的容量,就能撑满整个管道。

 

 

如图所示,假设往返时间为 8 秒,去 4 秒,回 4 秒,每秒发送一个包,已经过去了 8 秒,则 8 个包都发出去了,其中前四个已经到达接收端,但是 ACK 还没返回,不能算发送成功,5-8 后四个包还在路上,还没被接收,这个时候,管道正好撑满,在发送端,已发送未确认的 8 个包,正好等于带宽,也即每秒发送一个包,也即每秒发送一个包,乘以来回时间 8 秒。

如果在这个基础上调大窗口,使得单位时间可以发送更多的包,那么会出现接收端处理不过来,多出来的包就会被丢弃,这个时候,我们可以增加一个缓存,但是缓存里面的包4秒内肯定达不到接收端了,它的缺点会增加时延,如果时延达到一定程度就会超时重传

TCP拥塞控制主要用来避免两种情况,包丢失和超时重传,一旦出现了这些现象说明发送的太快了,要慢一点。
具体的方法就是发送端慢启动, 比如倒水,刚开始倒的很慢,渐渐变快。然后设定一个阈值,当超过这个值的时候就要慢下来
慢下来还是在增长,这时候就可能水满则溢,出现拥塞,需要降低倒水的速度,等水慢慢渗下去。

拥塞的一种表现是丢包,需要超时重传,这个时候,采用快速重传算法,将当前速度变为一般,所以速度还是在比较高的值,也没有一夜回到解放前。

tcp采用累积确认(累积ack)机制,一个窗口内的数据接收完全才进行发回一个ack.

9 面试问题
NO1 --- TCP和UDP的区别

  • TCP是面向连接的,UDP是面向无连接的
  • TCP是可靠的,UDP不是可靠的
  • (也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付)
  • TCP是面向字节流的,UDP是面向报文的
  • TCP只支持点对点通信,UDP支持一对一,一对多,多对一,多对多的通信模式
  • TCP首部开销(20字节)比UDP的首部开销(8个字节)要大
  • TCP保证数据正确性,UDP可能丢包
  • TCP保证数据顺序,UDP不保证

NO2 --- 什么是面向连接,什么是面向无连接

在互通之前,面向连接的协议会先建立连接,如 TCP 有三次握手,而 UDP 不会

  • **面向连接:**是指通信百双方在通信时,要事先建立一条通信线路,其有三个过程:建立连接、使用连接和释放连接。电话系统度是一个面向连接的模式,拨号、通知话、挂机;TCP协议就是一种面向连接的协议。
  • **面向无连接:**是指通信双方不需要事先建立一条通信线路道,而是把每个带有目的地址的包(报文分组)送到线路上,由系统自主选定路线进行传输。邮专政系统是一个无连接的模式,天罗地网式的选择路线,天女散属花式的传播形式;IP、UDP协议就是一种无连接协议。

NO3 --- TCP为什么是可靠连接

通过TCP连接传输的数据无差错,不丢失,不重复,且按顺序到达
TCP报文头里面的序号能使TCP的数据按序到达
报文头里面的确认序号能保证不丢包,累计确认及超时重传机制
TCP拥有流量控制及拥塞控制的机制
NO4 --- 为什么要进行三次握手?两次不可以吗?
1
为了防止服务器端开启一些无用的连接增加服务器开销以及防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。

  • 第一种情况:防止服务器端开启一些无用的连接增加服务器开销

由于网络传输是有延时的(要通过网络光纤和各种中间代理服务器),在传输的过程中,比如客户端发起SYN = 1 创建连接的请求(第一次握手)。
如果服务器就直接创建了这个连接并返回包含SYN,ACK和Seq等内容的数据包给客户端,这个数据包因为网络传输的原因丢失了,丢失之后客户端一直没接收到服务器返回的数据包。
客户端可能设置了一个超时时间,时间到了就关闭创建连接的请求。再次重新发出创建连接的请求,而服务器端是不知道的,如果没有第三次握手告诉服务器端客户端收到了服务器传输数据的话,服务器端是不知道客户端有没有收到服务器端发送的消息的。
这样的话,没有给服务器端一个创建还是关闭连接端口的请求,服务器端的端口就一直开着,等到客户端因超时重新发出请求时,服务器端会重新再开一个端口连接,那么上一个数据请求的端口就一直开着,长此以往,这样开着的端口就多了,就会造成服务器开销的严重浪费。

  • 第二种情况:防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误

已经失效的客户端发出的请求信息,由于某种原因传输到了服务器端,服务器端以为是客户端发出的有效请求,接收后产生错误。
所以我们需要“第三次握手” 来确认这个过程,让客户端和服务器端都能及时察觉到因为网络等一些问题导致的连接创建失败,这样服务器端的端口就可以关闭了而不用一直等待。

也可以这样理解

  • “第三次握手”是客户端向服务器发送数据,这个数据就是要告诉服务器,客户端有没有收到服务器“第二次握手”时传过去的数据。若发送的这个数据是“收到了”的信息,接收后服务器就正常建立TCP连接,否则建立TCP连接失败,服务器关闭连接端口。由此减少服务器开销和防止接收到失效请求发生的错误。

NO5 --- 为什么“握手是三次,“挥手””却需要四次?
1
TCP建立连接时之所以只需要“三次握手”,是因为在第二次“握手” 过程中,服务器端发给客户端的TCP报文是以SYN与ACK作为标识位的。
SYN是请求连接标志,表示服务器端同意建立连接;ACK是确认报文,表示告诉客户端,服务器端收到了它的请求报文
即SYN建立连接报文与ACK确认接收报文是在同一次“握手”当中传输的,所以“三次握手”不多也不好,双方刚好信息互通。

TCP释放连接时之所以需要“四次握手”,是因为FIN释放连接报文与ACK确认接收报文是分别由第二次和第三次“握手”传输的。
为何建立连接时一起传,释放连接时却要分开传输?
建立连接时,被动房服务器端结束CLOSED阶段进入“握手”阶段并不需要任何准备,可以直接返回SYN和ACK报文,开始建立连接。
释放连接时,被动方服务器,突然收到主动方客户端释放连接的请求并不能立即释放连接,因为还有必要的数据需要进行处理,所以服务器先返回ACK确认收到报文,经过CLOSED-WAIT阶段准备好释放连接之后,才能返回FIN释放连接报文。

所以是,“三次握手”,“四次挥手”。

NO6 --- 为什么客户端在TIME-WAIT阶段要等2MSL?

为的是确认服务器端是否收到客户端发出的ACK确认报文

当客户端发出最后的ACK确认报文时,并不能确定服务器就能够收到该端报文。
所以在客户端发送完ACK确认报文之后,会设置一个时长为2MSL的计时器。
MSL指的是Maximum Segment Lifetime:一段TCP报文在传输过程中的最大生命周期。
2MSL即时服务器发出FIN报文和客户端发出的ACK确认报文所能保持有效的最大时长。

1)服务器端在1MSL内没有收到客户端发出的ACK确认报文,就会再次向客户端发出FIN报文;
2)如果客户端在2MSL内,再次收到了来自服务器端的FIN报文,说明服务器端由于各种原因没有接收到客户端发出的ACK确认报文。客户端再次向服务器端发出ACK确认报文,计时器重置,重新开始2MSL的计时;
3)否则客户端在2MSL内没有再次收到来自服务器端的FIN报文,说明服务器端正常接收了ACK确认报文,客户端可以进入CLOSED阶段,完成“四次挥手”。

所以,客户端要经历时长为2SML的TIME-WAIT阶段;这也是为什么客户端比服务器端晚进入CLOSED阶段的原因

NO7 --- TCP协议如何来保证传输的可靠性

TCP提供一种面向连接的、可靠的字节流服务,
其中,面向连接意味着两个使用TCP的应用(通常是一个客户一个服务器)在彼此交换数据之前先见一个一个TCP连接。
在一个TCP连接中,仅有两方进行彼此通信,字节流服务意味着两个应用程序通过TCP链接交换8bit字节构成的字节流,TCP不在字节流中插入记录标识符。
对于可靠性,TCP通过以下方式进行保证:

  • 数据包校验:目的是检测数据在传输过程中的任何变化,若检测出包有错,则丢弃褒文段并且不给出响应,这时TCP发送数据端超时后会重发数据
  • 对失序数据包重新排序:既然TCP报文段作为IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段的到达也可能会失序。TCP将对失序数据进行重排序,然后才交给应用层。
  • 丢弃重复数据:对于重复数据,能够丢弃重复数据
  • 应答机制: 当TCP收到发自TCP连接另一端的数据,它将发送一个确认。这个确认不是立即发送,通常将推迟几分之一秒
  • 超时重发: 当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段
  • 流量控制 TCP连接的每一方都有固定大小的缓冲空间。TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据,这可以防止较快主机致使较慢主机的缓冲区溢出,这就是流量控制。TCP使用的流量控制协议是可变大小的滑动窗口协议。

面向字节流:

创建一个TCP的socket, 同时在内核中创建一个发送缓冲区和一个接收缓冲区;
另一方面, TCP的一个连接, 既有发送缓冲区, 也有接收缓冲区, 那么对于这一个连接, 既可以读数据, 也可以写数据. 这个概念叫做 全双工 。

  1. 调用write时, 数据会先写入发送缓冲区中;
  2. 如果发送的字节数太长, 会被拆分成多个TCP的数据包发出; 如果发送的字节数太短, 就会先在缓冲区里等待, 等到缓冲区长度差不多了, 或者其他合适的时机发送出去;
  3. 接收数据的时候, 数据也是从网卡驱动程序到达内核的接收缓冲区;
  4. 然后应用程序可以调用read从接收缓冲区拿数据;

TCP重传的次数和间隔时间

第一次发送后所设置的超时时间实际上为1.5秒,此后该时间在每次重传时增加一倍,一直到64秒,采用的是指数退避算法。一共重传12次,大约9分钟才放弃重传,该时间在目前的TCP实现中是不可变的,Solaris2.2允许管理者改变这个时间,tcp_ip_abort_interval变量。且其默认值为两分钟,而不是最常用的9分钟。

TCP粘包问题

tcp作为面向流的协议,不存在“粘包问题”,这个大家已经说清楚了,我补充一下国内开发人员说“粘包问题”的时候,到底想说什么?

首先,什么叫“包”?

在基于tcp开发应用的语境下,其实有两种“包”,其一是tcp在传输的时候封装的报文,分为包头和负载,其二是应用开发者在应用层封装的报文结构。

第二,什么叫“粘”?这里同样有两种含义。

其一是指,由于tcp是面向流的协议,不会按照应用开发者的期望保持send输入数据的边界,导致接收侧有可能一下子收到多个应用层报文,需要应用开发者自己分开,有些人觉得这样不合理(那你为啥不用udp),起了个名叫“粘包”。

其二是指,用户数据被tcp发出去的时候,存在多个小尺寸数据被封装在一个tcp报文中发出去的可能性。这种“粘”不是接收侧的效果,而是由于Nagle算法(或者TCP_CORK)的存在,在发送的时候,就把应用开发者多次send的数据,“粘”在一个tcp报文里面发出去了,于是,先被send的数据可能需要等待一段时间,才能跟后面被send的数据一起组成报文发出去。

日常生活中说“粘包”的人,指的可能是第一种情况,也可能是第二种,不继续追问的话,很难知道他们到底在说什么。

第三,这两个其实都不是“问题”。

第一个是tcp的应有之义,人家本身就是个面向流的协议,如果你要用它传输数据报(datagram),必然要自己实现stream2datagram的过程。这不叫解决问题,这叫实现功能。

第二个是tcp在实现的时候,为了解决大量小报文场景下包头比负载大,导致传输性价比太低的问题,专门设计的。其实在99%的情况下,Nagle算法根本就不会导致可感知的传输延迟,只是在某些场景下,Nagle算法和延迟ACK机制碰到一起,才会导致可感知的延迟。

有些应用开发者病急乱投医,看到Nagle算法可能导致发送等待,并且可以禁止掉,于是以讹传讹说禁止Nagle算法可以让发送更快。其实99%的情况下禁不禁止都一样,延迟根本不是Nagle算法导致的;就算真有问题,最优解决方案也不是屏蔽Nagle算法。

最后,粘包是个“土话”,容易引起歧义,不建议沿用。stream2datagram就是stream2datagram,TCP_NODELAY就是TCP_NODELAY,不要说什么“粘包”。

ps:

说了半天忘了说怎么解决“问题”。

第一种“粘包”,靠设计一个带包头的应用层报文结构就能解决。包头定长,以特定标志开头,里带着负载长度,这样接收侧只要以定长尝试读取包头,再按照包头里的负载长度读取负载就行了,多出来的数据都留在缓冲区里即可。

其实ip报文和tcp报文都是这么干的。

第二种“粘包”,设置TCP_NODELAY就能屏蔽Nagle算法,但你最好确定自己知道自己在干什么。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章