TCP/IP四层模型的理解

  • Post author:
  • Post category:其他




应用层

没什么好说的,优秀的文章很多



传输层


TCP传输控制层

TCP是面向连接的,可靠的传输协议

面向连接:当通信双方通过三次握手,并且在都在内部开辟了通信所需要的资源,例如SOCKET缓冲区等,这就算一个连接真正建立了。


整个过程

  • 三次握手:
    三次握手

    还有一个就是为什么要进行三次握手,这也是一个非常常见的问题,因为tcp是可靠的稳定的连接,连接之前,通信的双方必须要先交流一些必要信息在发送数据之前,例如发送的数据的seq是多少,发送数据的win是多大。


    那么怎么要保证双方都能知道呢?


    假设现在我们只进行一次握手,那肯定是不行的,一次握手即使是这条消息百分之百发送成功的情况下,两个点之中的某一个会不知道对方发送能力,发送的seq。


    为什么不可以是两次握手?


    当客户端向服务器端发送一个连接请求时,由于某种原因长时间驻留在网络节点中,无法到达服务器端,由于TCP的超时重传机制,当客户端在特定的时间内没有收到服务器端的的确认应答时,就会重新向服务器端发送连接请求,该请求到达服务器端并建立连接,进行数据传输,当数据传输完成时,释放了TCP连接。

    若此时第一次的连接请求报文段延迟了一段时间后到达了服务器端,本来这是一个很早到达的失效的报文段,但是服务器端收到了该链接请求后误以为是客户端重新又发起了一次连接请求,于是服务器端发出确认应答报文段,并表示同意建立连接。如果没有第三次握手,由于服务器端发送了确认应答信息,则表示新的连接建立成功,但是客户端并没有向服务器端发送任何建立请求,客户端将忽略服务器端的确认报文,更不会发送任何请求或数据。而服务器端认为建立成功了,并一直在等待建立连接,直到超出计数器的设定值,则认为服务器端出现了异常,并关闭此链接。这个等待的过程中,浪费了服务器端的资源。

    如果是三次握手将不会出现不该建立的连接。

    三次握手中如果最后的一次握手信息发送失败了怎么办?那么如果 第三次握手中的ACK包丢失的情况下,客户端现在是已经认为连接已经建立了,Client 将会向 server端发送数据包了,但是现在server这边的连接还没有建立成功,client直接发送数据包过来,Server就不懂了,Server端将以 RST包响应,方能感知到Server的错误。

  • 发送数据
  • 四次挥手
    四次挥手

    为什么需要4次挥手呢?

    假设现在客户端一方向断开连接,客户端就需要发送一个断开连接的数据包。然后服务器收到数据包之后,因为可靠传输的原因,需要给客户端发送一个ACK包,确认自己收到了想断开连接的包。而不是说他服务器也同意断开连接,因为server这时候可能还有数据没发

    (注意是没发,而不是没收,TCP能保证消息到达的顺序性,所以既然server都确认你断开连接的包了,那么其他包肯定也就收到了)

    ,所以server还需要继续持有连接,继续发送。等到server也想断开连接的时候,server也发送一个断开连接的包,然后client也再给一个ack。这样能保证什么?保证通信双方都知道对方不在传输数据了,这时候释放资源。

    client收到server的确认断开连接的请求之后,不是立即断开连接,而是进入time_wait状态,等待2msl。 在Client发送出最后的ACK回复,但该ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该ACK

    (又是可靠连接)

    。为什么等待2msl就能确认server收到呢?其实是不能百分之百确认的,虽然说server会重发fin,但是如果fin也一直丢失怎么办?这种可能会存在,但是几率非常低,其实就算是一次丢包的几率都非常低,更别说ack和重发的fin都丢了。 所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。当然我们可以等待更长的时候来防止server没有收到ack,但是没有这个必要了已经

    (资源的角度来考虑)

    ,2MSL已经让server有了一次重发的机会,这样连续丢失几个包的概率实在是太低了。




利用抓包命令来查看上面的通讯过程

# yum install tcpdump   // 如果没有tcpdump,需要先安装这个程序
# tcpdump -nn -i eth0 port 80
-nn :不转换任何数值型ip和端口号到对应网站和协议
-i	:指定要抓取的网卡,这里指定eth0。不知道有哪些网卡使用ifconfig查看
port:指定要抓取的端口为80


另起终端,用来发送网络请求

# curl www.baidu.com	// 这个命令会进行一次完整的网络通信,且不会保持连接,会进行4次挥手断开连接,最终在原终端就能显示交互的数据了

在这里插入图片描述

  1. 前三个包就是三次握手的包,然后就是客户端请求百度主页的包

    (http请求内容)

    ,其中Flags[P.]的含义是告诉对方快处理这个包,我没有包继续发了。

  2. 然后百度回复了一个确认包,也就是确认他已经收到了,这就是TCP可靠性的来源。

  3. 现在是第5条数据包了,百度开始响应我们的http请求了,给我们返回他的响应数据。然后我们收到了这个包,给了百度一个确认。

    TCP的两个点每次收到对方的数据的时候都会发回一个响应包,就是告诉对方自己已经收到了,这就是可靠性的一个重要来源

  4. 第五条的那个数据包没有将百度想要给我们响应内容一次发送完毕,因为每次发送的数据的大小是有限制的,所以可能需要分多个包来发完,这个大小是三次握手的时候双方沟通好的,就是win的大小,这个就是告诉对方,你给我发消息一次能发多大的数据包

  5. 我们又给了百度一个确认

  6. 数据交互完毕了,因为Curl是一次完整过的请求,其会进行四次挥手,开始进行四次挥手了。我们主动的向百度发送断开连接的请求


显示当前主机当前有哪些TCP连接

netstat -natp
-n: 禁止将协议中的一些数字或者协议的名称,例如localhost,或者将80转换为http
-a: 显示所有的连接
-t: tcp连接
-p:	显示哪个进程持有这个链接



网络层


查看某个网卡的配置信息

vim /etc/sysconfig/network-scripts/ifcfg-eth0	// 查看eth0这个网卡的配置 

在这里插入图片描述

这就是这个网卡的配置

  • dhcp:表示该网卡的ip地址是通过DHCP服务动态获取
  • ONBOOT:自动启动


查看路由表

# route -n

在这里插入图片描述

出现了这样的结果,这个结果是什么含义?

每一行代表一个路由规则,决定我们的数据包如何发出去,发到哪去。Metric代表的是跃点的数量,一个跃点代表一个路由器,当有多个路由规则匹配的时候,一系统会优先选择跃点少的路径进行发送。

Use Iface代表这条路由信息是哪个网卡的,如果匹配了对用的路由信息,就从该条路由信息的网卡将数据发送出去,一般我们加入新的网卡

(网卡工作在数据链路层,网卡mac地址)

,系统都会默认的为我们加一条路由规则,当然我们也可以手动添加。


Genmask

,也就是掩码,假如我们现在要发送数据到

162.13.12.3

,如何决定怎么走?首先用

Genmask

与之进行与操作,得到的结果再与

Destination

进行比较,相同就代表这个链路可以到达目标地址,就会利用网卡将数据发送到

Gateway(网关)

让其继续进行数据的传递,也就是到达了下一跳,这样就进行了一次路由。这里明显如果我们想到达

162.13.12.3

,就是使用的第二条链路规则,因为其与操作之后结果相同

这种掩码和目标地址是

0.0.0.0

这种形式的一般是被叫做默认路由,任何数据包通过这条路由规则都能匹配成功,一般的家庭电脑这条路由都是指向公网的。

如果我们要在局域网中添加文件存储服务,就需要手动的添加一条路由规则,将指定的ip执行我们的文件服务器



数据链路层

上面我们说到了网关,我们将数据发送到网关,也就是下一跳。但是问题来了,我们如何发送?ip地址写谁?写我们要到达的目标的ip?还是网关的ip?其实是写的目标的ip地址。那要如何发送到网关呢?这要交给数据链路层来进行操作了,数据链路层利用mac地址来进行通信。利用**

arp

**协议在已知ip的情况下获取到对应的mac地址。现在已知网关的ip地址,就可以获取到网关的mac地址,然后在链路层上进行发送。又在网络层的数据外部套上一层链路层中的数据,包含了源mac地址

(主机的mac)

和目标mac地址

(网关的mac)

,当然不只包含这两个数据。然后数据发送到网关之后,把链路层的数据去掉,查看网络层的数据,发现ip地址不是自己,其又会查询其自己的路由表,找到了下一跳。又获取下一跳的mac地址,又套上一层数据链路层的数据,其中包含源mac地址

(此时就是网关的mac了)

和目标mac地址

(此时就是下一跳的mac)

了,就这样一直寻找下一条,直到某个位置拆开链路层的数据包之后发现网络层的数据中的ip是自己,这就到达了目标位置。


mac地址,每一次跳跃都会发生变化

mac地址每次跳跃都会改变,那源ip地址和目标ip地址是否会变呢?

这个问题的答案是不一定的,要看场景,我们一般的家庭路由器上网的时候,用户的ip地址一般都是

192.168.2.xxx

这种形式的,那我们在网络上通信肯定不可能使用这种ip地址啊,这样岂不是会有很多相同的源ip地址在网络上进行请求,怎么区分谁是谁呢?所以说,我们一般的家庭路由器,源ip地址是会改变的,其会被运营商变成一个公网的ip地址,这样响应数据的时候才能正确的路由回来。但是如果你本身就是公网ip,那就不会变化了。这就是看场景。


ARP协议实验

同样打开两个终端

// 第一个终端执行
# tcpdump -nn -i eth0 port 80 or arp	// 监听80端口或者是arp协议的请求

// 第二个终端执行
# arp -d 192.168.163.2 && curl www.baidu.com	
// 第一部分表示删除192.168.163.2的mac地址记录,这样又需要重新去请求该ip的mac地址,这个IP地址是网关的ip地址,这个就是删除了网关的mac地址记录
// 第二部分就是请求百度 ,因为前一部分删除了网关的mac地址记录,这里要向百度发送请求肯定又需要网关的mac地址,我们也就能看到对应的获取mac地址的arp请求

在第一个终端我们就能看到抓到的记录

在这里插入图片描述

可以看到,最开始的这条arp请求就是用来获取

192.168.163.128

的mac地址的,这个请求是以广播的形式发出去的,然后

192.168.163.128

收到了这个广播并回复了他。这就是一个arp请求的形式


注意:

arp -d 192.168.163.2 && curl www.baidu.com

这个两个命令必须一起执行,因为如果如果先执行第一部分再执行第二部分,可能抓包记录中不会有arp请求,因为其可能会周期性的自动获取某个ip对应的mac,可能在你抓包之前,自动获取了,192.168.163.2 的mac地址就已经有了,当然也就不会发送arp请求了


新的问题,运营商如何区分那么多宽带用户

举个例子,比如我们现在使用没有公网ip的家庭路由器上网,我们现在请求百度,我们的ip地址是192.168.1.2,数据发送出去之后,ip地址会被运营商变成公网ip,然后成功的将数据发送到了百度,但是百度响应数据的时候响应到的是运营商啊,因为请求的源ip地址被运营商变成了他的ip,那运营商如何将数据发送给我们?这就要依赖另外一个技术了:

端口映射




内网和外网之间的通信(端口映射)


windows下查看路由表


在这里插入图片描述

看起来稍微与linux的有所区别,但都是差不多的,这里的接口就与linux下的网卡是一个意思,就决定从哪个口出去。在链路上就表示不用经过网关,直接发送到目标网络。而带有网关地址的,则需要将数据包发送到网关,再由网关进行转发



版权声明:本文为P19777原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。