你想要的TCP要点,这里全都有







1 TCP特点


①TCP是面向连接的,一个应用进程在向另一个进程发送数据前必须先建立连接,发送某些预备报文段。


②TCP提供全双工服务,允许通信双方的应用进程在任何时候发送数据。TCP连接的两端都有发送缓存和接收缓存:发送时,应用程序把数据传送给 TCP 缓存后就可以做自己的事,TCP在合适的时候发送;接收时,TCP把收到的数据放入缓存,应用程序在合适的时候读取。


③TCP连接是点对点的,只能是单个发送方和单个接收方之间的连接。


④TCP提供可靠的交付服务,通过TCP传送的数据无差错、不丢失、不重复,按序到达。


⑤TCP是面向字节流的,流是指流入进程或从进程流出的字节序列。虽然应用程序和TCP的交互是每次一个数据块,但TCP把数据块仅看成一连串无结构的字节流。TCP不保证接收方的数据块和发送方的数据块具有对应大小的关系,但接收方的字节流必须和发送方的字节流完全一样。应用程序必须有能力识别收到的字节流,把它还原成应用层数据。


2 TCP和UDP的区别


①UDP无连接,发送数据前不需要建立连接,减少了开销和时延。


②UDP使用尽最大努力交付,不保证可靠性,主机不需要维持复杂的连接状态。


③UDP面向报文,UDP对应用层报文添加首部后就交付IP层。


④UDP没有拥塞控制,网络拥塞不会降低源主机的发送速率,这对某些实时应用很重要,如视频会议。


⑤UDP支持一对一、一对多和多对多通信。


3 TCP报文结构


TCP报文段分为首部和数据两部分。首部的前20个字节固定,后面有4n字节根据需要增加。


你想要TCP要点,这里全都有_java


4 自动重传请求ARQ


ARQ包括停止等待协议、回退N步协议和选择重传协议,后两种结合了窗口机制,属于连续ARQ协议。


4.1 停止等待协议


每发送完一个分组就停止发送,等待对方确认,在收到确认后再发送下一个分组。包括三种情况:


①无差错

A发送分组M1,发送完后暂停并等待B的确认;B收到M1后向A发送确认;A收到确认后再发送下一个分组M2。


②出现差错

B收到M1后检测到了差错,或者M1在传输过程中丢失,这两种情况下B都不会发送确认信息,解决方法是:A只要超过一段时间没有收到确认,就进行超时重传,每发送完一个分组就设置超时计时器,如果在计时器到期前收到确认就撤销计时。

注意:①发送完分组后必须暂时保留副本,收到确认再清除。②分组和确认分组都必须进行编号。③超时时间应当比分组传输的往返时间稍长,过短会产生不必要的重传,过长会降低通信效率。


③确认丢失和确认迟到

B发送的确认丢失,A会超时重传,B会丢弃重传分组并重新确认;B发送的确认迟到,A收到重复确认后将其丢弃。

通常A最终总是可以收到对所有发出分组的确认,如果A不断重传分组但总收不到确认,就说明通信线路质量太差,不能通信。

停止等待协议的优点是简单,缺点是信道利用率低。为了提高传输效率,发送方可以连续发送多个分组,不必每发送完一个分组就停下来等待确认,使信道上一直有数据传送。但流水线传输可能会遇到差错,解决方法包括回退N步和选择重传。


4.2 回退N步协议


回退N步,允许发送方发送多个分组而不需要等待确认。回退N步中发送方已发送但还未确认的序号和允许发送但还未发送的序号可以被看作一个长度为N的窗口,随协议运行该窗口向前滑动,因此回退N步也被称为滑动窗口协议。


回退N步采用累积确认的方式,对按序到达的最后一个分组发送确认,如果超时,发送方会重传所有已发送但还未确认的分组。例如发送了序号为1--5的五个分组,除了第3个全部收到了,那么确认序号就是2,发送方将重传3--5的分组。


在回退N步协议中,接收方丢弃所有失序分组,因为接收方必须按序交付数据。这种做法的优点是缓存简单,不需要缓存任何失序分组;缺点是对失序分组的重传可能出错而导致更多重传。


4.3 选择重传协议


回退N步中单个分组的差错就能引起大量分组重传,随着信道差错率的增加,流水线会被不必要重传的分组所充斥。


选择重传,让发送方仅重传那些它怀疑接收出错的分组,避免不必要的重传。接收方将确认一个正确接收的分组而不管其是否按序,失序分组将被缓存直到收到所有丢失分组,此时将分组按序交付上层。


5 TCP可靠原理


TCP的可靠传输包含很多机制,例如使用检验和来检测传输中的比特错误、使用定时器超时重传、使用序号检测丢失分组和冗余副本、使用确认号告诉发送方确认的分组信息。


数据分块:应用数据被分割成TCP认为最适合发送的数据块。


序列号:TCP给发送的每一个包进行编号,接收方对数据包进行排序,把有序数据传送给应用层。


校验和:TCP将保持它首部和数据的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段。


停止等待协议:每发完一个分组就停止发送,等待对方确认,在收到确认后再发下一个分组。如果不能及时收到一个确认,将重发这个报文段(超时重传),并且TCP的接收端会丢弃重复的数据。


流量控制:TCP连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失,TCP利用滑动窗口实现流量控制。


拥塞控制:当网络拥塞时,减少数据的发送。


6 滑动窗口


滑动窗口以字节为单位。发送端有一个发送窗口,窗口中的序号是允许发送的序号,窗口的后沿是已发送且确认的序号,窗口的前沿是不允许发送的序号。窗口的后沿可能不动(没有收到新的确认),也有可能前移(收到了新的确认),但不会后移(不可能撤销已经确认的数据)。窗口的前沿一般是向前的,可能不动(没有收到新的请求或对方的接收窗口变小),也可能收缩(TCP强烈不建议这么做,因为发送端在收到通知前可能已经发送了很多数据,将产生错误)。


发送缓存存放应用程序传给TCP准备发送的数据和已发送但还未确认的数据;接收缓存存放按序到达但尚未被应用程序读取的数据和未按序到达的数据。


发送窗口根据接收窗口设置,但并不总是一样大,还要根据网络的拥塞情况调整。


接收方必须有累积确认功能,既可以在合适的时候确认,也可以在发送数据时捎带确认,但不能过分推迟,一般不超过0.5秒。


7 拥塞控制


网络中对资源的需求超过可用量的情况就叫拥塞,当吞吐量明显小于理想吞吐量时就出现了轻度拥塞。拥塞控制就是减少注入网络的数据,减轻路由器和链路的负担,这是一个全局性问题,涉及网络中的所有路由器和主机,而流量控制是一个端到端的问题。


根据网络层是否为拥塞控制提供显式帮助,将拥塞控制分为:端到端拥塞控制网络辅助的拥塞控制。TCP使用端到端的拥塞控制,因为IP层不会向端系统提供显式的拥塞反馈。TCP让发送方根据拥塞程度限制发送速率。如果发送方感知到没什么拥塞会增加发送速率,否则会降低发送速率。限制发送速率通过拥塞窗口实现,判断拥塞通过超时或连续接收到3个冗余ACK实现。


TCP的拥塞控制算法包括了慢启动、拥塞避免、快重传和快恢复。慢启动和拥塞避免是TCP的强制部分,差异在于对收到的ACK做出反应时拥塞窗口增加的方式,慢启动比拥塞避免增加得更快。快恢复是推荐部分,对TCP发送方不是必须的。


7.1 慢启动


拥塞窗口cwnd以一个最大报文段MSS开始,每当传输的报文段首次被确认就增加一个MSS。因此每经过一个往返时间RTT,拥塞窗口就会翻倍,发送速率也会翻倍。


结束慢启动的情况:①发生超时事件,发送方将cwnd设为1,重新开始慢启动,并将慢启动阈值设置为cwnd/2。②当拥塞窗口达到慢启动阈值时就结束慢启动而进入拥塞避免模式。③如果检测到三个冗余的ACK,TCP就会执行快重传并进入快恢复状态。


7.2 拥塞避免


一旦进入拥塞避免状态,cwnd值大约是上次拥塞时的1/2,距离拥塞并不遥远。因此TCP不会每经过一个RTT就将cwnd翻倍,而是较为保守地在每个RTT后将cwnd加1。


发生超时事件时,拥塞避免和慢启动一样,将cwnd设为1,并将慢启动阈值设置为cwnd/2。


7.3 快重传


快重传要求接收方在收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方)而不要等到自己发送数据时捎带确认。快重传算法规定,发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待设置的重传计时器时间到期。


7.4 快恢复


快重传配合使用的还有快恢复算法,当发送方连续收到三个重复确认时,就执行“乘法减小”算法,把ssthresh门限减半,但是接下去并不执行慢开始算法:因为如果网络出现拥塞的话就不会收到好几个重复的确认,所以发送方现在认为网络可能没有出现拥塞。所以此时不执行慢开始算法,而是将cwnd设置为ssthresh的大小,然后执行拥塞避免算法。


8 TCP三次握手


8.1 三次握手过程


就是建立TCP连接,就是指建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立。在socket编程中,这一过程由客户端执行connect来触发,整个流程如下图所示:


你想要TCP要点,这里全都有_java_02


第一次握手:Client将标志位SYN置为1,随机产生一个值seq=x,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。


第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack= x+1,随机产生一个值seq=y,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。


第三次握手:Client收到确认后,检查ack是否为x+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=y+1,并将该数据包发送给Server,Server检查ack是否为y+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。


8.2 为什么客户端还要发送第三次握手确认


情况1:客户端发送连接请求报文,服务器端发出确认报文,如果该报文丢失了,客户端并未受到确认报文,不会发送其他数据,而服务器端认为连接已经建立,一直等待客户端发送消息,服务器端的资源就浪费了。


情况2:客户端发送连接请求报文,由于网络时延滞留了,客户端连接释放以后才到达服务器端,这是一个已失效的报文段,此时服务器端发出确认,而客户端不会作出反应,而服务器端认为连接已经建立,一直等待客户端发送消息,服务器端的资源就浪费了。


8.3 如果已经建立了连接,但是客户端突然出现故障了怎么办


TCP设有一个保活计时器,服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。


9 TCP四次挥手


9.1 四次挥手过程


终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发,整个流程如下图所示:


你想要TCP要点,这里全都有_java_03


由于TCP连接时全双工的,因此每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭,上图描述的即是如此。


第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。


第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。


第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。


第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。


9.2 连接半关闭状态


客户端发送完数据,会发送一个TCP连接终止报文,服务器收到该连接释放报文,回复确认报文,客户端接收到该确认报文进入连接半关闭状态(客户端只能接收数据,不能发送数据的状态)。


9.3 客户端等待2MSL原因


第一,保证客户端发送的最后一个ACK报文能够到达服务器,因为这个ACK报文可能丢失,站在服务器的角度看来,我已经发送了FIN+ACK报文请求断开了,客户端还没有给我回应,应该是我发送的请求断开报文它没有收到,于是服务器又会重新发送一次,而客户端就能在这个2MSL时间段内收到这个重传的报文,接着给出回应报文,并且会重启2MSL计时器。


第二,防止类似与“三次握手”中提到了的“已经失效的连接请求报文段”出现在本连接中。客户端发送完最后一个确认报文后,在这个2MSL时间中,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样新的连接中不会出现旧连接的请求报文。


9.4 TIME-WAIT问题


在高并发短连接的TCP服务器上,服务器处理完请求后立刻主动关闭连接,该场景下大量socket处于TIME-WAIT状态。TIME-WAIT状态无法真正释放句柄资源,socket使用的本地端口在默认情况下不能再被使用,会限制有效连接数量,成为性能瓶颈。


解决:调小tcp_fin_timeout的值、将tcp_tw_reuse设为1开启重用,将tcp_tw_recycle设为1开启快速回收。


10 TCP粘包


10.1 Tcp Socket通信的底层原理


你想要TCP要点,这里全都有_java_04


socket通信过程如图所示:首先客户端将发送内容通过send()方法将内容发送到客户端计算机的内核区,然后由操作系统将内容通过底层路径发送到服务器端的内核区,然后由服务器程序通过recv()方法从服务器端计算机内核区取出数据。即send方法和recv方法都是操作自己机器的内核区。


10.2 什么是TCP粘包问题


TCP粘包就是指发送方发送的若干包数据到达接收方时粘成了一包,从接收缓冲区来看,后一包数据的头紧接着前一包数据的尾,出现粘包的原因是多方面的,可能是来自发送方,也可能是来自接收方。


10.3 造成TCP粘包的原因


(1)发送方原因

为了减少网络中报文段的数量,TCP使用Nagle算法,Nagle算法会收集多个小分组,在一个确认到来时一起发送,因此造成发送方可能会出现粘包问题。


(2)接收方原因

TCP接收到数据包时,将接收到的数据包保存在接收缓存里,然后应用程序主动从缓存读取收到的分组。如果TCP接收数据包到缓存的速度大于应用程序从缓存中读取数据包的速度,多个包就会被缓存,应用程序就有可能读取到多个首尾相接粘到一起的包。


10.4 什么时候需要处理粘包现象


如果发送方发送的多组数据本来就是同一块数据的不同部分,比如说一个文件被分成多个部分发送,这时不需要处理粘包现象。

如果多个分组毫不相干,甚至是并列关系,那么就需要处理粘包现象。


10.5 如何处理粘包现象


对于第一种粘包产生方式可以在两次send()直接使用recv()来阻止连续发送的情况发生。


由于产生粘包的原因是接收方的无边界接收,因此发送端可以在发送数据之前向接收端告知发送内容的大小即可


10.6 UDP会不会产生粘包问题?


TCP为了保证可靠传输并减少额外的开销(每次发包都要验证),采用了基于流的传输,基于流的传输不认为消息是一条一条的,是无保护消息边界的(保护消息边界:指传输协议把数据当做一条独立的消息在网上传输,接收端一次只能接受一条独立的消息)。


UDP则是面向消息传输的,是有保护消息边界的,接收方一次只接受一条独立的信息,所以不存在粘包问题。


举个例子:有三个数据包,大小分别为2k、4k、6k,如果采用UDP发送的话,不管接受方的接收缓存有多大,我们必须要进行至少三次以上的发送才能把数据包发送完,但是使用TCP协议发送的话,我们只需要接受方的接收缓存有12k的大小,就可以一次把这3个数据包全部发送完毕。



END

你想要TCP要点,这里全都有_java_05




你想要TCP要点,这里全都有_java_06你想要TCP要点,这里全都有_java_07

扫描二维码获取更多精彩