前言: 本文内容以菜鸟教程、图解网络为基本,一些图片和介绍也是引自其中。本篇文章主要用于自己的知识梳理。


tcp基本知识

具体内容

tcp头格式

TCP头格式

ps:重点关注标色部分,与本篇文章相关性较大。

下面我们对图片标色部分进行分开介绍:

序列号:在建立连接时由计算机生成的随机数作为其初始值,通过 SYN 包传给接收端主机,每发送一
次数据,就「累加」一次该「数据字节数」的大小。用来解决网络包乱序问题。

确认应答号:指下一次「期望」收到的数据的序列号,发送端收到这个确认应答以后可以认为在这个序
号以前的数据都已经被正常接收。用来解决不丢包的问题。

控制位

  • ACK:该位为 1 时,「确认应答」的字段变为有效,TCP 规定除了最初建立连接时的 SYN包之外该位必须设置为 1 。
  • RST:该位为 1 时,表示 TCP 连接中出现异常必须强制断开连接。
  • SYN:该位为 1 时,表示希望建立连接,并在其「序列号」的字段进行序列号初始值的设定。
  • FIN:该位为 1 时,表示今后不会再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双方的主机之间就可以相互交换 FIN 位为 1 的 TCP 段。

TCP协议的功能

你知道为什么需要TCP协议吗?TCP工作在哪一个层次?

前面我们已经讲到过IP,它用于主机与主机之间的通信,也叫点对点(end to end)通信。
但是,IP 层是「不可靠」的。它只是保证包的终点网络,不保证网络包的交付、不保证网络包的按序交付、也不保证网络包中的数据的完整性。

所以,如果需要保障网络数据包的可靠性,那么就需要由上层(传输层)的 TCP 协议来负责。
确保接收端接收的网络包是无损坏无间隔非冗余按序的就是TCP协议的主要功能。
TCP是工作在传输层上的服务。

什么是TCP?

TCP 是面向连接的可靠的基于字节流的传输层通信协议。

  • 面向连接:一定是「一对一」才能连接,不能像 UDP 协议可以一个主机同时向多个主机发送消息,也就是一对多是无法做到的;
  • 可靠的:无论的网络链路中出现了怎样的链路变化,TCP 都可以保证一个报文一定能够到达接收端;
  • 字节流:消息是「没有边界」的,所以无论我们消息有多大都可以进行传输。并且消息是「有序的」,当「前一个」消息没有收到的时候,即使它先收到了后面的字节,那么也不能扔给应用层去处理,同时对「重复」的报文会自动丢弃。

什么是TCP连接

所谓连接,简单来说就是:用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket序列号窗口大小称为连接。

TCP连接也要符合上述要求。即建立一个TCP连接需要客户端和服务端达成以上三个信息的共识。

  • Socket:由 IP 地址和端口号组成
  • 序列号:用来解决乱序问题等
  • 窗口大小:用来做流量控制

下面我们对建议一个tcp连接的过程提出几个问题,并给予答案。

问题一:如何唯一的确定一个TCP连接?

答:TCP 四元组可以唯一的确定一个连接,四元组包括如下:

4元组
源地址目的地址的字段(32位)是在 IP 头部中,作用是通过 IP 协议发送报文给对方主机。
源端口目的端口的字段(16位)是在 TCP 头部中,作用是告诉 TCP 协议应该把报文发给哪个进程。

问题二:有一个 IP 的服务器监听了一个端口,它的 TCP 的最大连接数是多少?

对 IPv4,客户端的 IP 数最多为 2 的 32 次方,客户端的端口数最多为 2 的 16 次方,也就是服务端单机最大 TCP 连接数,约为 2 的 48 次方。

当然,在实际情况下,TCP 的最大连接数不可能达到这个理论最大值。原因如下:

  • 首先主要是文件描述符限制,Socket 都是文件,所以首先要通过 ulimit 配置文件描述符的数目;
  • 另一个是内存限制,每个 TCP 连接都要占用一定内存,操作系统的内存是有限的。

这里留下一个微信公众号的文章:公众号【低并发编程】搜索tcp即可


问题三:UDP 和 TCP 有什么区别呢?分别的应用场景是?
UDP 不提供复杂的控制机制,利用 IP 提供面向「无连接」的通信服务。

UDP 协议真的非常简,头部只有 8 个字节( 64 位),UDP 的头部格式如下:
UDP头部格式
可以与TCP头部格式进行比对,UDP头部只有8个字节,即每一个区域占用2个字节。而TCP头出去选型区域,都还有20个字节。

  • 目标和源端口:主要是告诉 UDP 协议应该把报文发给哪个进程。
  • 包长度:该字段保存了 UDP 首部的长度跟数据的长度之和。
  • 校验和:校验和是为了提供可靠的 UDP 首部和数据而设计。

那么二者的区别都有哪些呢?

  1. 连接
    TCP 是面向连接的传输层协议,传输数据前先要建立连接。
    UDP 是不需要连接,即刻传输数据。
  2. 服务对象
    TCP 是一对一的两点服务,即一条连接只有两个端点。
    UDP 支持一对一、一对多、多对多的交互通信
  3. 可靠性
    TCP 是可靠交付数据的,数据可以无差错、不丢失、不重复、按需到达。
    UDP 是尽最大努力交付,不保证可靠交付数据。
  4. 拥塞控制、流量控制
    TCP 有拥塞控制和流量控制机制,保证数据传输的安全性。
    UDP 则没有,即使网络非常拥堵了,也不会影响 UDP 的发送速率。
  5. 首部开销
    TCP 首部长度较长,会有一定的开销,首部在没有使用「选项」字段时是 20 个字节,如果使用了「选项」字段则会变长的。
    UDP 首部只有 8 个字节,并且是固定不变的,开销较小。
  6. 传输方式
    TCP 是流式传输,没有边界,但保证顺序和可靠。
    UDP 是一个包一个包的发送,是有边界的,但可能会丢包和乱序。
  7. 分片不同
    TCP 的数据大小如果大于 MSS 大小,则会在传输层进行分片,目标主机收到后,也同样在传输层组装 TCP 数据包,如果中途丢失了一个分片,只需要传输丢失的这个分片。
    UDP 的数据大小如果大于 MTU 大小,则会在 IP 层进行分片,目标主机收到后,在 IP 层组装完数据,接着再传给传输层,但是如果中途丢了一个分片,则就需要重传所有的数据包,这样传输效率非常差,所以通常 UDP 的报文应该小于 MTU。

TCP 和 UDP 应用场景:
由于 TCP 是面向连接,能保证数据的可靠性交付,因此经常用于:

  • FTP 文件传输
  • HTTP / HTTPS

由于 UDP 面向无连接,它可以随时发送数据,再加上UDP本身的处理既简单又高效,因此经常用于:

  • 包总量较少的通信,如 DNS 、 SNMP 等
  • 视频、音频等多媒体通信
  • 广播通信

    QQ使用的协议就是UDP协议。


TCP连接建立

老规矩,给个框架图

tcp连接的建立

TCP三次握手

TCP 是面向连接的协议,所以使用 TCP 前必须先建立连接,而建立连接是通过三次握手来进行的.

ps:在学习计网知识之前,我就已经听说过tcp三次握手这个大名鼎鼎的知识点,那时候就很想知道它到底是个什么东西。现在,机会来了。(下面会有较多的图片对这个过程进行分析)

梦的开始

初次接触

二次接触

三次接触

看了,上面的图解,相信你也对TCP连接的建立有了初步了解,但是还是要给出一定注意事项:

  • 要关注每一次握手之后服务端与客户端状态的变化。
  • 要关注每一次握手后TCP头部内容的变化,图片中对改变的内容做了标注。
  • 第三次握手是可以携带数据的,前两次握手是不可以携带数据的。

一旦完成三次握手,双方都处于 ESTABLISHED 状态,此时连接就已建立完成,客户端和服务端就可以相互发送数据了。

下面我们会对tcp连接的建立过程中的一些注意点和扩展内容进行补充,这些内容会采用使用二级标题进行划分,但是内容归到此部分。

如何在 Linux 系统中查看 TCP 状态?

TCP 的连接状态查看,在 Linux 可以通过 netstat -napt 命令查看。
yans

为什么是三次握手?不是两次、四次?

一个比较常见的回答是:因为三次握手才能保证双方具有接收和发送的能力。
但是这个答案是片面的,只是笼统的说法,没有给出具体的原因。

前面我们已经提到了TCP连接的定义,这里回顾一下:用于保证可靠性流量控制维护的某些状态信息,这些信息的组合,包括Socket序列号窗口大小称为连接。
所以,重要的是为什么三次握手才可以初始化Socket序列号窗口大小并建立 TCP 连接。

接下来以三个方面分析三次握手的原因:

  • 三次握手才可以阻止重复历史连接的初始化(主要原因)
  • 三次握手才可以同步双方的初始序列号
  • 三次握手才可以避免资源

原因一:避免历史连接

简单来说,三次握手的首要原因是为了防止旧的重复连接初始化造成混乱。
网络环境是错综复杂的,往往并不是如我们期望的一样,先发送的数据包,就先到达目标主机,可能会由于网络拥堵等乱七八糟的原因,会使得旧的数据包,先到达目标主机,那么这种情况下TCP 三次握手是如何避免的呢?

如何避免
图中可见,为了避免历史连接,tcp采用了数据验证的方法。因为前面我们已经知道,第一次接触时客户端发送的包的TCP段会写入序列号,而服务端会消息的时候会把这个序列号+1写入应答区,以供客户端验证;同理,当服务端把消息传回的时候也会把一个新的序列号写入,而当客户端接收后进行第三次握手时会把这个序列号+1写入应答区,以供服务端验证。

如果是两次握手连接,就不能判断当前连接是否是历史连接,三次握手则可以在客户端(发送方)准备发送第三次报文时,客户端因有足够的上下文来判断当前连接是否是历史连接:

  • 如果是历史连接(序列号过期或超时),则第三次握手发送的报文是 RST 报文,以此中止历史连接;
  • 如果不是历史连接,则第三次发送的报文是 ACK 报文,通信双方就会成功建立连接;

原因二:同步双方初始序列号

TCP 协议的通信双方, 都必须维护一个「序列号」, 序列号是可靠传输的一个关键因素

序列号的作用:

  • 接收方可以去除重复的数据;
  • 接收方可以根据数据包的序列号按序接收;
  • 可以标识发送出去的数据包中, 哪些是已经被对方收到的;

序列号的同步
可见,四次握手其实也能够可靠的同步双方的初始化序号,但由于第二步和第三步可以优化成一步,所以就成了「三次握手」。

原因三: 避免资源浪费

如果只有「两次握手」,当客户端的 SYN 请求连接在网络中阻塞,客户端没有接收到 ACK 报文,就会重新发送 SYN ,由于没有第三次握手,服务器不清楚客户端是否收到了自己发送的建立连接的ACK 确认信号,所以每收到一个 SYN 就只能先主动建立一个连接,这会造成什么情况呢?
如果客户端的 SYN 阻塞了,重复发送多次 SYN 报文,那么服务器在收到请求后就会建立多个冗余的无效链接,造成不必要的资源浪费。
(这一点与第一点原理相同,即客户端在每接收到返回包后不会进行验证,而是建立连接)

1
TCP 建立连接时,通过三次握手能防止历史连接的建立,能减少双方不必要的资源开销,能帮助双方同步初始化序列号。序列号能够保证数据包不重复、不丢弃和按序传输。

TCP连接断开

TCP 断开连接是通过四次挥手方式,双方都可以主动断开连接,断开连接后主机中的「资源」将被释放。

四次挥手

  • 客户端打算关闭连接,此时会发送一个 TCP 首部 FIN 标志位被置为 1 的报文,也即 FIN报文,之后客户端进入 FIN_WAIT_1 状态。
  • 服务端收到该报文后,就向客户端发送 ACK 应答报文,接着服务端进入 CLOSED_WAIT 状态。
  • 客户端收到服务端的 ACK 应答报文后,之后进入FIN_WAIT_2状态。
  • 等待服务端处理完数据后,也向客户端发送 FIN 报文,之后服务端进入LAST_ACK状态。
  • 客户端收到服务端的 FIN 报文后,回一个 ACK 应答报文,之后进入 TIME_WAIT 状态
  • 服务器收到了 ACK 应答报文后,就进入了 CLOSED 状态,至此服务端已经完成连接的关闭。
  • 客户端在经过 2MSL 一段时间后,自动进入 CLOSED 状态,至此客户端也完成连接的关闭。

    几个注意点:

    • 每一次接收或者发送后都会改变状态
    • 只有主动关闭连接才有TIME_WAIT状态。

为什么需要挥手四次?

再来回顾下四次挥手双方发 FIN 包的过程,就能理解为什么需要四次了。

  • 关闭连接时,客户端向服务端发送 FIN 时,仅仅表示客户端不再发送数据了但是还能接收数据。
  • 服务器收到客户端的FIN报文时,先回一个 ACK 应答报文,而服务端可能还有数据需要处理
  • 和发送,等服务端不再发送数据时,才发送FIN报文给客户端来表示同意现在关闭连接。

从上面过程可知,服务端通常需要等待完成数据的发送和处理,所以服务端的 ACK 和 FIN 一般都会分开发送,从而比三次握手导致多了一次。

ps:TIME_WAIT 状态是一个比较重要的知识点,这个状态有好处也有缺点,还有优化方法。以后会具体说到。


预知后事如何

到这里,关于TCP的初步知识就告一段落,但是只是这一个分类的。TCP是一个很复杂的内容,上面的文章只是讲了一些皮毛,对这个知识块的大体框架有一个了解。有关TCP的知识会在以后的计网分类继续深入说的。