TCP协议上

回顾和整理以下 TCP 的基础知识,下面是我在极客时间的课程上学的,好久不用感觉忘没了。

我整理了以下TCP 协议的学习路径,首先要记住TCP 协议的特点:保证顺序、丢包重传、连接维护、流量控制、拥塞控制。还要记一下TCP 的包头格式,它都包含了哪些信息。

记住了特点之后,问自己为什么TCP 协议会有这些特点,带着这个问题再逐步往后学习更容易理解。包头里有原宿端口,从哪来到哪去。序号和确认序号,保证顺序,丢包重传。连接维护,状态位是TCP协议客户端和服务端都有的连接状态,这也解释了连接时三次握手,和断开时四次挥手。流量控制和拥塞控制,TCP用了滑动窗口解决。

  • TCP包头格式
    1、源端口号和目标端口号,是不可少的,没有端口号就不知道数据应该给哪个应用(或进程)。
    2、包的序号,包没有序号就会出现乱序的问题,编号用来确认哪个包先来,哪个包后到。
    3、确认序号,发出去的包要有确认,不然就不知道对方有没有收到。
    对TCP层来讲,IP层你丢不丢包,我管不着,但是在TCP我会保证可靠性。
  • 状态位:SYN是发起一个连接,ACK是回复,RST是重新连接,FIN是结束连接等。TCP是面向连接的,因而双方要维护连接的状态,这些状态位的包的发送,会引起双方的状态变更。
  • 窗口大小:TCP要做流量控制,通信双方各声明一个窗口,标识当前能够处理的能力,别发送太快,撑死我,也别发送太慢,饿死我。
  • TCP还会做拥塞控制,对于真正的通路堵车不堵车,它无能为力,它能做的是控制发送的速度
  • TCP头的解析,重点掌握:
    1、顺序问题,稳重不乱
    2、丢包问题,承诺靠谱
    3、连接维护,有始有终
    4、流量控制,把握分寸
    5、拥塞控制,知进知退
  • TCP的三次握手
    TCP的连接建立,我们通常称为三次握手
    A:您好,我是A
    B:您好A,我是B
    A:您好B
    我们也常称为”请求 -> 应答 -> 应答之应答“的三个回合。只要保证双方的消息都有去有回,就基本可以了。
    建立连接后如果A一直不发包,建立连接后空着。在程序设计的时候要求开启keepalive机制,即使没有真实的数据包,也有探活包。
  • 三次握手除了建立连接外,还解决了TCP包的序号问题。
    A要告诉B,A这面发起的包的序号起始是从哪个号开始的,B也要同样告诉A,B这面发起的包是从哪个号开始的。
    但序号不是从1开始的,因为这样往往会出现冲突。每个连接都要有不同的序号,这个序号的起始序号是随着时间变化的,可以看成一个32位的计数器,每4ms加1,计算一下,如果到重复,需要四个多小时。
    IP包头里有个TTL,也即生存时间。
  • 双方为了维护这个连接,要维护一个状态机,在建立连接的过程中,双方的状态变化时序图如下:
    一开始,客户端和服务端都处于 CLOSED 状态。
    先是服务端主动监听某个端口,处于 LISTEN 状态。
    然后客户端主动发起连接SYN,之后处于 SYN-SENT 状态。
    服务端收到发起的连接,返回 SYN,并且 ACK 客户端的 SYN,之后处于SYN-RCVD 状态。
    客户端收到服务端发送的 SYN 和 ACK 之后,发送 ACK 的 ACK,之后处于 ESTABLISHED 状态,因为它一发一收成功了。
    服务端收到 ACK 的 ACK 之后,处于 ESTABLISHED 状态,因为它也一发一收了。
  • TCP 四次挥手
    ”好说好撒“,这常被称为四次挥手。
    A:B啊,我不想玩了。
    B:哦,你不想玩了啊,我知道了。
    这个时候只是A不想玩了,A不再给B发数据了。但是B没说不想玩啊,B没有做完自己的事情,还是可以发数据给A的,所以称为半关闭状态。
    这个时候A可以选择不再接收数据,也可以选择最后再接收一段数据,等B也主动关闭。
    B:A啊,好吧,我也不想玩了,拜拜。
    A:好的,拜拜。
    这样整个连接就关闭了。这是和平分手的情况。
    撕破脸的情况:
    一种是,A说”不想玩了“之后,直接跑路,是会有问题的,因为B还没有发起结束,就算B发起结束,也得不到回答。
    另一种是,A说”不想玩了“,B直接跑路,也是有问题的,因为A不知道B是还有数据要发送,还是过一会发送结束,这时候A会重新发送”不玩了“。
    TCP协议专门设计了几个状态来处理这些问题。我们来看断开连接的时候的状态时序图。
    断开的时候,我们可以看到,当 A 说“不玩了”,就进入 FIN__WAIT_1 的状态,B 收到“A 不玩”的消息后,发送知道了,就进入 CLOSE_WAIT 的状态。
    A 收到“B 说知道了”,就进入 FIN_WAIT_2 的状态,如果这个时候 B 直接跑路,则 A 将永远在这个状态。TCP 协议里面并没有对这个状态的处理,但是 Linux 有,可以调整 tcp_fin_timeout 这个参数,设置一.超时时间。
  • TIME_WAIT的作用 1
    如果 B 没有跑路,发送了“B 也不玩了”的请求到达 A 时,A 发送“知道 B 也不玩了”的 ACK 后,从 FIN_WAIT_2 状态结束,按说 A 可以跑路了,但是最后的这个 ACK 万一 B 收不到呢?则 B 会重新发一个“B 不玩了”,这个时候 A 已经跑路了的话,B就再也收不到 ACK 了,因而 TCP 协议要求 A 最后等待一段时间 TIME_WAIT,这个时间要足够长,长到如果B 没收到 ACK 的话,“B 说不玩了”会重发的,A 会重新发一个 ACK 并且足够时间到达 B。
  • TIME_WAIT的作用 2
    A 直接跑路还有一个问题是,A 的端口就直接空出来了,但是B 不知道,B 原来发过的很多包很可能还在路上,如果A 的端口被一个新的应用占用了,这个新的应用会收到上个连接中B 发过来的包,虽然序列号是重新生成的,但是这里要上一个双保险,防止产生混乱,因而也需要等足够长的时间,等到原来 B 发送的所有的包都死翘翘,再空出端口来。
  • 报文最大生存时间
    等待的时间设为 2MSL,MSL是Maximum Segment Lifetime,报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。因为 TCP 报文基于是 IP 协议的,而 IP 头中有一个TTL 域,是 IP 数据报可以经过的最大路由数,每经过一个处理他的路由器此值就减 1,当此值为 0 则数据报将被丢弃,同时发送ICMP报文通知源主机。协议规定MSL为2分钟,实际应用中常用的是30秒,1分钟和两分钟等。
    还有一个异常情况是,B 超过了 2MSL 的时间,依然没有收到它发的 FIN 的ACK,怎么办呢?按照 TCP 的原理,B 当然还会重发 FIN,这个时候 A 再收到这个包之后,A 就表示,我已经在这里等了这么长时间了,已经仁至义尽了,之后的我就都不认了,于是就直接发送 RST,B 就知道 A 早就跑了.
  • TCP状态机 将连接建立和连接断开的两个时序图综合起来,就是著名的TCP的状态机。将这个状态机和时序状态机对照着看,不然容易晕。
    这个图种,加黑加粗的部分,是上面说的主要流程,其中阿拉伯数字的序号,是连接过程中的顺序,而大写中文数字的序号,是连接断开过程中的顺序。加粗的实线是客户A的状态变迁,加粗的虚线是服务端B的状态变迁。不加粗虚线是其他非主流过程。
  • 小结
    • TCP包头复杂,注意五个问题。
    • 连接建立三次握手,断开时四次挥手。
  • 思考
    • 如何在系统中查看某个连接的状态?
      可以用 netstat 或者 lsof 命令 grep 一下 establish listen close_wait 等这些查看
    • 为了维护连接的状态,还有其他的数据结构来处理其他的四个问题,知道是什么吗?
 

posted on 2019-08-26 20:39  拾掇的往昔  阅读(179)  评论(0编辑  收藏  举报

导航