tcp与udp的主要区别_与路径无关的问题

tcp与udp的主要区别_与路径无关的问题TCP与UDP的区别 TCP(TransmissionControlProtocol)的概念 TCP是一种面向连接的,提供可靠交付服务和全双工通信的,基于字节流的端到端的传输层通信协议

目录

TCP与UDP的区别

TCP协议和UDP协议为什么会共存

TCP和UDP分别对应的协议

为什么 TCP 叫数据流模式? UDP 叫数据报模式?

TCP的挥手握手

TCP的三次握手过程?为什么会采用三次握手,二次握手可以吗?

TCP握手超时重传机制

TCP三次握手的性能提升

TCP的四次挥手过程,为什么要四次

TCP四次挥手中TIME_WAIT状态的概念及意义。

TCP四次挥手的性能优化

怎样使用 UDP 实现 TCP 的可靠传输

TCP通过哪些措施,保证传输可靠?

TCP的拥塞控制原理

TCP的滑动窗口协议与停止等待协议的区别

TCP流量控制原理

TCP 半连接队列和全连接队列

TCP的粘包与拆包问题


TCP与UDP的区别

TCP(Transmission Control Protocol)的概念

TCP是一种面向连接的,提供可靠交付服务和全双工通信的,基于字节流的端到端的传输层通信协议。

TCP在传输数据之前必须先建立连接,数据传输结束后要释放连接。

每一条TCP连接只能有2个端点,故TCP不提供广播或多播服务。

TCP提供可靠交付,通过TCP连接传输的数据,无差错、不丢失、不重复、并且按序到达。

TCP是面向字节流的。虽然应用进程和TCP的交互是一次一个数据块(大小不等),但TCP把应用程序交下来的数据看成仅仅是一连串的无结构的字节流。TCP并不知道所传输的字节流的含义。

UDP(UserDatagram Protocol)的概念

UDP是一种无连接的,尽最大努力交付的,基于报文的端到端的传输层通信协议。

UDP在发送数据之前不需要建立连接

UDP不保证可靠交付,主机不需要建立复杂的连接状态

UDP是面向报文的。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界,即应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。在接收端,UDP一次交付一个完整的报文。

UDP没有拥塞控制,网络出现的拥塞不会使源主机的发送速率降低。

UDP支持一对一、一对多、多对一和多对多的交互通信。

UDP的首部开销小,只有8个字节,比TCP的20个字节的首部要短。

区别角度

TCP

UDP

是否连接

面向连接(发送数据前需要建立连接)

无连接(发送数据无需连接)

是否丢包重试

实现了数据传输时各种控制功能,可以进行丢包的重发控制,还可以对次序乱掉的分包进行顺序控制

不会进行丢包重试,也不会纠正到达的顺序

模式

流模式(面向字节流)

数据报模式(面向报文)

对应关系

一对一

支持一对一,一对多,多对一和多对多的交互通信

头部开销

最小20字节

只能8字节

可靠性

全双工非常可靠、无差错、不丢失、不重复、且按序到达

不保证可靠交付,不保证顺序到达

拥塞控制

拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)

资源要求

TCP程序结构较复杂,较多

UDP程序结构简单,少

使用情况

TCP协议适用于对效率要求相对低,但对准确性要求相对高的场景下,或者是有一种连接概念的场景下;而UDP协议适用于对效率要求相对高,对准确性要求相对低的场景。

 

很多文章都说TCP协议可靠,UDP协议不可靠!为什么前者可靠,后者不可靠呢?既然UDP协议不可靠,为什么还要使用它呢?所谓的TCP协议是面向连接的协议,面向连接是什么呢?TCP和UDP都是传输层的协议!从编程的角度看,就是两个模块(模块就是代码的集合,一系列代码的组合提供相应的功能!模块化最终目的就是:分工协作!模块化好处:便于扩展开发以及维护!)。

在一个TCP连接中,仅有两方进行彼此通信,因此广播和多播不能用于TCP

TCP使用校验和,累积确认和重传机制来保证可靠传输

TCP使用滑动窗口机制来实现流量控制,通过动态改变窗口的大小进行拥塞控制

TCP协议和UDP协议为什么会共存

1)大家要知道,一种物理线路,单位时间内,能够创建的“虚拟信道”是有限的!

2)使用TCP协议传输数据,当数据从A端传到B端后,B端会发送一个确认包(ACK包)给A端,告知A端数据我已收到!UDP协议就没有这种确认机制!这就是为什么说TCP协议可靠,UDP协议不可靠.

QQ普通会员就是使用的UDP协议进行传输数据!既然UDP协议自身没有确认机制,这个工作可以交给应用层的进程来完成(QQ)!大家使用QQ的时候,感觉出错的几率还是非常小吧!当然,把这个确认工作完全交给QQ自身来做,就直接导致了,QQ软件体积增大!

有些应用,对数据传输可靠性要求非常高,例如大家浏览网页,通过网页注册帐号、转帐等服务,这是不容许出错的,使用TCP协议能把出错的可能性降到最低(当然,网络自身很糟糕,TCP协议也没办法)。但是,提供这种可靠服务,会加大网络带宽的开销,因为“虚拟信道”是持续存在的,同时网络中还会出现大量的ACK和FIN包!

因此,鱼和熊掌不可兼得,需根据实际情况选择传输协议。TCP协议提供了可靠的数据传输,但是其拥塞控制、数据校验、重传机制的网络开销很大,不适合实时通信,所以选择开销很小的UDP协议来传输数据。

TCP和UDP分别对应的协议

TCP对应的协议:

1) FTP:定义了文件传输协议,使用21端口。

2) Telnet:一种用于远程登陆的端口,使用23端口,用户可以以自己的身份远程连接到计算机上,可提供基于DOS模式下的通信服务。

3) SMTP:邮件传送协议,用于发送邮件。服务器开放的是25号端口。

4) POP3:它是和SMTP对应,POP3用于接收邮件。POP3协议所用的是110端口。

5) HTTP:是从Web服务器传输超文本到本地浏览器的传送协议,使用80端口

UDP对应的协议:

1) DNS:用于域名解析服务,将域名地址转换为IP地址。DNS用的是53号端口。

2) SNMP:简单网络管理协议,使用161号端口,是用来管理网络设备的。由于网络设备很多,无连接的服务就体现出其优势。

3) TFTP(Trival File Transfer Protocal),简单文件传输协议,该协议在熟知端口69上使用UDP服务。

tcp与udp的主要区别_与路径无关的问题

为什么 TCP 叫数据流模式? UDP 叫数据报模式?

所谓的“流模式”,是指TCP发送端发送几次数据和接收端接收几次数据是没有必然联系的,比如你通过 TCP连接给另一端发送数据,你只调用了一次 write,发送了100个字节,但是对方可以分10次收完,每次10个字节;你也可以调用10次write,每次10个字节,但是对方可以一次就收完。

原因:这是因为TCP是面向连接的,一个 socket 中收到的数据都是由同一台主机发出,且有序地到达,所以每次读取多少数据都可以。

所谓的“数据报模式”,是指UDP发送端调用了几次 write,接收端必须用相同次数的 read 读完。UDP是基于报文的,在接收的时候,每次最多只能读取一个报文,报文和报文是不会合并的,如果缓冲区小于报文长度,则多出的部分会被丢弃。

原因:这是因为UDP是无连接的,只要知道接收端的 IP 和端口,任何主机都可以向接收端发送数据。 这时候,如果一次能读取超过一个报文的数据, 则会乱套。

TCP的挥手握手

TCP的三次握手过程?为什么会采用三次握手,二次握手可以吗?

答:建立连接的过程是利用客户服务器模式,假设主机A为客户端,主机B为服务器端。

(1)TCP的三次握手过程:主机A向B发送连接请求;主机B对收到的主机A的报文段进行确认;主机A再次对主机B的确认进行确认。

(2)采用三次握手是为了防止失效的连接请求报文段突然又传送到主机B,因而产生错误。失效的连接请求报文段是指:主机A发出的连接请求没有收到主机B的确认,于是经过一段时间后,主机A又重新向主机B发送连接请求,且建立成功,顺序完成数据传输。考虑这样一种特殊情况,主机A第一次发送的连接请求并没有丢失,而是因为网络节点导致延迟达到主机B,主机B以为是主机A又发起的新连接,于是主机B同意连接,并向主机A发回确认,但是此时主机A根本不会理会,主机B就一直在等待主机A发送数据,导致主机B的资源浪费。

(3)如果不采用三次握手,我们考虑以下场景:

        采用一次握手:

                首先A发送一个(SYN)到B,意思是A要和B建立连接进行通信;

                如果是只有一次握手的话,这样肯定是不行的,A压根都不知道B是不是收到了这个请求。

        采用二次握手:

                B收到A要建立连接的请求之后,发送一个确认(SYN+ACK)给A,意思是收到A的消息了,B这里也是通的,表示可以建立连接;

                如果只有两次通信的话,这时候B不确定A是否收到了确认消息,有可能这个确认消息由于某些原因丢了。

        采用三次握手:

                A如果收到了B的确认消息之后,再发出一个确认(ACK)消息,意思是告诉B,这边是通的,然后A和B就可以建立连接相互通信了;

                这个时候经过了三次握手,A和B双方确认了两边都是通的,可以相互通信了,已经可以建立一个可靠的连接,并且可以相互发送数据。

tcp与udp的主要区别_与路径无关的问题

TCP握手超时重传机制

(1) 如果第一个包,A发送给B请求建立连接的报文(SYN)如果丢掉了,A会周期性的超时重传,直到B发出确认(SYN+ACK);
(2) 如果第二个包,B发送给A的确认报文(SYN+ACK)如果丢掉了,B会周期性的超时重传,直到A发出确认(ACK);
(3) 如果第三个包,A发送给B的确认报文(ACK)如果丢掉了

  • A在发送完确认报文之后,单方面会进入ESTABLISHED的状态,B还是SYN_RCVD状态
  • 如果此时双方都没有数据需要发送,B会周期性的超时发送(SYN+ACK),直到收到A的确认报文(ACK),此时B也进入ESTABLISHED状态,双方可以发送数据;
  • 如果A有数据发送,A发送的是(ACK+DATA),B会在收到这个数据包的时候自动切换到ESTABLISHED状态,并接受数据(DATA);
  • 如果这个时候B要发送数据,B是发送不了数据的,会周期性的超时重传(SYN+ACK)直到收到A的确认(ACK)B才能发送数据。

TCP握手连接过程双方的状态变化

tcp与udp的主要区别_与路径无关的问题

TCP三次握手的性能提升

客户端优化(发起连接方)

客户端作为主动发起连接⽅,⾸先它将发送 SYN 包,于是客户端的连接就会处于 SYN_SENT 状态。 客户端在等待服务端回复的 ACK 报⽂,正常情况下,服务器会在⼏毫秒内返回 SYN+ACK ,但如果客户端⻓时间 没有收到 SYN+ACK 报⽂,则会᯿发 SYN 包,重发的次数由 tcp_syn_retries 参数控制,默认是 5 次,整个过程大概需要花费1分钟。

因此,可以根据⽹络的稳定性和⽬标服务器的繁忙程度修改 SYN 的重传次数,调整客户端的三次握⼿时间上限。⽐如 内⽹中通讯时,就可以适当调低重试次数,尽快把错误暴露给应⽤程序。

服务端优化(连接接收方)

当服务端收到 SYN 包后,服务端会⽴⻢回复 SYN+ACK 包,表明确认收到了客户端的序列号,同时也把⾃⼰的序 列号发给对⽅。

此时,服务端出现了新连接,状态是 SYN_RCV 。在这个状态下,Linux 内核就会建⽴⼀个「半连接队列」来维 护「未完成」的握⼿信息,当半连接队列溢出后,服务端就⽆法再建⽴新的连接。

SYN 攻击,攻击的是就是这个半连接队列。

因此,常见的优化方式如下:

1. 调整SYN半连接队列长度

2. 调整 SYN + ACK 报文的重传次数

3. 调整 accept 队列的长度

如何绕过三次握⼿?

在 Linux 3.7 内核版本之后,提供了 TCP Fast Open 功能,这个功能可以减少 TCP 连接建⽴的时延。

在客户端⾸次建⽴连接时的过程:

  1. 客户端发送 SYN 报⽂,该报⽂包含 Fast Open 选项,且该选项的 Cookie 为空,这表明客户端请求 Fast Open Cookie;

  2. ⽀持 TCP Fast Open 的服务器⽣成 Cookie,并将其置于 SYN-ACK 数据包中的 Fast Open 选项以发回客户 端;

  3. 客户端收到 SYN-ACK 后,本地缓存 Fast Open 选项中的 Cookie。

所以,第⼀次发起 HTTP GET 请求的时候,还是需要正常的三次握⼿流程。

之后,如果客户端再次向服务器建⽴连接时的过程:

  1. 客户端发送 SYN 报⽂,该报⽂包含「数据」(对于⾮ TFO 的普通 TCP 握⼿过程,SYN 报⽂中不包含「数 据」)以及此前记录的 Cookie;

  2. ⽀持 TCP Fast Open 的服务器会对收到 Cookie 进⾏校验:如果 Cookie 有效,服务器将在 SYN-ACK 报⽂中 对 SYN 和「数据」进⾏确认,服务器随后将「数据」递送⾄相应的应⽤程序;如果 Cookie ⽆效,服务器将 丢弃 SYN 报⽂中包含的「数据」,且其随后发出的 SYN-ACK 报⽂将只确认 SYN 的对应序列号;

  3. 如果服务器接受了 SYN 报⽂中的「数据」,服务器可在握⼿完成之前发送「数据」,这就减少了握⼿带来的 1 个 RTT 的时间消耗;

  4. 客户端将发送 ACK 确认服务器发回的 SYN 以及「数据」,但如果客户端在初始的 SYN 报⽂中发送的「数 据」没有被确认,则客户端将᯿新发送「数据」;

  5. 此后的 TCP 连接的数据传输过程和⾮ TFO 的正常情况⼀致。 所以,之后发起 HTTP GET 请求的时候,可以绕过三次握⼿,这就减少了握⼿带来的 1 个 RTT 的时间消耗。

开启了 TFO 功能,cookie 的值是存放到 TCP option 字段⾥的:

注:客户端在请求并存储了 Fast Open Cookie 之后,可以不断᯿复 TCP Fast Open 直⾄服务器认为 Cookie ⽆效 (通常为过期)。另外,TCP Fast Open 功能需要客户端和服务端同时⽀持,才有效果。

TCP的四次挥手过程,为什么要四次

答:因为TCP有个半关闭状态,假设A.B要释放连接,那么A发送一个释放连接报文给B,B收到后发送确认,这个时候A不发数据,但是B如果发数据A还是要接收,这叫半关闭。然后B还要发给A连接释放报文,然后A发确认,所以是4次。

主动关闭方通常有以下两种关闭方式

1. close():关闭读写,比较不优雅

2. shutdown():half-close

  • SHUT_RD:关闭读。这时应用层不应该再尝试接收数据,内核协议栈中就算接收缓冲区收到数据也会被丢弃;

  • SHUT_WR:关闭写。如果发送缓冲区中还有数据没发,会将将数据传递到目标主机;

  • SHUT_RDWR:关闭读和写。相当于 close() 了。

在tcp连接握手时为何ACK是和SYN一起发送?这里ACK却没有和FIN一起发送呢?

是因为tcp是全双工模式,接收到FIN时意味将没有数据再发来,但是还是可以继续发送数据。

tcp与udp的主要区别_与路径无关的问题

tcp与udp的主要区别_与路径无关的问题

tcp与udp的主要区别_与路径无关的问题

tcp与udp的主要区别_与路径无关的问题

有没有可能存在三次挥手?

TCP 四次挥手里,第二次和第三次挥手之间,是有可能有数据传输的(全双工模式)。第三次挥手的目的是为了告诉主动方,“被动方没有数据要发了”。

所以,在第一次挥手之后,如果被动方没有数据要发给主动方。第二和第三次挥手是有可能合并传输的。这样就出现了三次挥手。

tcp与udp的主要区别_与路径无关的问题

另外的,由于存在延迟确认的机制,TCP可以在第二次挥手过程中,同时也把数据给发送过去,由第三次挥手的ACK对数据进行确认即可,如下图

图片

什么是RST包?

RST(Connection reset by peer):用于强制关闭TCP链接。

什么情况下会发送RST包?

1)端口未打开

2)请求超时

3)提前关闭

4)在一个已关闭的socket上收到数据:比如主动关闭方是调用close方法,同时把读也给关闭了,这样主动方若再次收到对方传来的数据,会直接丢弃,并回复一个RST

        被动方内核协议栈收到了 RST,会把连接关闭。但内核连接关闭了,应用层也不知道(除非被通知)。

        此时被动方应用层接下来的操作,无非就是读或写:

  • 如果是读,则会返回 RST 的报错,也就是我们常见的 Connection reset by peer;

  • 如果是写,那么程序会产生 SIGPIPE 信号,应用层代码可以捕获并处理信号,如果不处理,则默认情况下进程会终止,异常退出。

TCP连接关闭的正常方法是四次握手。但四次握手不是关闭TCP连接的唯一方法,有时,如果主机需要尽快关闭连接,RST (Reset)包将被发送。注意,由于RST包不是TCP连接中的必须部分, 可以只发送RST包(即不带ACK标记)。但在正常的TCP连接中RST包可以带ACK确认标记。

TCP四次挥手中TIME_WAIT状态的概念及意义。

1)为了保证客户端发送的最后一个ACK报文段能够到达服务器。这个ACK报文段可能丢失,因而使处在LAST_ACK状态的服务器收不到确认。服务器会超时重传FIN+ACK报文段,客户端就能在2MSL时间内收到这个重传的FIN+ACK报文段,接着客户端重传一次确认,重启计时器。最好,客户端和服务器都正常进入到CLOSED状态。如果客户端在TIME-WAIT状态不等待一段时间,而是再发送完ACK报文后立即释放连接,那么就无法收到服务器重传的FIN+ACK报文段,因而也不会再发送一次确认报文。这样,服务器就无法按照正常步骤进入CLOSED状态。

2)防止已失效的连接请求报文段出现在本连接中。客户端在发送完最后一个ACK确认报文段后,再经过时间2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样就可以使下一个新的连接中不会出现这种旧的连接请求报文段。

为什么TIME_WAIT状态还需要等2*MSL(Max SegmentLifetime,最大分段生存期)秒之后才能返回到CLOSED状态呢?

答:因为虽然双方都同意关闭连接了,而且握手的4个报文也都发送完毕,按理可以直接回到CLOSED状态(就好比从SYN_SENT状态到ESTABLISH状态那样),但是我们必须假想网络是不可靠的,你无法保证你最后发送的ACK报文一定会被对方收到,就是说对方处于LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文。

time_wait 是「服务器端」的状态?or 「客户端」的状态?

  • RE:time_wait 是「主动关闭 TCP 连接」一方的状态,可能是「客服端」的,也可能是「服务器端」的

  • 一般情况下,都是「客户端」所处的状态;「服务器端」一般设置「不主动关闭连接」

服务器在对外服务时,是「客户端」发起的断开连接?还是「服务器」发起的断开连接?

  • 正常情况下,都是「客户端」发起的断开连接

  • 「服务器」一般设置为「不主动关闭连接」,服务器通常执行「被动关闭」

  • 但 HTTP 请求中,http 头部 connection 参数,默认为 close,则服务端处理完请求会主动关闭 TCP 连接,若为close,则服务器在响应时就会断开连接,这时在高并发的情况下,服务端产生TIME_WAIT过多的情况就很正常了。

  • 虽然HTTP默认Connection值为close,但是,现在的浏览器发送请求的时候一般都会设置Connection为keep-alive了,这个时候一般就都是客户端发起的断开了

TCP四次挥手的性能优化

主动关闭⽅的优化

主动发起 FIN 报⽂断开连接的⼀⽅,如果迟迟没收到对⽅的 ACK 回复,则会重传 FIN 报⽂,重传的次数由 tcp_orphan_retries 参数决定。

当主动⽅收到 ACK 报⽂后,连接就进⼊ FIN_WAIT2 状态,根据关闭的⽅式不同,优化的⽅式也不同:

  1. 如果这是 close 函数关闭的连接,那么它就是孤⼉连接。如果 tcp_fin_timeout 秒内没有收到对⽅的 FIN 报⽂,连接就直接关闭。同时,为了应对孤⼉连接占⽤太多的资源, tcp_max_orphans 定义了最⼤孤⼉连接的数量,超过时连接就会直接释放。

  2. 反之是 shutdown 函数关闭的连接,则不受此参数限制;

当主动⽅接收到 FIN 报⽂,并返回 ACK 后,主动⽅的连接进⼊ TIME_WAIT 状态。这⼀状态会持续 1 分钟,为了防⽌ TIME_WAIT 状态占⽤太多的资源, tcp_max_tw_buckets 定义了最⼤数量,超过时连接也会直接释放。

当 TIME_WAIT 状态过多时,还可以通过设置 tcp_tw_reuse 和 tcp_timestamps 为 1 ,将 TIME_WAIT 状态的端⼝复⽤于作为客户端的新连接,注意该参数只适⽤于客户端。

被动关闭方的优化

被动关闭的连接⽅应对⾮常简单,它在回复 ACK 后就进⼊了 CLOSE_WAIT 状态,等待进程调⽤ close 函数关闭连接。因此,出现⼤量 CLOSE_WAIT 状态的连接时,应当从应⽤程序中找问题。 当被动⽅发送 FIN 报⽂后,连接就进⼊ LAST_ACK 状态,在未等到 ACK 时,会在 tcp_orphan_retries 参数的控制下重发 FIN 报⽂。

服务器部分状态过多

服务器SYN_RCVD过多(被动连)

SYN Flood 攻击会导致此种场景

有如下解决方式

  1. 缩短超时(SYN Timeout)时间

  2. 增加最大半连接数

  3. 过滤网关防护

  4. SYN cookies技术

服务器TIME_WAIT过多(主动关)

在高并发短连接的TCP服务器上,当服务器处理完请求后立刻主动正常关闭连接。这个场景下会出现大量socket处于TIME_WAIT状态。如果客户端的并发量持续很高,此时部分客户端就会显示连接不上。

有如下解决方式

  1. 服务器端允许 time_wait 状态的 socket 被重用

  2. 缩减 time_wait 时间,设置为 1 MSL(即,2 mins)

服务器CLOSE_WAIT过多(被动关)

通常处于close_wait状态,说明服务端是被动关闭连接的,可能是某种情况下对方关闭了socket链接(有可能是超时),但是我方忙于读或者写,发送ACK后没有来得及发送FIN报文。这种场景比较可能出现在服务端存在接口耗时较长的情况,或者接口处理卡住了。

有如下解决方式

  1. 让客户端增大接口超时时间

  2. 检查服务端接口是否有卡住或处理太慢的情况

怎样使用 UDP 实现 TCP 的可靠传输

要使用 UDP 来构建可靠的面向连接的数据传输,就要实现类似于 TCP 协议的超时重传,有序接收,应答确认,校验,滑动窗口,流量控制等机制,等于说要在传输层的上一层(或者直接在应用层)实现 TCP 协议的可靠数据传输机制,比如使用 UDP 数据包+序列号,UDP 数据包+时间戳等方法,在服务器端进行应答确认机制,这样就会保证不可靠的 UDP 协议进行可靠的数据传输

TCP通过哪些措施,保证传输可靠?

TCP提供一种面向连接的、可靠的字节流服务。

    面向连接:意味着两个使用TCP的应用(通常是一个客户和一个服务器)在彼此交换数据之前必须先建立一个TCP连接。在一个TCP连接中,仅有两方进行彼此通信。广播和多播不能用于TCP。

TCP通过下列方式来提供可靠性:

1、应用数据被分割成TCP认为最适合发送的数据块。这和UDP完全不同,应用程序产生的数据报长度将保持不变。(将数据截断为合理的长度,数据块)

2、当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。 (超时重)

3、当TCP收到发自TCP连接另一端的数据,它将发送一个确认。这个确认不是立即发送,通常将推迟几分之一秒 (对于收到的请求,给出确认响应) (之所以推迟,可能是要对包做完整校验)

4、TCP将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段。 (校验出包有错,丢弃报文段,不给出响应,TCP发送数据端,超时会重发数据)(校验机制)

5、既然TCP报文段作为IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段的到达也可能会失序。如果必要,TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层。 (对失序数据进行重新排序,然后才交给应用层)

6、既然IP数据报会发生重复,TCP的接收端必须丢弃重复的数据。(对于重复数据,能够丢弃重复数据)

7、TCP还能提供流量控制。TCP连接的每一方都有固定大小的缓冲空间。TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据。这将防止较快主机致使较慢主机的缓冲区溢出。(TCP可以进行流量控制,防止较快主机致使较慢主机的缓冲区溢出)TCP使用的流量控制协议是可变大小的滑动窗口协议。

字节流服务:

两个应用程序通过TCP连接交换8bit字节构成的字节流。TCP不在字节流中插入记录标识符。我们将这称为字节流服务(bytestreamservice)。

TCP对字节流的内容不作任何解释。TCP不知道传输的数据字节流是二进制数据,还是ASCII字符、EBCDIC字符或者其他类型数据。对字节流的解释由TCP连接双方的应用层解释。

TCP的拥塞控制原理

为防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提:网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机、路由器,以及与降低网络传输性能有关的所有因素。

拥塞控制代价:需要获得网络内部流量分布的信息。在实施拥塞控制之前,还需要在结点之间交换信息和各种命令,以便选择控制的策略和实施控制。这样就产生了额外的开销。拥塞控制还需要将一些资源分配给各个用户单独使用,使得网络资源不能更好地实现共享。

几种拥塞控制方法:

慢开始(slow-start )、拥塞避免(congestion avoidance )

快重传( fastretransmit )、快恢复( fastrecovery )

一切的基础还是慢开始,这种方法的思路是这样的:

1. 发送方维持一个叫做“拥塞窗口”的变量,该变量和接收窗口共同决定了发送者的发送窗口,发送方的发送窗口的上限值应当为接收方窗口rwnd和拥塞窗口cwnd这两个变量中的较小的一个;

2. 当主机开始发送数据时,避免一下子将大量字节注入到网络,造成或者增加拥塞,选择发送一个1字节的试探报文;

3. 当收到第一个字节的数据的确认后,就发送2个字节的报文;

4. 若再次收到2个字节的确认,则发送4个字节,依次递增2的指数级;

5. 最后会达到一个提前预设的“慢开始门限ssthresh”,比如24,即一次发送了24个分组,此时遵循下面的条件判定:

     a. cwnd < ssthresh, 继续使用慢开始算法;

     b. cwnd > ssthresh,停止使用慢开始算法,改用拥塞避免算法;

     c. cwnd = ssthresh,既可以使用慢开始算法,也可以使用拥塞避免算法;

6. 所谓拥塞避免算法就是:每经过一个往返时间RTT就把发送方的拥塞窗口+1,即让拥塞窗口缓慢地增大,按照线性规律增长;

7. 当出现网络拥塞,比如丢包时,将慢开始门限ssthresh设置为出现拥塞时的发送方窗口的一半(但不能小于2),然后将cwnd设为1,执行慢开始算法(较低的起点,指数级增长);

上述方法的目的是在拥塞发生时循序减少主机发送到网络中的分组数,使得发生拥塞的路由器有足够的时间把队列中积压的分组处理完毕。慢开始和拥塞控制算法常常作为一个整体使用,而快重传和快恢复则是为了减少因为拥塞导致的数据包丢失带来的重传时间,从而避免传递无用的数据到网络。下图为TCP Tahoe(慢开始+拥塞避免)版本,实际已废弃不用

tcp与udp的主要区别_与路径无关的问题

快重传的机制是:

1. 接收方建立这样的机制,如果一个包丢失,则对后续的包继续发送针对该包的重传请求;

2. 一旦发送方接收到三个一样的确认,就知道该包之后出现了错误,立刻重传该包;

3. 此时发送方开始执行“快恢复”算法:

    a. 慢开始门限变为出现拥塞时的一半(为了预防网络发生拥塞);

    b. 拥塞窗口cwnd设为上面变化后的慢开始门限的值

    c. 执行拥塞避免算法(高起点,线性增长);

tcp与udp的主要区别_与路径无关的问题

tcp与udp的主要区别_与路径无关的问题

新的TCP Reno版本是在快重传后采用快恢复算法而不是采用慢开始算法。

在采用快恢复算法时,慢开始算法只是在TCP连接建立时网络出现超时 时才使用。

TCP的滑动窗口协议与停止等待协议的区别

滑动窗口协议中,允许发送方发送多个分组(当有多个分组可用时)而不需等待确认,但它受限于在流水线中为未确认的分组数不能超过某个最大允许数N。滑动窗口协议是TCP使用的一种流量控制方法,此协议能够加速数据的传输。只有在接收窗口向前滑动时(与此同时也发送了确认),发送窗口才有可能向前滑动。

收发两端的窗口按照以上规律不断地向前滑动,因此这种协议称为滑动窗口协议。

当发送窗口和接收窗口的大小都等于1时,就是停止等待协议。

所谓滑动窗口协议,自己理解有两点:

1.“窗口”对应的是一段可以被发送者发送的字节序列,其连续的范围称之为“窗口”;

2.“滑动”则是指这段“允许发送的范围”是可以随着发送的过程而变化的,方式就是按顺序“滑动”。在引入一个例子来说这个协议之前,我觉得很有必要先了解以下前提:

    a. TCP协议的两端分别为发送者A和接收者B,由于是全双工协议,因此A和B应该分别维护着一个独立的发送缓冲区和接收缓冲区,由于对等性(A发B收和B发A收),我们以A发送B接收的情况作为例子;

    b. 发送窗口是发送缓存中的一部分,是可以被TCP协议发送的那部分,其实应用层需要发送的所有数据都被放进了发送者的发送缓冲区;

    c. 发送窗口中相关的有四个概念:已发送并收到确认的数据(不发送窗口和发送缓冲区之内)、已发送但未收到确认的数据(位于发送窗口之中)、允许发送但尚未发送的数据以及发送窗口外发送缓冲区内暂时不允许发送的数据;

    d. 每次成功发送数据之后,发送窗口就会在发送缓冲区中按顺序移动,将新的数据包含到窗口中准备发送;

 TCP建立连接的初始,B会告诉A自己的接收窗口rwnd大小,比如为‘20’:

 字节31-50为发送窗口

tcp与udp的主要区别_与路径无关的问题

 A发送11个字节后,发送窗口位置不变,B接收到了乱序的数据分组,如下图

tcp与udp的主要区别_与路径无关的问题

只有当A成功发送了数据,即发送的数据得到了B的确认之后,才会移动滑动窗口离开已发送的数据;同时B则确认连续的数据分组,对于乱序的分组则先接收下来,避免网络重复传递:

tcp与udp的主要区别_与路径无关的问题

TCP流量控制原理

tcp与udp的主要区别_与路径无关的问题

1.概述

     所谓的流量控制就是让发送方的发送速率不要太快,让接收方来得及接。利用滑动窗口机制可以很方便的在TCP连接上实现对发送方的流量控制。TCP的窗口单位是字节,不是报文段,发送方的发送窗口不能超过接收方给出的接收窗口的数值。接收方在返回的ACK中会包含自己的接收窗口的大小,以控制发送方的数据发送。

tcp与udp的主要区别_与路径无关的问题

如上图所示A向B发送数据。在连接建立时,B告诉A接收窗口rwnd(receiver window)= 400,单位字节,因此发送方A的发送窗口不能400。

(可以看出,B向A发送的三个报文段都设置了 ACK = 1以保证字段有效,后面的rwnd值就是接收方对发送方的三次流量控制。)

如图所示,说明了利用可变窗口大小进行流量控制。设主机A向主机B发送数据。双方确定的窗口值是400.再设每一个报文段为100字节长,序号的初始值为seq=1,图中的箭头上面大写ACK,表示首部中的确认字段为ACK,小写ack表示确认字段的值。

     接收方的主机B进行了三次流量控制。第一次把窗口设置为rwnd=300,第二次减小到rwnd=100最后减到rwnd=0,即不允许发送方再发送过数据了。这种使发送方暂停发送的状态将持续到主机B重新发出一个新的窗口值为止。

     假如,B向A发送了零窗口的报文段后不久,B的接收缓存又有了一些存储空间。于是B向A发送了rwind=400的报文段,然而这个报文段在传送中丢失了。A一直等待收到B发送的非零窗口的通知,而B也一直等待A发送的数据。这样就死锁了。为了解决这种死锁状态,TCP为每个连接设有一个持续计时器。只要TCP连接的一方收到对方的零窗口通知,就启动持续计时器,周期性的发送一个零窗口探测报文段。对方就在确认这个报文的时候给出 现在的窗口大小(注意:TCP规定,即使设置为零窗口,也必须接收以下几种报文段:零窗口探测报文段、确认报文段和携带紧急数据的报文段)。

2.TCP报文段发送时机的选择

     TCP报文段发送时机主要有以下几种选择途径。

     1)TCP维持一个变量,它等于最大报文段长度MSS,只要缓存中存放的数据达到MSS字节就组装成一个TCP报文段发送出去。

     2)由发送方的应用程序指明要求发送报文段,即TCP支持的推送操作

     3)是发送方的一个计时器期限到了,这时就把当前已有的缓存数据装入报文段发送出去。

传递效率的问题(避免大量TCP ⼩数据报⽂的传输):

一个显而易见的问题是:单个发送字节单个确认,和窗口有一个空余即通知发送方发送一个字节,无疑增加了网络中的许多不必要的报文(请想想为了一个字节数据而添加的40字节头部吧!),所以我们的原则是尽可能一次多发送几个字节,或者窗口空余较多的时候通知发送方一次发送多个字节。

对于前者(尽可能多发)我们广泛使用Nagle算法,即:

1. 若发送应用进程要把发送的数据逐个字节地送到TCP的发送缓存,则发送方就把第一个数据字节先发送出去,把后面的字节先缓存起来;(拥塞控制)

2. 当发送方收到第一个字节的确认后(也得到了网络情况和对方的接收窗口大小),再把缓冲区的剩余字节组成合适大小的报文发送出去;

3. 没有已发送未确认报⽂时,⽴刻发送数据。

4. 存在未确认报⽂时,直到「没有已发送未确认报⽂」或「数据⻓度达到 MSS ⼤⼩」时,再发送数据。

对于后者(尽可能多接收)我们往往的做法是让接收方等待一段时间,或者接收方获得足够的空间容纳一个报文段或者等到接收缓存有一半空闲的时候,再通知发送方发送数据。

tcp与udp的主要区别_与路径无关的问题

Nagle 算法⼀定会有⼀个⼩报⽂,也就是在最开始的时候。

Nagle 算法默认是打开的,如果对于⼀些需要⼩数据包交互的场景的程序,⽐如,telnet 或 ssh 这样的交互 性⽐较强的程序,则需要关闭 Nagle 算法。

TCP 半连接队列和全连接队列

在 TCP 三次握⼿的时候,Linux 内核会维护两个队列,分别是

1. 半连接队列,也称 SYN 队列

2. 全连接队列,也称 accepet 队列;

服务端收到客户端发起的 SYN 请求后,内核会把该连接存储到半连接队列,并向客户端响应 SYN+ACK,接着客 户端会返回 ACK,服务端收到第三次握⼿的 ACK 后,内核会把连接从半连接队列移除,然后创建新的完全的连 接,并将其添加到 accept 队列,等待进程调⽤ accept 函数时把连接取出来。

tcp与udp的主要区别_与路径无关的问题

不管是半连接队列还是全连接队列,都有最⼤⻓度限制,超过限制时,内核会直接丢弃,或返回 RST 包。

TCP的粘包与拆包问题

TCP的粘包和拆包问题往往出现在基于TCP协议的通讯中,比如RPC框架、Netty等。

为什么UDP没有粘包?

粘包拆包问题在数据链路层、网络层以及传输层都有可能发生。日常的网络应用开发大都在传输层进行,由于UDP有消息保护边界(数据报),不会发生粘包拆包问题,因此粘包拆包问题只发生在TCP协议中。

TCP是面向流,没有边界,而操作系统在发送TCP数据时,会通过缓冲区来进行优化,例如缓冲区为1024个字节大小。

如果一次请求发送的数据量比较小,没达到缓冲区大小,TCP则会将多个请求合并为同一个请求进行发送,这就形成了粘包问题。

如果一次请求发送的数据量比较大,超过了缓冲区大小,TCP就会将其拆分为多次发送,这就是拆包。

tcp与udp的主要区别_与路径无关的问题

常见的解决方案

对于粘包和拆包问题,常见的解决方案有四种:

  • 发送端将每个包都封装成固定的长度,比如100字节大小。如果不足100字节可通过补0或空等进行填充到指定长度;
  • 发送端在每个包的末尾使用固定的分隔符,例如\r\n。如果发生拆包需等待多个包发送过来之后再找到其中的\r\n进行合并;例如,FTP协议;
  • 将消息分为头部和消息体,头部中保存整个消息的长度,只有读取到足够长度的消息之后才算是读到了一个完整的消息;
  • 通过自定义协议进行粘包和拆包的处理。

Netty对粘包和拆包问题的处理

Netty对解决粘包和拆包的方案做了抽象,提供了一些解码器(Decoder)来解决粘包和拆包的问题。如:

  • LineBasedFrameDecoder:以行为单位进行数据包的解码;
  • DelimiterBasedFrameDecoder:以特殊的符号作为分隔来进行数据包的解码;
  • FixedLengthFrameDecoder:以固定长度进行数据包的解码;
  • LenghtFieldBasedFrameDecode:适用于消息头包含消息长度的协议(最常用);

基于Netty进行网络读写的程序,可以直接使用这些Decoder来完成数据包的解码。对于高并发、大流量的系统来说,每个数据包都不应该传输多余的数据(所以补齐的方式不可取),LenghtFieldBasedFrameDecode更适合这样的场景。

小结

TCP协议粘包拆包问题是因为TCP协议数据传输是基于字节流的,它不包含消息、数据包等概念,需要应用层协议自己设计消息的边界,即消息帧(Message Framing)。如果应用层协议没有使用基于长度或者基于终结符息边界等方式进行处理,则会导致多个消息的粘包和拆包。

虽然很多框架中都有现成的解决方案,比如Netty,但底层的原理我们还是要清楚的,而且还要知道有这么会事,才能更好的结合场景进行使用。

扩展阅读

硬不硬你说了算!近 40 张图解被问千百遍的 TCP 三次握手和四次挥手面试题TCP 学起来很轻松的,就是头冷。https://mp.weixin.qq.com/s/tH8RFmjrveOmgLvk9hmrkw被印证的感觉,真爽!神一般的抓包图https://mp.weixin.qq.com/s/veUBOnHkxiVbTDSFo_y5qw

今天的文章tcp与udp的主要区别_与路径无关的问题分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/64692.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注