TCP/IP 协议
是网络通讯的基石,TCP/IP 协议
不是只有 TCP
和 IP
协议,它是整个网络通讯中全部协议的简称。php
维基百科:TCP/IP协议簇)html
维基百科:OSI模型java
# TCP/IP 参考模型维基百科 https://zh.wikipedia.org/wiki/TCP/IP%E5%8D%8F%E8%AE%AE%E6%97%8F # OIS 参考模型维基百科 https://zh.wikipedia.org/wiki/OSI%E6%A8%A1%E5%9E%8B
<img src="http://oss.mflyyou.cn/blog/20200801104517.png?author=zhangpanqin" alt="image-20200801104517510" style="zoom:50%;" />linux
图片来自 《图解 TCP/IP 与 OSI 参考模型》 中 TCP/IP 协议分层模型
OSI 参考模型
(七层)是个理论模型,实际咱们用的是 TCP/IP
(四层)模型。不过咱们能够经过 OSI 参考模型
来学习 TCP/IP
模型。面试
应用层:应用程序通讯细节的协议,好比经常使用的 HTTP
。redis
传输层:主要是负责两个节点之间数据传输,通讯标识是 port
端口号。算法
网络层:地址管理和路由选择,在两点之间找到一条最佳的通讯路线,通讯标识是 IP
后端
数据链路层:负责物理层面连接的通讯(同一个网段内)。也就是局域网中经过交换机连接的节点。通讯标识是 Mac 地址
,网卡出厂自带的标识。centos
物理层:将链路层的数据帧(字节流)转换为电压或光信号传播。数组
网络通讯能够作什么呢?
redisson
(一个操做 redis 的 java 库),就是使用的 netty
来作网络通讯链接 redis 服务的。
微服务中的服务发现和通讯,就须要你熟悉网络通讯。
你要是在通讯行业,那就不是了解了,你连协议的规范都得很清楚,否则路由器你都整不出来,还说什么 5G
。
做为一个 Java 后端开发,主要是开发偏应用层面的程序,离底层相对比较远,熟练掌握便可,若是之后作通讯行业的时候,你也必定会进一步学习的相关细节的。
TCP/IP
你不了解,也不会有多大问题,CRUD
仍是没有问题的。可是你了解了以后,平常开发定位和解决问题方面有很大助力,总之学习 TCP/IP
是一个重要不紧急的事情,根据本身目标和层次安排。
帧
数据格式交换机上有多个端口(不是 port)供计算机链接,交换机会维护端口与链接这个端口的 PC 的 Mac 地址映射表。当交换机接受到数据的时候,会根据目的 Mac 地址,发送到对应的端口上,而后通过网线发送到目的 PC。
交换机连接多个电脑组成一个局域网,交换机连接交换机又能够组成一个更大的局域网。
好比 A、B 交换机各有 100 个端口,A 连接了 99 个PC,而后 B 交换机连接99 个,再将其中的一个端口 A/B 之间相互链接组成一个更大的局域网。
路由器工做在网络层,主要用于将一个网段数据包转发到另外一个网段内。路由器上也会有个几个 LAN
口 (Local Area Network,局域网),用于创建局域网。还会有一个 WAN
(Wide Area Network,广域网),链接运营商的网络。
路由器也具备交换机的功能,只是 LAN 口 比较少,能够接入的电脑比较少。
当 PC
或者 手机
链接无线路由器时也会给 PC
分配一个局域网 IP
,子网掩码,网关等。
我住的地方的网络拓扑图以下:
当手机与电脑通讯的时候,实际经过 LAN
口走局域网通讯。
当手机访问 维基百科
时,实际是经过路由器跳入到光猫网段,再经过光猫跳入到小区运营商的网络,… 到维基百科的服务器上。
只要须要有 IP 地址的设备(光猫,路由器,PC,手机)都须要有网卡,网卡出厂自带有 Mac 地址。IP 和 Mac 地址的做用后文中会介绍。
<font color=red>这部份内容是我本身的理解,我没有在网上找到资料佐证,请谨慎对待</font>
其实交换机和路由器硬件差异不大,只是硬件上的软件决定了它能作什么。
2 层交换机上的软件(只有数据链路层)可能只作解析帧,拿到 mac 地址,而后查找当前交换机的端口对应的 mac 地址,而后从对应的端口传递过去。
路由器(有网络层和数据链路层),当拿到数据包的时候,发现目的 mac 地址不是本身,就会将数据包经过 LAN 口发送出去。
当发送的数据包的 目的 MAC 地址
是当前路由器上 MAC地址
,路由器就会对其解包,拿到数据包 目的 IP
,而后根据 目的 IP
匹配下一跳 mac 地址,封包为新的帧数据发送出去。
从发送端发送数据的时候,数据通过每层的封包,经物理层传送到接收端。接收端收到数据包,一层一层进行拆包,而后将数据数据发送给我接收端的应用层的应用程序。
一般咱们说的第一层就是 物理层
,第二层是 链路层
…...
源 MAC 地址
就是发送端的 MAC 地址
,目标 MAC 地址不是最终的 MAC 地址,是下一跳节点的 MAC 地址。
类型
指的是这个以太网帧中的 数据
是何种类型的数据,好比 IPV4,IPV6。而后调用对应的接口进行处理。
数据链路层传输的帧是有大小限制的(64-1518 字节),能传输的数据的最大值就是 最大传输单元
,简称 MTU
,Maximum Transmission Unit
。这个值在以太网中一般是 1500。
# 查看网卡对应的 MTU ifconfig -a netstat -i
网络层主要以 IP
协议为主,也有 ICMP
,ARP(在 TCP\IP 模型
中,arp
属于网络层。在 osi 七层模型
,arp
数据链路层。)。
IP
是网络层通讯的标识。可是 IP
不容易记忆,因此出现了 域名
。
访问 DNS
能够将域名解析为 IP
。
能够在本地配置 host
,定义域名和 IP 对应关系,这样就不用解析了。
也能够在电脑配置 DNS 解析时访问的 ip,这样域名解析时就会访问这个服务。
<img src="http://oss.mflyyou.cn/blog/20200801182357.png?author=zhangpanqin" alt="image-20200801182357581" style="zoom:50%;" />
# 解析域名的 ip dig www.mflyyou.cn
IP 地址
又能够分为 IPV4
和 IPV6
,目前使用比较广的是 IPV4
,因此只介绍 IPV4
。
IP 地址
由 32 (2 进制)位组成,32 位被 .
分为了四组。每组 8 位,十进制表示就是 xxx.xxx.xxx.xxx(xxx 取值在 0-255)。
IP 地址
由 网络地址
(网段) 和 主机号
。
同一个网段的电脑用 2 层交互机相连,而后就能够局域网通讯了。
同一个网段内,主机号不能重复,重复主机号的电脑不能上网。
为了便于区分出 IP 在那个网段,引入了子网掩码
(netmask)。IP 地址与子网掩码按位与计算能够得出网段,32 位 中取出网段所在的位,剩余就是主机号能取得值。
IP 中主机号全为 0 就是网段,全为 1 就是广播地址。这两个是不能被分配给电脑的。
IP:192.168.202.116
子网掩码:255.255.252.0
网段为:192.168.200.0
广播地址为:192.168.203.255
IP:192.168.201.56
子网掩码:255.255.252.0
网段为:192.168.200.0
广播地址为:192.168.203.255
网络层是不可靠传输,发送失败的数据包,网络层是不会再发一次数据包,可是会有 ICMP
包回复告诉你发包究竟是什么问题。传输层
能够根据 ICMP
来判断是否须要重发包。
ARP
用于 IP 的 对应的MAC 地址。
目的 IP 在路由表中查询下一跳的 IP,在查询这个 IP 对应的 mac 地址
查询的这个 IP
是当前网段内的 ip,它会经过广播地址发送给当前网段内全部主机,收到这个协议的主机会判断是不是当前主机,是的话就会恢复当前 ip 对应的 MAC 地址。
当我在浏览器输入 wwww.mflyyou.cn
的时候:
一、先解析域名(DNS) www.mflyyou.cn
为 IP
(目的 IP: 47.104.168.20)
二、将目的 IP 与本地路由表中的子网掩码进行按位与,计算出网段与 Destination 匹配,看哪一个匹配度更高,走哪一个条目。都没有匹配到走默认条目(0.0.0.0)
# 查看路由表 route -n
Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 192.168.31.1 0.0.0.0 UG 100 0 0 eth0
三、而后用 arp 查询(有缓存可不查,走缓存)192.168.31.1 对应的 mac 地址
四、数据链路层封装以太网帧数据包中的目的 MAC 地址址就是 192.168.31.1
对应的 mac 地址,而后将数据帧发送到下一个节点(这也就常说的下一跳,数据包发送只是找到当前接节点的下一个节点)
五、到下一个路由器节点,路由器解包,看是发给本身的数据包(根据帧中的目的 MAC 地址与本身的 MAC 地址比较),不是就丢弃了;是的话就会解包拿到 目的 IP
(47.104.168.20),而后在当前路由器上根据路由表查询下一跳,发送给下一个节点;。。。。 直到目的服务器,或者发送的包 TTL
为 0
六、发到目的服务器的网卡上,网卡将数据复制到内核缓冲区,应用程序从缓冲区中读取数据
<font color=red>IPv4 数据结构</font>
<img src="http://oss.mflyyou.cn/blog/20200802000153.png?author=zhangpanqin" alt="image-20200802000153692" style="zoom:50%;" />
图来自《图解 TCP/IP》
Data
(实际发送的数据)是什么类型的数据,1 标识 ICMP, 4 标识 IP, 6标识 TCP, 17 标识 UDP。根据这个挂载协议程序就知道调用哪些接口来进行后续的处理了。数据链路层中 以太网数据帧
的 MTU
是 1500 字节,限定了 IP 数据包最大为 1500 字节。而后去掉 IP 包首部 20 字节,通常 IP 数据包发送的数据为 1480 字节。
当咱们发送一个 3058 字节的 IP 数据包时,这显然大于了数据链路层的 MTU (1500 字节)。因此网络层会对大于链路层MTU 的数据包进行分片。拆分一个一个的1500 的数据包发送接收端,接收端接收到这三个包,在汇聚成一个完成的,在调用传输层接口。
# 会发送 3050 字节数据与 8 字节的 ICMP 首部,这个命令会总共发送 ip 数据大小 3058 字节。 ping -s 3050 www.mflyyou.cn
<img src="http://oss.mflyyou.cn/blog/20200801230141.png?author=zhangpanqin" alt="image-20200801230141070" style="zoom:50%;" />
<img src="http://oss.mflyyou.cn/blog/20200801230528.png?author=zhangpanqin" alt="image-20200801230528418" style="zoom:50%;" />
<img src="http://oss.mflyyou.cn/blog/20200801230423.png?author=zhangpanqin" alt="image-20200801230423653" style="zoom:50%;" />
经过 wireshark
抓包能够看到,IP 数据包的首部长度占了 20 字节,实际每次发送数据为 1480 字节,最后一次发送了 98 字节。
从 Fragment 和 Identification 能够看到这三个包属于同一个 IP 数据包,而且从 Fragment offset
能将这三个包合成一个完成的网络层数据包。
TCP
是面向连接的,可靠的,全双工协议。
面向链接就是发送以前,须要创建一个连接通道,数据都是在这个连接中发送。
网络层
是不可靠协议,数据发送失败是不会重发的。
TCP
协议中发送端会记录发送的那些数据包被客户端收到了。接收端接受数据以后,会回复一个 ACK
包(由数据格式中的控制位决定),确认应答号告诉发送端哪些数据包接收到了。
发送端
发送了数据包以后,这个包会有一个重发倒计时,在这个倒计时内没有收到接收端
回复 ACK
包,就会再重发一个数据包。若是是 HTTP 请求
,就至关于一样的数据请求了两次。
咱们知道支付接口都要求幂等性,有一部分缘由是由于这个超时重发。发送端发送了请求,接收端处理好业务以后回复的 ACK
包超时,发送端超时重发这个请求。若是不保证接口的幂等性,那么扣钱就会扣两次。
咱们要作的就是保证这个重发 n+1 次再也不扣用户的钱,通常会用一个 token 来判断是否是重复请求,重复就不走扣款处理了,直接返回已经支付,保证接口的幂等性。或者用一个帐单流水来保证幂等性。
链接既然须要创建,那么也会有链接断开。断开链接需双方协商好以后断开链接,不能单方面关闭而无论对方。由于创建链接以后占用的计算机资源须要释放掉。你单方面强制断开链接释放了资源,可是对方不知道须要断开链接,分配的计算机资源一直占用那就是不可靠协议了。因此 TCP
有四次挥手断开链接。
全双工就是链接两边均可以主动发送接受数据,而不是轮训访问有没有数据到达。
首先咱们要先了解 TCP
数据格式,才能更容易知道 TCP 的工做原理。
<img src="http://oss.mflyyou.cn/blog/20200802000246.png?author=zhangpanqin" alt="image-20200802000246545" style="zoom:50%;" />
占用 2 个字节。标识 发送端
程序的端口号,当接收端须要回复消息的时候,须要带上这个端口号。
占用 2 个字节。标识 接收端
程序的端口号,能够传递给监听在这个端口的程序
占用 6 位,不满一个字节。标识当前 TCP
包是什么包,在通讯过程当中有一些特殊做用。
SYN
表示但愿创建三次握手连接,并初始化序列号。
ACK
对收到数据包的应答确认。接收端接受数据以后,会回复 ACK
包,发送端从其上 确认应答号
知道接收端哪些数据已经接受了。
FIN
表示没有数据发送了,但愿断开链接
PASH
接收端接收到这个数据包须要马上传递给应用层,不能等待接收更多的数据包
RET
连接出现异常,须要强制断开链接
URG
表示包中有须要紧急处理的数据
占用 4 个字节。TCP
三次握手的时候,发送端和接收端各自初始化(随机的)本身的 `序列号。
咱们能够这样理解,发送端发送的数据就是一个字节数组,这个数组中每一个字节都有一个 序列号
。
发送端和接收端都有本身的序列号,而且不相同,在三次握手的时候本身初始化,而后告知对方。
占用 4 个字节。确认应答号
也是指的序列号,指的是指望发送端下次发送的序列号,这个序列号(确认应答号)以前的数据已经接受处理了。
下图是我抓包创建三次连接,而后我发送三次 1\n
数据。
三次握手,发送端经过发送 SYN
包,发送本身的初始化序列号(893189542),而后发送的每一个字节都会有一个序列号。
接收端发送 ACK
包中的 确认应答号
,指明这个序列号以前的数据我已经接受了。
<img src="http://oss.mflyyou.cn/blog/20200802205000.png?author=zhangpanqin" alt="image-20200802205000890" style="zoom:50%;" />
窗口大小适用于流控的。发送端不能一直发送消息,须要根据个人接受能力来调整发包的速率。
内核会为每一个 TCP/IP
分配读写缓冲区,网卡会从这些读写缓冲区中把数据取走,而后发送。数据大体能够分为这几类。
TCP/IP
是可靠链接,因此它须要记录哪些数据发送已被对方接受了(由确认应答号能够知道),接受的数据会被淘汰掉,节省内存空间。
窗口大小做用:接收端会经过 ACK
告诉 发送端
调整窗口大小。
当窗口中的数据全都是 已发送未确认数据
时,发送端不能再发送新的数据,必须等待窗口空出位置来。
当有一个数据包被确认了,发送端就能够发送新的数据包。已发送未确认数据
会在超时的时候从新发包。
占用 2 个字节。校验和
用于校验数据包是否损坏。每一个数据包都一个 校验和
,接收端
接收到数据以后,使用相同的算法对数据计算出一个值,而后和 校验和
比较,不同说明数据在传输过程当中损坏了,接收端
会丢弃这个包,等待 发送端
从新发这个包。
链路层能发送的最大以太网帧为 1500 字节,MTU 为 1500。
IP 数据包能发送的最大数据 = MTU - IP 首部大小(通常 20 字节),IP 数据包超过这个 1500 字节会分片
TCP 传输数据以段 (Segment) 为单位。
TCP 为了不分片,会主动将数据分片以后交给网络层。 TCP 能传输的最大分段(只是数据不包括首部)称之为 Max Segment Size,简称为 MSS。
MSS = MTU - IP 首部大小 - TCP 首部大小
在以太网中 TCP 的 MSS = 1500(MTU) - 20(通常 IP 首部大小) - 20(通常 TCP 首部大小)= 1460,这个值须要根据首部计算
MSS 值在三次握手时,会经过 MTU 计算的。
<img src="http://oss.mflyyou.cn/blog/20200802212532.png?author=zhangpanqin" alt="image-20200802212532628" style="zoom: 33%;" />
图片来自 码出高效:Java 开发手册
为何是三次握手创建链接呢?不少面试官也会问。这实际上是可靠链接的最少握手次数。
<img src="http://oss.mflyyou.cn/blog/20200802212808.png?author=zhangpanqin" alt="image-20200802212808724" style="zoom:50%;" />
图片来自 码出高效:Java 开发手册
这里还有个 全链接队列和半连接队列 的知识点
<img src="http://oss.mflyyou.cn/blog/20200802213247.png?author=zhangpanqin" alt="image-20200802213247725" style="zoom: 33%;" />
图片来自 码出高效:Java 开发手册
CLOSE_WAIT
是收到对方 FIN
包以后,回复 ACK
以后进入的状态。以后不会接受数据了,进行已收数据的业务处理以后,在发送一个 ACK+FIN
,进入 LASK_ACK
,而后等待对方发送 ACK
,超时没有等到,会重试发送(内核能够配置重试发送次数)。当你发现服务端有大量的 CLOSE_WAIT
连接,服务端的代码有问题,须要排查。
TIME_WAIT
的连接多的话,服务端能够优化,否则这个连接会占用很长时间,在高并发的时候,会致使没有资源释放的慢。
MSL 为 Maximum Segment Lifetime,在 centos 中默认值为 60s
# sysctl -a | grep tcp_fin_timeout # 推荐小于 30,也不能过小,15-30 net.ipv4.tcp_fin_timeout = 60
说明 A 机器连接会在 120 s 以后才能释放。这个是为了保证 B 机器 能接收到最后一个 ACK
,当处于 LAST_ACK
的超时没有收到A 发来的 ACK
的话,会重试发送一个 FIN+ACK
。这个 2MSL 也是为了最大限度保证 B 机器正常关闭。
三次握手创建链接
和 四次挥手断开链接
须要结合抓包工具本身分析一下,理解会更深入。
Wireshark
抓包分析是很厉害的,mac os
和 linux
都有命令行程序 tshark
,能够在服务器用 tshark
抓包,拿到本地来分析。
抓包的时候必定要指定抓什么包,什么包都抓的话,一会你的电脑内存就飙升好多(别问我为啥知道,问就是 30g 内存都让它吃了)。
Wireshark
有个 抓包过滤器
和 显示过滤器
。抓包的时候指定抓什么包这是 抓包过滤器的做用
,抓包以后显示显示那些内容那是 显示过滤器的做用
# -i 指定那个网卡 # -f 指定抓包过滤器 # -Y 显示过滤器 # -w 指定抓包数据到文件,没有 -w 输出屏幕 # -V 显示 TCP/IP 每层包的详细信息,建议将抓包的文件在图形化界面中查看,不指定 -V tshark -i en0 -f "tcp" -Y "http" # 抓取访问 www.mflyyou.cn 的包 tshark -i en0 -w a.pcap -f "host www.mflyyou.cn" # 指定抓那个协议 tcp,ip,icmp,arp,udp tshark -i en0 -f "tcp" # host 指定域名或者 ip # port 指定端口 # 访问 www.mflyyou.cn 的包,或者 icmp. ping www.baidu.com 也会被抓到 tshark -i en0 -f "host www.mflyyou.cn || icmp" tshark -i en0 -f "port 80" # 条件之间支持逻辑运算符 || && ! # 抓取 ssh 连接的包 tshark -i en0 -f "host www.mflyyou.cn && port 22"
《图解 TCP/IP》
本文由 张攀钦的博客 http://www.mflyyou.cn/ 创做。 可自由转载、引用,但需署名做者且注明文章出处。如转载至微信公众号,请在文末添加做者公众号二维码。微信公众号名称:Mflyyou