计算机组成与系统结构,数据结构与算法,计算机网络,操作系统,编译原理都是计算机基础课程。简单总结下计算机通讯网络的各种基础概念,与通讯过程。通讯双方使用的通讯协议
TCP/IP五层模型
只要是在网络上跑的包,都是完整的。可以有下层没上层,绝对不可能有上层,没下层。至于为什么要分层,程序复杂了就要分层一个概念,降低开发成本,把各个成之间扮演的角色分离出来。
数据传输过程:
1 | 源主机 路由器 交换机 目标主机 |
助记:传输层(Transmission) 网络层(Internet)
TCP/IP 4层模型
应用层
传输层
网络层
网络接口层(数据链路+物理层)
数据链路层:
在网络发展过程中先后出现三种链路层包结构:
- Ethernet
目的MAC、源MAC、类型、数据、FCS - 802.3 LLC
目的MAC、源MAC、长度、目的服务加入点Snap、源服务加入点Snap、控制LLC、FCS(32bitCRC) - 802.3 LLC Snap
目的MAC、源MAC、长度、目的服务加入点Snap、源服务加入点Snap、控制LLC、Snap、FCS(32bitCRC)
socket
- select、poll、epoll、iocp
- MTU: Maxitum Transmission Unit 最大传输单元
- MSS: Maxitum Segment Size 最大分段大小
MTU= MSS+TCP层头部长度+IP层头部长度 (MSS是指应用层在一个数据包内最大能传输的字节数,MTU是指IP层在一个数据包内最大能传输的字节数)
举个例子:如果你要搬家,需要把东西打包,用车运走。这样的情况下,车的大小受路的宽度限制;箱子的大小受车限制;能够搬运的东西的大小受箱子的限制。这时可以将路的宽度理解成第二层的MTU,车的大小理解成第三层的MTU,箱子的大小理解成第四层的MTU,搬运的东西理解成MSS
我们可以把MSS设置成1300,解决大部分环境问题,TCP建立连接握手的时候会协商各自支持的最大包大小
客户端IP | 客户端Port | 服务端IP | 服务端Port | |
---|---|---|---|---|
socketfd | *.* |
* |
192.168.33.228 | 80 |
socketfd1 | 192.168.33.226 | 13455 | 192.168.33.228 | 80 |
理论上一台电脑并且只有一个网卡也只配置了一个IP地址可以对同一个服务端建立65535(端口数量)个连接,一个服务端可以有IP*65535
(2^32*65535
)个客户端连接
服务端:Socket()→Bind()→Listen()→Accept→Receive()/Send()→Close()
客户端: Socket()→Connect()→Send()/Receive()→Close()
滑动窗口(Sliding Window):一发一确认太慢,所以弄出了滑动窗口,可以同时发送多个报文、进行多个报文的确认,确认后窗口向右移动。结构体struct tcp_sock中有很多成员数据跟滑动窗口协议相关,max_window记录来自对端通告的窗口的最大值,snd_wnd是发送窗口的大小
物理层
以太网V2格式数据帧 : 链路层
Destination Source Type DataAndPad FCS
6 6 2 46~1500 4
目的MAC:48bit ,可以广播、单播、组播
源MAC: 48bit
类型:2字节,识别上层协议
数据:被封装数据,46-1500字节
FCS: 32比特的CRC
IP: 网络层
1 | 0 1 2 3 |
Source Address 和 Destination Address 在IP层, Source Port 和 Destination Port 在传输层
当发送端的MTU大于到目的路径链路上的MTU时就会被分片
Flags取值:
Bit 0: 保留,必须是0
Bit 1: (DF) 0 = 可能分片, 1 = 不分片
Bit 2: (MF) 0 = 最后的分片, 1 = 还有分片
TCP或UDP : 传输层
TCP
1 | 0 1 2 3 |
数据偏移(Data Offset),4bits,单位为4字节,它指出TCP报文头长度
UDP
1 | 0 7 8 15 16 23 24 31 |
UDP有报文长度Length字段,TCP没有报文长度字段
应用层:HTTP
数据帧{IP包{TCP或UDP包{Data}}}
以太网的物理特性决定了数据帧的长度范围为(46+18)→(1500+18),其中的18是数据帧的头和尾,也就是说数据帧的内容最大为1500,即MTU(Maximum Transmission Unit)为1500
在网络层,因为IP包的首部要占用20字节,所以这的MTU为1500-20=1480
在传输层,对于TCP包的首部要占用20字节,所以这的MTU为1480-20=1460
MTU 和 MSS 关系
最大传输单元MTU(Maximum Transmission Unit,MTU)
(1)以太网和802.3对数据帧的长度都有一个限制,其最大值分别是1500和1492个字节。链路层的这个特性称作MTU。
如果IP层有一个数据要传,且数据的长度比链路层的 MTU还大,那么IP层就要进行分片(fragmentation)。
(2)把一份IP数据报进行分片以后,由到达目的端的IP层来进行重新组装,其目的是使分片和重新组装过程对运输层(TCP/UDP)是透明的。
(3)尽管IP分片过程看起来透明的,但有一点让人不想使用它:即使只丢失一片数据也要重新传整个数据报。why?
因为IP层本身没有超时重传机制,由更高层(比如TCP)来负责超时和重传。
1 | Iface MTU RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg |
MSS(Maxitum Segment Size)最大分段大小的缩写,是TCP协议里面的一个概念
(1)MSS就是TCP数据包每次能够传输的最大数据分段。为了达到最佳的传输效能TCP协议在建立连接的时候通常要协商双方的MSS值,
这个值TCP协议在实现的时候往往用MTU值代替(需要减去IP数据包包头的大小20Bytes和TCP数据段的包头20Bytes)所以往往MSS为1460。
通讯双方会根据双方提供的MSS值得最小值确定为这次连接的最大MSS值。
(2)TCP无所谓分段,因为每个TCP数据报在组成前其大小就已经被MSS限制了,所以TCP数据报的长度是不可能大于MSS的,由它形成的IP包的长度也就不会大于MTU,因此不用IP分片
简而言之:
- IP分片产生的原因是网络层的MTU;TCP分段产生原因是MSS.
- IP分片由网络层完成,也在网络层进行重组;TCP分段是在传输层完成,并在传输层进行重组. //透明性
- 对于以太网,MSS为1460字节,而MUT往往会大于MSS.
故采用TCP协议进行数据传输,是不会造成IP分片的。若数据过大,只会在传输层进行数据分段,到了IP层就不用分片。
示例
PC–1–TP_Link –2–宽带路由器–3–电信公司—-微信服务器
在节点3处由于有额外8个字节的PPPoE头部封装,从服务器下行的1500字节IP包将会变成1508字节,按理说电信公司会依据:
IP包里的DF = 0 ,分片传输,这样不会有问题。
IP包里的DF = 1,丢弃,并发送ICMP告诉服务器包太大了,但是有时ICMP无法到达服务器(禁止ICMP),微信服务器一直重传1500字节包含图片的IP包,然后被丢弃,就会造成流量黑洞
解决方案:
修改节点1处的MTU = 1492 或更小。两端都需要修改,保持对称。这样TCP协商MSS,可以协商成1452。
这样上下行数据途径节点3处,整个包将变为: PPPoE + IP Header + TCP Header + TCP segment (MSS) = 8 + 20 + 20 + 1452 = 1500,正好等于MTU 1500,无需分片就不会有问题。
手工测试发现MTU值
网络层1500字节=20字节IP头+8字节ICMP头+1472字节的载荷
linux命令:
1 | ping -c 3 -s 1472 -M do 192.168.33.113 |
测试出MTU=1500
TCP-MSS 协商
程序会协商TCP-MSS,用最小的一方进行发送。
当时经过运营商网络,还是会被拆分成更小的包。
Server7 的MTU为9000字节,Server8 的MTU 2000 字节
Server7 MSS值为9000-40=8960字节
Server8 MSS值为2000-40=1960字节
Server7 和 Server8 会达成协议,两者中取Server8 的MSS值,即1960字节
最终达到了TCP的单包最大值2014字节,即TCP-MSS值
2014字节=14字节二层帧头+20字节IP头+20字节标准TCP头+12字节TCP可选项+1948的负载
此处的TCP-MSS仍然为1960字节,只是被拆分成为了12字节的TCP可选项+1948的负载
在运营商网络:一个完整的2014字节数据包就这样被一分为二:1514字节数据包+534字节数据包
解决方案:拦截TCP-MSS值set security flow tcp-mss all-tcp mss 1460
PathMTU Discovery
主机发送的数据包就会出现如下结局:
如果主机本地链路的MTU大于端到端链路中某一点的MTU值,那么这个数据包因为有DF=1的原因,会被丢弃。
而我们需要重点说说结局1。在结局1中,当链路中路由器丢弃此数据包时,此路由器会返回一个ICMP 的Destination Unreachable,Fragment Needed(目标不可达,需要分片)的消息给源主机如果路由器本地链路的MTU为整个端到端链路中最小值时,数据包很幸运的被送达目的地。
这个ICMP包中另有玄机,它同时也包含了此链路路由器的下一跳MTU值!
依赖ICMP协议
可能出现问题:
防火墙把ICMP干掉。
解决方案:大家可以考虑在边界防火墙上放行ICMP协议
夹心饼干,三层设备之间夹杂二层设备
数据包分片只存在于ISO第三层的网络设备上,而二层节点只查看二层帧(数据链路层 MAC地址),其并没有重写三层IP头的能力,更不知晓三层IP层的具体网络细节
当数据包过大以后,二层设备是不会也不能去分片数据包,因为在配置的时候,没有赋予它任何此二层VLAN的IP信息,导致其没有写IP数据包的能力去执行数据包分片
二层交换机:
(1) 当交换机从某个端口收到一个数据帧,它先读取包头中的源MAC地址,这样它就知道源MAC地址的机器是连在哪个端口上的;
(2) 再去读取数据帧头中的目的MAC地址,并在地址表中查找相应的端口;
(3) 如表中有与这目的MAC地址对应的端口,把数据帧直接复制到这端口上;
(4) 如表中找不到相应的端口则把数据帧广播到所有端口上,当目的机器对源机器回应时,交换机又可以学习一目的MAC地址与哪个端口对应,在下次传送数据时就不再需要对所有端口进行广播了。不断的循环这个过程,对于全网的MAC地址信息都可以学习到,二层交换机就是这样建立和维护它自己的地址表。
解决方案:
- 方案一:请务必保证路由器之间的二层设备MTU值与三层路由器设备MTU相同,所以可以改三层交换机配置
- 方案二:客户端或者服务端程序设置MSS值,比如1300
setsockopt(sockfd, IPPROTO_TCP, TCP_MAXSEG, (int *)&maxseg, sizeof(maxseg));
TIME_WAIT
TIME_WAIT 是这么一种状态:TCP 四次握手结束后,连接双方都不再交换消息,但主动关闭的一方保持这个连接在一段时间内不可用。原因:TIME_WAIT 是为了保证全双工的 TCP 连接正常终止,TIME_WAIT 的存在是为了保证网络中迷失的数据包正常过期
开发注意事项
对于使用异步返回的情况请求与返回如何对应上?指令与返回加上原样返回字段(流水号或者随机码)根据情况在内存自己维护
根据具体使用场景尽量简化与统一调用协议,比如把自定义tcp改用http+callback或者直接http post返回位置信息
服务端开发一般监听0.0.0.0或者:::,或者band监听服务器上所有ip,如果指定ip就只能通过那个ip访问。只监听回环地址(Loopback Address)127.0.0.1只能本机请求127.0.0.1
netstat只看到:::1521 ipv6的监听,是因为默认ipv6也能处理ipv4,如果禁用AP_ENABLE_V4_MAPPED就会出现两个监听
问题
UDP为什么没有粘包问题?
UDP有包头、包头里面有长度。他直接是一端发送什么数据,直接就发出去了 ,既然他不会对数据合并,每一个数据包都是完整的也就没有粘包一说了。UDP不存在粘包问题,是由于UDP发送的时候,没有经过Negal算法优化,不会将多个小包合并一次发送出去。另外,在UDP协议的接收端,采用了链式结构来记录每一个到达的UDP包,这样接收端应用程序一次recv只能从socket接收缓冲区中读出一个数据包。也就是说,发送端send了几次,接收端必须recv几次(无论recv时指定了多大的缓冲区)。
分包产生的原因就简单的多:可能是IP分片传输导致的,也可能是传输过程中丢失部分包导致出现的半包,还有可能就是一个包可能被分成了两次传输,在取数据的时候,先取到了一部分(还可能与接收的缓冲区大小有关系),总之就是一个数据包被分成了多次接收。