🍬 微信公众号:
公子小白coding
📚 文章收录专栏:
《网络系列》
👨👦👦 技术交流群: 🐧:
656101079
:公众号内即可获取🎨 所有文章第一时间都会在公众号发布,感兴趣的小伙伴快来接入我们吧~~
⏰ 本篇文章共计
4335
字 ,预计阅读用时**11分钟
前言
兄弟们大家在面试的时候,有没有被问到过 「 TCP 」 的知识啊?之前有次面试就被问到了 这个问题。
Q1:
面试官问我 tcp 是啥 ?
A1:
我是一脸的懵逼啊! 是啥啊,我这在平常也接触不到这个玩意儿,只是知道是一个 「 网络协议 」,其他的是一概也不知道啊!
相信兄弟们对 「 TCP 」 这块的了解,应该和我之前非常的相似吧。
对于兄弟们的了解,也就仅仅只限于,知道有这个东西,但这个是干啥用的不知道。还知道一个 TCP 和 UDP 的区别,知道这些简单的基础概念,其余比较深入的为什么那是一概回答不上来!
正是有了这次的面试经历,我决定下定决心把 「 TCP 」 前后的来龙去脉理出来,帮助自己,也造福家人们。
好了废话不多了,直接进入今天 「 TCP 」 的正题,把这篇文章当做是一个面试查漏补缺的地方也行!
直接开始
Q1:TCP 究竟是什么?
这个问题的答案,我们还是先找百度来帮忙吧!!
百度百科中给出的对于 TCP 解释是:传输控制协议(TCP,Transmission Control Protocol)是一种 面向连接的
、可靠的
、基于字节流
的传输层通信协议。
总结特点如下结构图:
这词条的解释重点在于它的后半句:
- 面向连接
- 可靠的
- 基于字节流
- 传输层
- 通讯协议
短短的一句话,我们从中提炼出了 5 个我们需要进一步去了解的名词,真是可怕哦,但是我们会逐一击破这层壁垒,我还不信弄不会了。
特点之 ——「通讯协议」
那我们先从 「 通讯协议 **」**说起吧,首先 「 协议 」 我们大家都知道,工作过的兄弟们肯定都签过劳动合同吧,你要是没签过,明天赶紧提桶跑路吧,别问,问就是我说的,再不济,用软件的时候总看到过必须同意软件协议吧,不然你都进不去。
由此我们可以知道,「 通讯协议 」,就是一种对于通信双方来说,互相认可和达成的一种共识,都认同或者说去遵守这个规范,这就是 「 通讯协议 」。
特点之 ——「传输层」
下面来说一下「传输层」是什么意思,了解过计算机网络的同学们,应该都接触个一个东西叫做—— 「网络分层模型吧」 !通俗来说,就是将网络连接,根据在每个阶段所干的事情不同,划分了多个层级。
网络分层这样做的好处就是,由于每层之间事情独立,并且划分层级,都是相互独立的互不影响。灵活性更好,也更好维护和实现。
知道了分层模型,我们简单介绍一下其中的「传输层」,「传输层」是分层模型中的其中一层,它在其中的作用是负责对两个主机进程之间的通信提供服务,也就是说传输层就是将两个不同电脑上的应用连接起来了。
就类似于你用微信给我发消息,那传输层就是把咋俩手机上的微信连接起来了。
从上面我们知道 传输层其实不只是有 「 TCP 」 这一种协议的,还有其他各种各样的协议。比如另一个我们熟悉的 「 UDP协议 」 等等。
特点之 ——「面向连接」、「可靠的」、「基于字节流」
让我们看后三个关键词,他也是跟**「** TCP **」**所息息相关的,面向连接的、可靠的、基于字节流
面向连接: 一定是**「一对一」**才能连接,不能像 「 UDP协议 」 可以一个主机同时向多个主机发送消息,也就是一对多是无法做到的
可靠的: 无论的网络链路中出现了怎样的链路变化,「 TCP 」 都可以保证一个报文一定能够到达接收端
基于字节流:最为核心的便是消息无界, 想要理解 「 TCP 」 基于字节流,就需要先来理解一下**「** UDP 」 的基于报文协议
Q1-1:说一下UDP 为什么是面向报文的?
消息在通过 「 UDP协议 」 封装时,操作系统底层是不会对消息进行拆分的,不管多大还是多小,都会直接在消息体上组装好 UDP 头,然后就直接交给下一层的网络层去处理了。
像这种直接不对消息做任何处理,只是包装了一层消息头就叫做面向报文协议。
聪明的小伙伴,肯定一下就想到了,既然 TCP 和 UDP 协议是不同的,并且 「 TCP协议 」 是基于字节流,那么对消息的处理方式应该是不同的。
「 TCP 」基于字节流不同于面向报文。一条消息数据,「 TCP 」在传输时,可能不像 UDP 一样固定发送一个网络包,而是可能会将消息拆分成多个 TCP 段。也就是说,一条完整的消息可能会被分割成多个 TCP 段进行传输。
那多条消息经过 「 TCP协议 」 发送时,有可能会经历哪些情况呢?
- 符合所有限制条件,不用拆分合并,正常发送两条封装 TCP 头,发送 「 TCP数据段 」
- 两条消息合并成一条 「 TCP数据段 」,进行发送
- 第二条消息中一部分信息随第一条消息发送
- 第一条消息中一部分随第二条消息发送
那么,我们就会有疑问了,TCP为什么要做这种拆包的操作,难道像UDP 一条消息一个报文不好吗?
想要彻底搞清楚 「 TCP协议 」 要对消息做拆分动作,那么就需要好好理解 「 TCP 」 的特点 —— 「 可靠性 」。
它和 UDP 协议的不同是,它会保证消息一定会发送到接收端,而 UDP 是不用保证消息一定发送到 接收端的手上的。
正是由于这个特性,从而让 「 TCP 」 为 「 可靠性 」 做出了很多改变。我们都知道现在网络中的包数量是非常多的。
「 TCP协议 」 想要确保消息交到接收方的手中,就需要两个前提条件:
- 网络通畅,不拥挤
- 接收端有能力接收
「 网络通畅 」
想要保证网络时通畅的,并且不拥挤的,那么最好就是可以最大可能的减少网络包的数量,这样才不会让网络越来越拥挤,从而导致接收端收不到消息。
怎么最大可能减少网络包数量呢?
比如,你和我用微信聊天,我问你的问题都是“你吃饭了吗?你睡觉了吗?你在洗澡吗?“
,你回复我的消息都是”嗯 嗯 嗯“
,这么敷衍的回答,「 TCP 」 都看不下去了。
三次的回答对应三次网络消息,可是这样的网络消息封装成包,实在是太小了。「 TCP 」 想了个法子,就是把这种很小的网络包,能不能拼接起来,一块发送出去啊!
这样网络包的数量,直接就能够从原来的三个,缩减为原来的一个,这不是能够很巧妙的解决网络拥挤问题嘛!
这种思路也正是 「 TCP 」 所使用的,这种多条消息合并的操作在 「 TCP 」 中被称作 「 粘包 」。
当然, TCP 消息合并(粘包)的条件有很多。这里先按下不表,下篇文章在做解释。
「 接收端有能力接收 」
接收端有能力和条件接收,想要达成这个条件。那么就需要发送端和接收端双方共同维护一个变量值。
当然这个值是可以动态变化的,根据情况动态变化,比如说接收端现在非常的忙,能够处理的消息非常的有限,把能够处理的最大值同步给发送端。
那么作为发送端一定要知道这个情况,下次发送消息的时候,就不能够超过这个最大值。
那如果说接下来的消息体积大于了接收端能接收的最大值怎么办呢?
那就只能将消息体拆分成多个部分了,然后再分成多个批次进行发送,这种操作也叫做 「 TCP 」 的 「 拆包 」 操作。
当然,「 TCP 」 为了保证传输消息的可靠性,不止做了这些手段,这只是保证可靠性的冰山一角罢了。
到这里也就解释了为什么 「 TCP协议 **」**是一种基于字节流传输而不是基于报文传输,就是因为对于 「 TCP 」 数据段来说。
一条 「 TCP数据段 」 是没有固定的边界的,有可能是一条完整的消息,也有可能发生 「 拆包 」 后是消息的一部分,还有可能发生 「 粘包 」 是多条消息的结合体。
💡补充 :消息保护边界,就是指传输协议把数据当作一条独立的消息在网络上传输,接收端只能接收独立消息。也就是说存在保护消息边界,接收端一次只能接收发送端发出的一个数据包。而面向字节流是指无消息保护边界的,如果发送端连续发送数据,接收端有可能在一次接收动作中,会接收两个或者更多的数据包。
经过上述不太严谨的对于 「 TCP 」 的解释,相信大家对于 「 TCP **」**是什么?以及如何理解 「 TCP 」 的 面向连接的、可靠的、基于字节流
的三大特点大致有所了解了。
「TCP头部格式」
接下来就让我们深入细致的来了解一下TCP的知识
首先来看一下 「 TCP头部格式 」 的定义吧:
序列号:在建立连接时由计算机生成的随机数作为其初始值,通过 SYN 包传给接收端主机,每发送一次数据,就 「累加」 一次该 「数据字节数」 的大小。用来解决网络包乱序问题。
确认应答号:指下一次「期望」收到的数据的序列号,发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。用来解决丢包的问题。
控制位:
-
ACK
:该位为 1 时, 「确认应答」 的字段变为有效,TCP 规定除了最初建立连接时的 SYN 包之外该位必须设置为 1 。RST
:该位为 1 时,表示 TCP 连接中出现异常必须强制断开连接。SYN
:该位为 1 时,表示希望建立连接,并在其 「序列号」 的字段进行序列号初始值的设定。FIN
:该位为 1 时,表示今后不会再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双方的主机之间就可以相互交换 FIN 位为 1 的 TCP 段。
上面看下来的兄弟们都知道,「 TCP 」 是处于传输层的网络协议,针对的两个主机之间进程之间的连接。
所以对于一个 「 TCP 」 连接来说,有四个参数值是必要的,也是确定唯一一个TCP 连接的因素。
这四个参数分别是:
- 源IP地址
- 目标IP地址
- 源主机端口
- 目标主机端口
当然 「 TCP 头部 」中只会去确定两个参数,分别是「 源主机端口 」和「 目标主机端口 」,这两个参数是用来确定进程之间的连接的。
剩下的两个参数 「 源 IP 地址 」 和 「 目标 IP 地址 」,是由 「 传输层 」 的下面一层也就是 「 网络层 」 去定义的,也就是说,「 传输层 」 是在 「 网络层 」 建立两台主机连接的前提下,再去确立两个进程之间的连接。
这里也更体现了网络分层的意义和特点。每一层只会做对应的事情。
Q2:我们为啥需要它(TCP)?
想要知道我们为什么需要 「 TCP协议 」,那我们就需要再看一下 「 TCP 」 的特点了。
回顾一下 TCP 的特点:面向连接、可靠、基于字节流。
他有一个很重要的特点,就是 「 TCP 」是很可靠的,这也是我们会在很多场景下使用「 TCP 」作为我们在「 传输层 」 协议的原因。
比如我们想要进行文件传输,我们想要把我们新发现的小电影传给好兄弟。那要是采用一个不可靠的连接比如与之对应的 UDP 协议,传过去,给好兄弟一看都打不开,感觉传了个寂寞。岂不是弄了一个笑话。
这种类似的情况还有很多,我们需要一种能够保证接收端收到消息的机制,很明显 「 TCP 」 是拥有这一系列机制的协议的。
TCP和UDP对比:
其实做一个对比大家就知道 「 TCP 」 的意义了,我们就拿它的好兄弟,同处于 「 传输层协议 **」的 ** 「 UDP 协议 」 作为比较,来对比一下这两者的区别:
从上述四个关键点来说明这两者之间的区别:
连接:
- 「 TCP 」 是面向连接的,得先连接,才能传输数据。并且连接对象一对一
- 「 UDP 」 是无连接的,传输数据不需要建立连接,所以支持一对一和一对多通信
可靠性:
- 「 TCP 」 可靠性是最大特点,保证消息可以无差错,不丢失、不重复,并且有序
- 「 UDP 」 不能保证可靠性,只能尽可能的将数据交付接收端
传输方式:
- 「 TCP 」 基于字节流传输的,没有固定的消息边界
- 「 UDP 」 基于报文传输的,有自己固定的消息边界
首部开销:
- 「 TCP 」 头部格式 ,最小会占用 20 字节
- 「 UDP 」 头部格式,默认只会占用 8 字节
知道了,这两个好兄弟之间的区别之后,我们再来梳理一下,「 TCP 」和「 UDP 」 在使用场景上的区别
TCP 和 UDP 应用场景:
由于 「 TCP 」 是面向连接,能保证数据的可靠性交付,因此经常用于:
- FTP 文件传输;
- HTTP / HTTPS;
由于 「 UDP **」**面向无连接,它可以随时发送数据,再加上 「 UDP 」 本身的处理既简单又高效,因此经常用于:
- 包总量较少的通信,如 DNS 、SNMP 等;
- 视频、音频等多媒体通信;
- 广播通信;
问题总结
到此为止,我们已经解开了我们一开始,所产生的疑惑了,分别是下面的这两个问题:
Q1:TCP 究竟是什么?
Q2:我们为啥需要它?
简单总结起来一句话回答这两个问题:
「 TCP 」是一种面向连接的、可靠的、基于字节流的 「 传输层协议 」。因为它的可靠性,所以我们需要由他来作为我们在 「 传输层 」 的使用协议。
埋的新坑
好不容易解释完了上面的问题,但是就又引入了新的问题,「 TCP 」 作为网络协议中最复杂的协议。
这里其实还留下了两个坑:
- 「 TCP 」 的拆包粘包是怎么回事
- 「 TCP 」的连接时如何建立的
由于本章篇幅有限,在这里不做过多阐述,有兴趣研究后续问题的。请关注我,后续会补上之前留下的大坑的,毕竟我是不会坑兄弟们的。
修炼完成
作者水平有限,写文章过程中难免出现错误和疏漏,请理性讨论与指正。
如果你觉得本文对你有一定的启发,引起了你的思考。
点赞、转发、收藏,下次你就能很快的找到我喽!
\
\