TCP传输

看过太多tcp相关文章,可是看完老是不过瘾,似懂非懂,反复考虑事后,我以为是那些文章太过理论,看起来没有体感,因此吸取不了。 但愿这篇文章能作到言简意赅,帮助你们透过案例来理解原理。
java

tcp的特色

这个你们基本都能说几句,面试的时候候选人也确定会告诉你这些:mysql

  • 三次握手
  • 四次挥手
  • 可靠链接
  • 丢包重传

可是我只但愿你们记住一个核心的:tcp是能够可靠传输协议,它的全部特色都为这个可靠传输服务面试

那么tcp是怎么样来保障可靠传输呢?

tcp在传输过程当中都有一个ack,接收方经过ack告诉发送方收到那些包了。这样发送方能知道有没有丢包,进而肯定重传。sql

tcp建链接的三次握手

来看一个java代码链接数据库的三次握手过程数据库

image.pngimage.pngtcp

三个红框表示创建链接的三次握手:性能

  • 第一步:client 发送 syn 到server 发起握手;
  • 第二步:server 收到 syn后回复syn+ack给client;
  • 第三步:client 收到syn+ack后,回复server一个ack表示收到了server的syn+ack(此时client的48287端口的链接已是established)

握手的核心目的是告知对方seq(绿框是client的初始seq,蓝色框是server 的初始seq),对方回复ack(收到的seq+包的大小),这样发送端就知道有没有丢包了。优化

握手的次要目的是告知和协商一些信息,图中黄框。spa

  • MSS–最大传输包
  • SACK_PERM–是否支持Selective ack(用户优化重传效率)
  • WS–窗口计算指数(有点复杂的话先不用管)

这就是tcp为何要握手创建链接,就是为了解决tcp的可靠传输。3d

tcp断开链接的四次挥手

再来看java连上mysql后,执行了一个SQL: select sleep(2); 而后就断开了链接

image.pngimage.png

四个红框表示断开链接的四次挥手:

  • 第一步: client主动发送fin包给server
  • 第二步: server回复ack(对应第一步fin包的ack)给client,表示server知道client要断开了
  • 第三步: server发送fin包给client,表示server也能够断开了
  • 第四部: client回复ack给server,表示既然双发都发送fin包表示断开,那么就真的断开吧

为何握手三次、挥手四次

这个问题太恶心,面试官太喜欢问,其实他也许只能背诵:由于……。

我也不知道怎么回答。网上都说tcp是双向的,因此断开要四次。可是我认为建链接也是双向的(双向都协调告知对方本身的seq号),为何不须要四次握手呢,因此网上说的不必定精准。

你再看三次握手的第二步发 syn+ack,若是拆分红两步先发ack再发syn彻底也是能够的(效率略低),这样三次握手也变成四次握手了。

看起来挥手的时候多一次,主要是收到第一个fin包后单独回复了一个ack包,若是能回复fin+ack那么四次挥手也就变成三次了。 来看一个案例:

image.pngimage.png

图中第二个红框就是回复的fin+ack,这样四次挥手变成三次了(若是一个包就是一次的话)。

个人理解:之因此绝大数时候咱们看到的都是四次挥手,是由于收到fin后,知道对方要关闭了,而后OS通知应用层要关闭啥的,这里应用层可能须要作些准备工做,有一些延时,因此先回ack,准备好了再发fin 。 握手过程没有这个准备过程因此能够当即发送syn+ack。

ack=seq+len

ack老是seq+len(包的大小),这样发送方明确知道server收到那些东西了。

可是特例是三次握手和四次挥手,虽然len都是0,可是syn和fin都要占用一个seq号,因此这里的ack都是seq+1。

image.pngimage.png

看图中左边红框里的len+seq就是接收方回复的ack的数字,表示这个包接收方收到了。而后下一个包的seq就是前一个包的len+seq,依次增长,一旦中间发出去的东西没有收到ack就是丢包了,过一段时间(或者其余方式)触发重传,保障了tcp传输的可靠性。

三次握手中协商的其它信息

MSS 最大一个包中能传输的信息(不含tcp、ip包头),MSS+包头就是MTU(最大传输单元),若是MTU过大可能在传输的过程当中被卡住过不去形成卡死(这个大小的包一直传输不过去),跟丢包还不同。

SACK_PERM 用于丢包的话提高重传效率,好比client一次发了一、二、三、四、5 这5个包给server,实际server收到了 一、三、四、5这四个包,中间2丢掉了。这个时候server回复ack的时候,都只能回复2,表示2前面全部的包都收到了,给我发第二个包吧,若是server 收到三、四、5仍是没有收到2的话,也是回复ack 2而不是回复ack 三、四、五、6的,表示快点发2过来。

可是这个时候client虽然知道2丢了,而后会重发2,可是不知道三、四、5有没有丢啊,实际三、四、5 server都收到了,若是支持sack,那么能够ack 2的时候同时告诉client 三、四、5都收到了,这样client重传的时候只重传2就能够,若是没有sack的话那么可能会重传二、三、四、5,这样效率就低了。

来看一个例子:

image.pngimage.png

图中的红框就是SACK。

知识点:ack数字表示这个数字前面的数据收到了。

总结下

tcp全部特性基本上核心都是为了可靠传输这个目标来服务的,而后有一些是出于优化性能的目的。

后续但愿再经过几个案例来深化一下上面的知识。

相关文章
相关标签/搜索