高频问题——TCP-IP协议知识快速入门

文章转载自博主:https://www.cnblogs.com/onepixel/p/7092302.html 强烈推荐,这个博主其他博文写得也精彩


深入浅出 TCP/IP 协议栈

TCP/IP 协议栈是一系列网络协议的总和,是构成网络通信的核心骨架,它定义了电子设备如何连入因特网,以及数据如何在它们之间进行传输。TCP/IP 协议采用4层结构,分别是应用层、传输层、网络层和链路层(网络接口层),每一层都呼叫它的下一层所提供的协议来完成自己的需求。由于我们大部分时间都工作在应用层,下层的事情不用我们操心;其次网络协议体系本身就很复杂庞大,入门门槛高,因此很难搞清楚TCP/IP的工作原理,通俗一点讲就是,一个主机的数据要经过哪些过程才能发送到对方的主机上。 接下来,我们就来探索一下这个过程。

0、物理介质

物理介质就是把电脑连接起来的物理手段,常见的有光纤、双绞线,以及无线电波,它决定了电信号(0和1)的传输方式,物理介质的不同决定了电信号的传输带宽、速率、传输距离以及抗干扰性等等。

TCP/IP协议栈分为四层,每一层都由特定的协议与对方进行通信,而协议之间的通信最终都要转化为 0 和 1 的电信号,通过物理介质进行传输才能到达对方的电脑,因此物理介质是网络通信的基石。

下面我们通过两张图先来大概了解一下TCP/IP协议的基本框架:

高频问题——TCP-IP协议知识快速入门

对应的OSI模型(后出现),如图: 

高频问题——TCP-IP协议知识快速入门

当通过http发起一个请求时,应用层、传输层、网络层和链路层的相关协议依次对该请求进行包装并携带对应的首部,最终在链路层生成以太网数据包,以太网数据包通过物理介质传输给对方主机,对方接收到数据包以后,然后再一层一层采用对应的协议进行拆包,最后把应用层数据交给应用程序处理。类比俄罗斯套娃。

网络通信就好比送快递,商品外面的一层层包裹就是各种协议,协议包含了商品信息、收货地址、收件人、联系方式等,然后还需要配送车、配送站、快递员,商品才能最终到达用户手中。

一般情况下,快递是不能直达的,需要先转发到对应的配送站,然后由配送站再进行派件。

配送车就是物理介质,配送站就是网关, 快递员就是路由器,收货地址就是IP地址,联系方式就是MAC地址。 

快递员负责把包裹转发到各个配送站,配送站根据收获地址里的省市区,确认是否需要继续转发到其他配送站,当包裹到达了目标配送站以后,配送站再根据联系方式找到收件人进行派件。  

下图中IP相当于信上的地址信息,TCP相当于信上的联系人信息。IP地址可以定位到范围,TCP负责精准投递。

高频问题——TCP-IP协议知识快速入门

前三部分都是新的包装,应用层数据才是内容。 

高频问题——TCP-IP协议知识快速入门

有了整体概念以后,下面我们详细了解一下各层的分工。

 

1、链路层(网络接口层)

网络通信就是把有特定意义的数据通过物理介质传送给对方,单纯的发送 0 和 1 是没有意义的,要传输有意义的数据,就需要以字节为单位对 0 和 1 进行分组,并且要标识好每一组电信号的信息特征,然后按照分组的顺序依次发送。以太网规定一组电信号就是一个数据包,一个数据包被称为一帧, 制定这个规则的协议就是以太网协议。一个完整的以太网数据包如下图所示:

高频问题——TCP-IP协议知识快速入门

整个数据帧由首部数据尾部三部分组成,首部固定为14个字节,包含了目标MAC地址、源MAC地址和类型;数据最短为46个字节,最长为1500个字节,如果需要传输的数据很长,就必须分割成多个帧进行发送;尾部固定为4个字节,表示数据帧校验序列,用于确定数据包在传输过程中是否损坏。因此,以太网协议通过对电信号进行分组并形成数据帧,然后通过物理介质把数据帧发送给接收方。那么以太网如何来识接收方的身份呢?

以太网规协议定,接入网络的设备都必须安装网络适配器,即网卡, 数据包必须是从一块网卡传送到另一块网卡。而网卡地址就是数据包的发送地址和接收地址,也就是帧首部所包含的MAC地址,MAC地址是每块网卡的身份标识,就如同我们身份证上的身份证号码,具有全球唯一性。MAC地址采用十六进制标识,共6个字节, 前三个字节是厂商编号,后三个字节是网卡流水号,例如 4C-0F-6E-12-D2-19

有了MAC地址以后,以太网采用广播形式,把数据包发给该子网内所有主机,子网内每台主机在接收到这个包以后,都会读取首部里的目标MAC地址,然后和自己的MAC地址进行对比,如果相同就做下一步处理,如果不同,就丢弃这个包。

所以链路层的主要工作就是对电信号进行分组并形成具有特定意义的数据帧,然后以广播的形式通过物理介质发送给接收方。

 

2、网络层

对于上面的过程,有几个细节问题值得我们思考:

发送者如何知道接收者的MAC地址?

发送者如何知道接收者和自己同属一个子网?

如果接收者和自己不在同一个子网,数据包如何发给对方?

为了解决这些问题,网络层引入了三个协议,分别是IP协议ARP协议路由协议。

【1】IP协议

通过前面的介绍我们知道,MAC地址只与厂商有关,与所处的网络无关,所以无法通过MAC地址来判断两台主机是否属于同一个子网。

因此,网络层引入了IP协议,制定了一套新地址,使得我们能够区分两台主机是否同属一个网络,这套地址就是网络地址,也就是所谓的IP地址。

IP地址目前有两个版本,分别是IPv4IPv6,IPv4是一个32位的地址,常采用4个十进制数字表示。IP协议将这个32位的地址分为两部分,前面部分代表网络地址,后面部分表示该主机在局域网中的地址。由于各类地址的分法不尽相同,以C类地址192.168.24.1为例其中前24位就是网络地址,后8位就是主机地址。因此, 如果两个IP地址在同一个子网内,则网络地址一定相同。为了判断IP地址中的网络地址,IP协议还引入了子网掩码, IP地址和子网掩码通过按位与运算后就可以得到网络地址。

由于发送者和接收者的IP地址是已知的(应用层的协议会传入), 因此我们只要通过子网掩码对两个IP地址进行AND运算后就能够判断双方是否在同一个子网了。

【2】ARP协议

即地址解析协议,是根据IP地址获取MAC地址的一个网络层协议。其工作原理如下:

简单的来说 ARP 的作用就是把 IP 地址映射为物理地址,而与之相反的 RARP(逆向 ARP)就是将物理地址映射为 IP 地址。

ARP首先会发起一个请求数据包,数据包的首部包含了目标主机的IP地址,然后这个数据包会在链路层进行再次包装,生成以太网数据包,最终由以太网广播给子网内的所有主机,每一台主机都会接收到这个数据包,并取出标头里的IP地址,然后和自己的IP地址进行比较,如果相同就返回自己的MAC地址,如果不同就丢弃该数据包。ARP接收返回消息,以此确定目标机的MAC地址;与此同时,ARP还会将返回的MAC地址与对应的IP地址存入本机ARP缓存中并保留一定时间,下次请求时直接查询ARP缓存以节约资源。cmd输入 arp -a 就可以查询本机缓存的ARP数据。

【3】路由协议

通过ARP协议的工作原理可以发现,ARP的MAC寻址还是局限在同一个子网中,因此网络层引入了路由协议,首先通过IP协议来判断两台主机是否在同一个子网中,如果在同一个子网,就通过ARP协议查询对应的MAC地址,然后以广播的形式向该子网内的主机发送数据包;如果不在同一个子网,以太网会将该数据包转发给本子网的网关进行路由。网关是互联网上子网与子网之间的桥梁,所以网关会进行多次转发,最终将该数据包转发到目标IP所在的子网中,然后再通过ARP获取目标机MAC,最终也是通过广播形式将数据包发送给接收方。

高频问题——TCP-IP协议知识快速入门

而完成这个路由协议的物理设备就是路由器,在错综复杂的网络世界里,路由器扮演者交通枢纽的角色,它会根据信道情况,选择并设定路由,以最佳路径来转发数据包。

【4】IP数据包

在网络层被包装的数据包就叫IP数据包,IPv4数据包的结构如下图所示:

高频问题——TCP-IP协议知识快速入门

IP数据包由首部和数据两部分组成,首部长度为20个字节,主要包含了目标IP地址和源IP地址,目标IP地址是网关路由的线索和依据;数据部分的最大长度为65515字节,理论上一个IP数据包的总长度可以达到65535个字节,而以太网数据包的最大长度是1500个字符,如果超过这个大小,就需要对IP数据包进行分割,分成多帧发送。

所以,网络层的主要工作是定义网络地址,区分网段,子网内MAC寻址,对于不同子网的数据包进行路由。

 

3、传输层

链路层定义了主机的身份,即MAC地址, 而网络层定义了IP地址,明确了主机所在的网段,有了这两个地址,数据包就从可以从一个主机发送到另一台主机。但实际上数据包是从一个主机的某个应用程序发出,然后由对方主机的应用程序接收。而每台电脑都有可能同时运行着很多个应用程序,所以当数据包被发送到主机上以后,是无法确定哪个应用程序要接收这个包。

因此传输层引入了UDP协议来解决这个问题,为了给每个应用程序标识身份,UDP协议定义了端口,同一个主机上的每个应用程序都需要指定唯一的端口号,并且规定网络中传输的数据包必须加上端口信息。 这样,当数据包到达主机以后,就可以根据端口号找到对应的应用程序了。UDP定义的数据包就叫做UDP数据包,结构如下所示:

高频问题——TCP-IP协议知识快速入门

UDP数据包由首部和数据两部分组成,首部长度为8个字节,主要包括源端口和目标端口;数据最大为65527个字节,整个数据包的长度最大可达到65535个字节。

UDP协议比较简单,实现容易,但它没有确认机制, 数据包一旦发出,无法知道对方是否收到,因此可靠性较差,为了解决这个问题,提高网络可靠性,TCP协议就诞生了,TCP即传输控制协议,是一种面向连接的、可靠的、基于字节流的通信协议。简单来说TCP就是有确认机制的UDP协议,每发出一个数据包都要求确认,如果有一个数据包丢失,就收不到确认,发送方就必须重发这个数据包。

为了保证传输的可靠性,TCP 协议在 UDP 基础之上建立了三次对话的确认机制,也就是说,在正式收发数据前,必须和对方建立可靠的连接。由于建立过程较为复杂,我们在这里做一个形象的描述:

高频问题——TCP-IP协议知识快速入门

主机A:我想发数据给你,可以么?

主机B:可以,你什么时候发?

主机A:我马上发,你接着!

经过三次对话之后,主机A才会向主机B发送正式数据,而UDP是面向非连接的协议,它不与对方建立连接,而是直接就把数据包发过去了。所以 TCP 能够保证数据包在传输过程中不被丢失,但美好的事物必然是要付出代价的,相比 UDP,TCP 实现过程复杂,消耗连接资源多,传输速度慢。

TCP 数据包和 UDP 一样,都是由首部和数据两部分组成,唯一不同的是,TCP 数据包没有长度限制,理论上可以无限长,但是为了保证网络的效率,通常 TCP 数据包的长度不会超过IP数据包的长度,以确保单个 TCP 数据包不必再分割。

总结一下,传输层的主要工作是定义端口,标识应用程序身份,实现端口到端口的通信,TCP协议可以保证数据传输的可靠性

传输控制协议(TCP):提供了完善的错误控制和流量控制,能够确保数据正常传输,是一个面向连接的协议。
用户数据报协议(UDP):只提供了基本的错误检测,是一个无连接的协议。
特点:
1)UDP:
把数据打包
数据大小有限制(64k)
不建立连接
速度快,但可靠性低
2)TCP:
建立连接通道
数据大小无限制
速度慢,但是可靠性高

 

4、应用层

理论上讲,有了以上三层协议的支持,数据已经可以从一个主机上的应用程序传输到另一台主机的应用程序了,但此时传过来的数据是字节流,不能很好的被程序识别,操作性差。因此,应用层定义了各种各样的协议来规范数据格式,常见的有 HTTP、FTP、SMTP 等,HTTP 是一种比较常用的应用层协议,主要用于B/S架构之间的数据通信,其报文格式如下:

高频问题——TCP-IP协议知识快速入门

在 Resquest Headers 中,Accept 表示客户端期望接收的数据格式,而 ContentType 则表示客户端发送的数据格式;在 Response Headers 中,ContentType 表示服务端响应的数据格式,这里定义的格式,一般是和  Resquest Headers 中 Accept 定义的格式是一致的。

有了这个规范以后,服务端收到请求以后,就能正确的解析客户端发来的数据,当请求处理完以后,再按照客户端要求的格式返回,客户端收到结果后,按照服务端返回的格式进行解析。

所以应用层的主要工作就是定义数据格式并按照对应的格式解读数据。

5、全流程

首先我们梳理一下每层模型的职责:

  • 链路层:对0和1进行分组,定义数据帧,确认主机的物理地址,传输数据;
  • 网络层:定义IP地址,确认主机所在的网络位置,并通过IP进行MAC寻址,对外网数据包进行路由转发;
  • 传输层:定义端口,确认主机上应用程序的身份,并将数据包交给对应的应用程序;
  • 应用层:定义数据格式,并按照对应的格式解读数据。

然后再把每层模型的职责串联起来,用一句通俗易懂的话讲就是:

当你输入一个网址并按下回车键的时候,首先,应用层协议对该请求包做了格式定义;紧接着传输层协议加上了双方的端口号,确认了双方通信的应用程序;然后网络协议加上了双方的IP地址,确认了双方的网络位置;最后链路层协议加上了双方的MAC地址,确认了双方的物理位置,同时将数据进行分组,形成数据帧,采用广播方式,通过传输介质发送给对方主机。而对于不同网段,该数据包首先会转发给网关路由器,经过多次转发后,最终被发送到目标主机。目标机接收到数据包后,采用对应的协议,对帧数据进行组装,然后再通过一层一层的协议进行解析,最终被应用层的协议解析并交给服务器处理。

6、总结

以上内容是对TCP/IP四层模型做了简单的介绍,而实际上每一层模型都有很多协议,每个协议要做的事情也很多,但我们首先得有一个清晰的脉络结构,掌握每一层模型最基本的作用,然后再去丰富细枝末节的东西,也许会更容易理解。

整体的示意图如图所示。

高频问题——TCP-IP协议知识快速入门


在原文基础上,又加了一些其他的参考和补充,来源有

https://blog.csdn.net/yulyu/article/details/69062288#commentBox  面试时,你被问到过 TCP/IP 协议吗?

https://zhuanlan.zhihu.com/p/29724438  十分钟入门

https://www.zhihu.com/question/51074319 TCP/IP 协议到底在讲什么?@师爷快上的答案讲的非常生动

https://blog.csdn.net/qzcsu/article/details/72861891#commentBox TCP的三次握手与四次挥手(详解+动图)

https://blog.csdn.net/Ningdaxing1994/article/details/73076795#commentBox五分钟读懂TCP协议

http://www.360kuai.com/pc/9b2f0201c112d5bd0?cota=4&sign=360_57c3bbd1&refer_scene=so_1 程序员被问到三次握手

http://www.360kuai.com/pc/9a9a48508410043a3?cota=4&sign=360_57c3bbd1&refer_scene=so_1关于三次握手与四次挥手面试官想考我们什么?

常见问题:

为什么客户端最后还要等待2MSL?
MSL(Maximum Segment Lifetime),TCP允许不同的实现可以设置不同的MSL值。

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

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

为什么建立连接是三次握手,关闭连接确是四次挥手呢?

建立连接的时候, 服务器在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。
而关闭连接时,服务器收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,而自己也未必全部数据都发送给对方了,所以己方可以立即关闭,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送,从而导致多了一次。

如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。

三次握手的作用

三次握手的作用也是有好多的,多记住几个,保证不亏。例如:

1、确认双方的接受能力、发送能力是否正常。

2、指定自己的初始化***,为后面的可靠传送做准备。

3、如果是 https 协议的话,三次握手这个过程,还会进行数字证书的验证以及加***的生成到。

单单这样还不足以应付三次握手,面试官可能还会问一些其他的问题,例如:

1、(ISN)是固定的吗

三次握手的一个重要功能是客户端和服务端交换ISN(Initial Sequence Number), 以便让对方知道接下来接收数据的时候如何按***组装数据。

如果ISN是固定的,攻击者很容易猜出后续的确认号,因此 ISN 是动态生成的。

2、什么是半连接队列

服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。当然还有一个全连接队列,就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。

这里在补充一点关于SYN-ACK 重传次数的问题:服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传,如果重传次数超 过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。注意,每次重传等待的时间不一定相同,一般会是指数增长,例如间隔时间为 1s, 2s, 4s, 8s, ….

3、三次握手过程中可以携带数据吗

很多人可能会认为三次握手都不能携带数据,其实第三次握手的时候,是可以携带数据的。也就是说,第一次、第二次握手不可以携带数据,而第三次握手是可以携带数据的。

为什么这样呢?大家可以想一个问题,假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据,因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN 报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。也就是说,第一次握手可以放数据的话,其中一个简单的原因就是会让服务器更加容易受到攻击了。

而对于第三次的话,此时客户端已经处于 established 状态,也就是说,对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据页没啥毛病。

关于三次握手的,https 的认证过程能知道一下最好,不过我就不说了,留着写 http 面试相关时的文章再说。

原理: 
在三次握手过程中,Server发送SYN-ACK之后,收到Client的ACK之前的TCP连接称为半连接(half-open connect),此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将产时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。

检测: 
SYN攻击时一种典型的DDOS攻击,检测SYN攻击的方式非常简单,即当Server上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了。windows下打开cmd,输入命令:”netstat -n -p TCP“,查看是否有大量的”SYN_RECEIVED“状态。
 

【问题1】为什么连接的时候是三次握手,关闭的时候却是四次握手?

答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。

【问题2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?

答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出最后的ACK回复,但该ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。

【问题3】为什么不能用两次握手进行连接?

答:3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始***进行协商,这个***在握手过程中被发送和确认。

       现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发 送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S 是否已准备好,不知道S建立什么样的***,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分 组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。

【问题4】如果已经建立了连接,但是客户端突然出现故障了怎么办?

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