1七层模型
注意TCP/IP协议簇在表示层和会话层是没有协议的
[图片上传失败...(image-6586a1-1612791064902)].jpg)
- 物理层:通过物理介质传输比特流, 数模转换和模数转换
- 数据链路层:将比特组合成帧,使用链路层地址 (以太网使用MAC地址)来访问介质,并进行差错检测。
- 错误检测和纠正,确保数据传输的可靠性,将比特数据组成了帧,交换机工作在这层,对帧解码,根据帧中的数据发送到网络接收方
- 网络层:IP寻址找到目标节点选择最佳路径来建立两个主机之间的连接
- 传输层:传输层建立了主机端到端的链接,传输层的作用是为上层协议提供端到端的可靠和透明的数据传输服务,包括处理差错控制和流量控制等问题。
- 应用层:为用户直接提供各种网络服务
数据链路层的差错检测的目的是做到"无比特差错"
运输层的差错检测的目的是做到"无传输差错"数据链路层的差错检测是早起的不可靠信道导致
首先,我们需要知道,我们程序的数据首先会打到TCP的Segment中,然后TCP的Segment会打到IP的Packet中,然后再打到以太网Ethernet的Frame中,传到对端后,各个层解析自己的协议,然后把数据交给更高层的协议处理。
2 TCP
注意:
- TCP的包是没有IP地址的,那是IP层上的事。但是有源端口和目标端口。
- 一个TCP连接需要四个元组来表示是同一个连接(src_ip, src_port, dst_ip, dst_port)准确说是五元组,还有一个是协议。但因为这里只是说TCP协议,所以,这里我只说四元组。
三次握手
IP是无连接的,不会占用两个计算机之间的通信线路,IP分包,不可靠
在建立连接之前,服务器先创建TCB(传输控制块),准备接受客户进程的连接请求,处于LISTEN(监听)状态
A首先创建TCB,然后向B发出连接请求,SYN置1,同时选择初始序号seq=x,进入SYN-SEND(同步已发送)状态
- syn_sent
- syn_rcvd
为什么要三次握手
- 主要是确认序号
- 次要节省资源
- 为了实现可靠数据传输, TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤。如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认
- 如果采用两次握手,那么只要服务器发出确认数据包就会建立连接,但由于客户端此时并未响应服务器端的请求,那此时服务器端就会一直在等待客户端,这样服务器端就白白浪费了一定的资源。若采用三次握手,服务器端没有收到来自客户端的再此确认,则就会知道客户端并没有要求建立请求,就不会浪费服务器的资源。
四次挥手
正常报文: ACK ack seq
为什么有TIME_WAIT(2MSL)状态
MSL(Maximum Segment Lifetime)报文最长存活时间
- 保证服务端正常关闭。因为有可能最后一个ACK丢失,所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。
- 2MSL可以保证本连接持续的时间内所产生的所有报文段从网络中消失。避免新旧连接混淆
MSL(Maximum Segment Lifetime)报文最大生存时间
Windows : MSL = 2 min
linux(Ubuntu, CentOs) : MSL = 60s
Unix : MSL = 30s
出现大量的Time_wait状态的原因
https://coolshell.cn/articles/11564.html
TIME_WAIT 表示主动关闭,是服务器自己可控的
因为linux分配给一个用户的文件句柄是有限的,而TIME_WAIT和CLOSE_WAIT两种状态如果一直被保持,那么意味着对应数目的通道就一直被占着,一旦达到句柄数上限,新的请求就无法被处理了,接着就是大量Too Many Open Files异常,tomcat崩溃。。。
解决方法:
- 修改最大文件句柄数
- server端应尽量减少主动关闭连接
出现大量的Close_wait状态的原因
CLOSE_WAIT 表示被动关闭
原因:接收到客户端的FIN,回复ACK。此时服务器进入Close_wait,直到服务器发起关闭连接。
所以原因是服务器没有及时关闭连接。即:对方关闭socket连接,我方忙于读或者写,没有及时关闭连接
解决办法:
- 检查代码,特别是释放资源的代码
- 检查配置,特别是处理请求的线程配置
可靠传输
- 序号
- 确认
- 超时重传
流量控制
基于滑动窗口。TCP使用滑动窗口做流量控制和乱序重排,匹配发送方的速率和接收方的速率
- 发送方和接收方各有一个窗口
- 接收方通过设置自己接收缓存的大小,动态的调整发送方的发送窗口大小,这就是接收窗口rwnd,即调整tcp报文段首部中的窗口字段值,来限制发送方向网络注入报文的速率
- 发送方根据当前网络拥塞程序的估计而确定窗口值,即cwnd (congenstion window)
- 发送方的发送窗口取min(cwnd, rwnd)
拥塞控制
防止过多的数据注入网络
针对的是cwnd(congestion window),不是发送窗口
慢开始 & 拥塞避免
慢开始
- cwnd指数增长至ssthresh,此时改用拥塞避免算法
拥塞避免
- cwnd加法增大
当网络拥塞(没有按时收到确认,重传计时器超时)
- cwnd回1
- ssthresh = cwnd / 2
- 这样做的目的是迅速减少网络中的分组数
快重传 & 快恢复
对慢开始 & 拥塞避免的改进
- 快重传:当发送方连续收到三个重复的ACK报文时,直接重传对方尚未收到的报文段,不必等到计时器超时
- 快恢复:在上面这种情况下,只是丢失个别报文段,而不是网络拥塞。因此执行快恢复
- 令 ssthresh = cwnd = cwnd /2
- 此时直接进入拥塞避免:即cwnd加法增大
- 由于跳过了cwnd从1开始,所以被称为快恢复
流量控制和拥塞控制的区别:
- 流量控制是为了匹配收发仿的速率,让接收方能来得及接收
- 拥塞控制是为了降低整个网络的拥塞程度。
粘包
场景1:
客户端和服务器建立一个连接,客户端连续发送两条消息,客户端关闭与服务端的连接。
服务端一共就读到一个数据包,这个数据包包含客户端发出的两条消息的完整信息,这个时候基于之前逻辑实现的服务端就蒙了,因为服务端不知道第一条消息从哪儿结束和第二条消息从哪儿开始,这种情况其实是发生了TCP粘包。
场景2:
服务端一共收到了两个数据包,第一个数据包只包含了第一条消息的一部分,第一条消息的后半部分和第二条消息都在第二个数据包中,或者是第一个数据包包含了第一条消息的完整信息和第二条消息的一部分信息,第二个数据包包含了第二条消息的剩下部分,这种情况其实是发送了TCP拆,因为发生了一条消息被拆分在两个包里面发送了,同样上面的服务器逻辑对于这种情况是不好处理的。
发生TCP粘包、拆包主要是由于下面一些原因:
- 应用程序写入的数据大于套接字缓冲区大小,这将会发生拆包。
- 应用程序写入数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络上,这将会发生粘包。
- 进行mss(最大报文长度)大小的TCP分段,当TCP报文长度-TCP头部长度>mss的时候将发生拆包。
- 接收方法不及时读取套接字缓冲区数据,这将发生粘包。
粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都很少,通常TCP会根据negal优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。
如何解决拆包粘包
既然知道了tcp是无界的数据流,且协议本身无法避免粘包,拆包的发生,那我们只能在应用层数据协议上,加以控制。通常在制定传输数据时,可以使用如下方法:
- 使用带消息头的协议、消息头存储消息开始标识及消息长度信息,服务端获取消息头的时候解析出消息长度,然后向后读取该长度的内容。
- 设置定长消息,服务端每次读取既定长度的内容作为一条完整消息。
- 设置消息边界,服务端从网络流中按消息编辑分离出消息内容。
a)先基于第三种方法,假设区分数据边界的标识为换行符"\n"(注意请求数据本身内部不能包含换行符),数据格式为Json,例如下面是一个符合这个规则的请求包。
{"type":"message","content":"hello"}\n
注意上面的请求数据末尾有一个换行字符(在PHP中用双引号字符串"\n"表示),代表一个请求的结束。
b)基于第一种方法,可以制定,首部固定10个字节长度用来保存整个数据包长度,位数不够补0的数据协议
0000000036{"type":"message","content":"hello"}
c)基于第一种方法,可以制定,首部4字节网络字节序unsigned int,标记整个包的长度
****{"type":"message","content":"hello all"}
其中首部四字节*号代表一个网络字节序的unsigned int数据,为不可见字符,紧接着是Json的数据格式的包体数据。
SynFlood
在三次握手过程中,服务器发送SYN,ACK之后,收到客户端的ACK之前的TCP连接称为半连接(half-open connect)。此时服务器处于Syn_RECV状态。当收到ACK后,服务器转入ESTABLISHED状态。
Syn攻击就是 攻击客户端 在短时间内伪造大量不存在的IP地址,向服务器不断地发送syn包,服务器回复确认包,并等待客户的确认,由于源地址是不存在的,服务器需要不断的重发直 至超时,这些伪造的SYN包将长时间占用未连接队列,正常的SYN请求被丢弃,目标系统运行缓慢,严重者引起网络堵塞甚至系统瘫痪。
目前,Linux下默认会进行5次重发SYN-ACK包,重试的间隔时间从1s开始,下次的重试间隔时间是前一次的双倍,5次的重试时间间隔为1s, 2s, 4s, 8s, 16s, 总共31s, 称为指数退避,第5次发出后还要等32s才知道第5次也超时了,所以,总共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 63s, TCP才会把断开这个连接。由于,SYN超时需要63秒,那么就给攻击者一个攻击服务器的机会,攻击者在短时间内发送大量的SYN包给Server(俗称SYN flood攻击),用于耗尽Server的SYN队列。
检测SYN攻击非常的方便,当你在服务器上看到大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击。在LINUX下可以如下命令检测是否被Syn攻击:
`[root@rsync_test ~]``# netstat -n | awk '/^tcp/ {++sam[$NF]} END {for(num in sam)print num,sam[num]}'``TIME_WAIT 30``FIN_WAIT1 1``ESTABLISHED 615``SYN_RECV 2`
防护措施
- 缩短SYN Timeout时间
- Syn Cache
服务端在收到SYN报文时,在一个专用HASH表cache中保存这种半连接信息,直到收到正确的回应ACK报文再分配TCB。这个开销远小于TCB的开销。还需要保存序列号。 - Syn Cookie
- 使用对方的IP、端口、己方IP、端口的固定信息,生成 Sequence Number
- SYN 队列满后,通过 tcp_syncookies 参数回发 SYN cookie
- tcp_syncookies是一个开关,是否打开SYN Cookie功能,该功能可以防止部分SYN攻击。
- 若为正常连接则 client 会回发 SYN cookie,直接建立连接分配 TCB 。
- 攻击者不会回复所以,正常的回复了SYN cookie 就可以在队列外直接建立
- 它的基本原理非常简单,那就是“完成三次握手前不为任何一个连接分配任何资源”
SYN Cookie是对TCP服务器端的三次握手做一些修改,专门用来防范SYN Flood攻击的一种手段。它的原理是,在TCP服务器接收到TCP SYN包并返回TCP SYN + ACK包时,不分配一个专门的数据区,而是根据这个SYN包计算出一个cookie值。这个cookie作为将要返回的SYN ACK包的初始序列号。当客户端返回一个ACK包时,根据包头信息计算cookie,与返回的确认序列号(初始序列号 + 1)进行对比,如果相同,则是一个正常连接,然后,分配资源,建立连接。
SYNC COOKIES 就是这个序列号的一
个特殊实现
* 初始 5 位: t mod 32,t 是时间戳
* 接下 3 位: mss 的编码值
* 最后 24 位: 服务器 Port/ip,客户端 Port/ip 值的 hash
客户端收到这个序列号 n 之后,发送 n + 1 给服务器,服务器通过 n 来比对 sync
cookie
在开启了 sync cookie 的情况下,sync queue 满了的时候,连接再也不被丢掉,而是直
接返回相应的 SYN/ACK 报文,看起来就像 sync queue 增大了一样。
连接队列
服务器端也会维护两种队列,处于SYN_RCVD状态的半连接队列,而处于ESTABLISHED状态但仍未被应用程序accept的为全连接队列。如果这两个队列满了之后,就会出现各种丢包的情形。
3 TCP与UDP
- TCP面向连接;UDP是无连接的
- TCP提供可靠的服务。UDP尽最大努力交付
- TCP面向字节流;UDP是面向报文的
- TCP有拥塞控制。UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低
- 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
4 IP
寻址
A -> B
A将自己的ip和子网掩码与运算得到网段,将B的ip与子网掩码与运算得到子网段,对比。
-
如果是同一网段,直接发到B
- ARP广播,找到B的mac地址
-
如果是不同网段
- 查找路由表如何到达目的网段,目的IP与各网络的掩码与运算
- 直接交付
- 间接交付
- 默认路由
- 查找路由表如何到达目的网段,目的IP与各网络的掩码与运算
IP数据包分片
各层数据包长度:
HTTP: 无
TCP: 无,依靠IP层分片
UDP: 65535
IP: 65535,有分组长度字段
以太网:1500
以太网就是一种基于总线的、广播式的局域网络,CSMA/CD协议
广域网:576
分片重组是由ip负责,流量控制和有序传输由tcp控制
5HTTP
HTTP1.1
特点:connection: keepalive 长连接
存在效率问题
- 第一个:串行的文件传输
- 第二个:连接数过多
HTTP2
- 多路复用
- 允许同时通过单一的 HTTP/2 连接发起多重的请求-响应消息
- 二进制分帧
- 帧对数据进行顺序
- 传输的每个帧都关联到一个“流”
在HTTP1.1
的协议中,我们传输的request
和response
都是基本于文本的,这样就会引发一个问题:所有的数据必须按顺序传输,比如需要传输:hello world
,只能从h
到d
一个一个的传输,不能并行传输,因为接收端并不知道这些字符的顺序,所以并行传输在HTTP1.1
是不能实现的。
HTTP/2
引入二进制数据帧
和流
的概念,其中帧对数据进行顺序标识,如下图所示,这样浏览器收到数据之后,就可以按照序列对数据进行合并,而不会出现合并后数据错乱的情况。同样是因为有了序列,服务器就可以并行的传输数据,这就是流
所做的事情。
HTTPS
HTTPS数据传输流程
- 服务端配置:采用HTTPS协议的服务器必须要有一套数字证书。这套证书其实就是一对公钥和私钥。
- 客户端发起HTTPS请求:就是用户在浏览器中输入一个https网址,连接到server的443端口。
- 服务端传送证书:即公钥,只是包含了很多信息,如证书的颁发机构,过期时间等。
- 客户端解析证书:由客户端的TLS来完成的,首先会验证公钥是否有效,比如颁发机构,过期时间等。如果证书没有问题,
- 传送随机值(私钥):客户端生成一个随机值,然后用证书对该随机值进行加密并发送给服务端。
- 服务端解密随机值(私钥):服务端用私钥解密后,得到了客户端传过来的随机值。
- 服务端传输加密后的信息:服务端将要传送的信息用随机值(私钥)加密后,传送给客户端。
- 客户端解密信息:客户端使用随机值(私钥)解密服务端发送的加密信息,获取解密后的信息。
区别
- HTTPS需要到CA(电子认证服务)申请证书,HTTP不需要
- HTTPS密文传输,HTTP明文传输
- 连接方式不同,HTTPS默认使用443端口,HTTP使用80端口
6.Socket
Socket并不是一个协议,是位于应用层和传输层中间的一个抽象层
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
以下假设使用tcp
如果面向tcp编程的话,tcp的三次握手,四次挥手,流量控制,可靠传输,拥塞控制等等都太复杂了。而我们其实并不需要自己去做。就有一个封装好的东西帮我们去做,那就是socket。
客户端:
clientfd = socket(...);
connect(clientfd, server_ip, server_port); // 三次握手
send();
receive();
close(clientfd) // 四次挥手
服务端:
listenfd = socket();
bind(listenfd, server_port);
listen(listenfd);
while(true){
connfd = accept(listenfd,...); // 三次握手
receive();
send();
}
8 从输入URL到页面加载发生了什么
- 应用层--发起请求逻辑HTTP
- 传输层--TCP
- 网络层--包路由(IP)
- 数据链路层--帧(似乎是包的容器)
- 物理层--bit流(bitstreams)
假设是HTTP
浏览器会改变scheme为https,使用默认的443端口重新发送请求
地址解析
从url中分解出协议名、主机名、 端口、对象路径等
如果是不填写协议名称,则自动添加http:// .:80 Get方法
如果是https,使用默认的443端口发送请求,进入https过程
DNS解析
采用UDP传输
- 浏览器缓存
- 本地的hosts文件
- 本地域名服务器
- 根域名服务器
- 顶级域名服务器
- 权限域名服务器
- 本地DNS服务器就把请求发至13台根DNS ( . ),根DNS服务器收到请求后会判断这个域名(.com)是谁来授权管理,并会返回一个负责该顶级域名服务器的一个IP。
- 联系负责.com域的这台服务器(顶级域名服务器)。
- 返回baidu.com给本地DNS服务器。
- 本地域名服务器向权限域名服务器查找www.baidu.com的地址(权限域名服务器)。
封装 HTTP 请求数据包
封装 TCP 包并建立连接
三次握手
封装成 TCP 包,浏览器会选择一个大于1024的本机端口向目标IP地址的80端口发起TCP连接请求,建立 TCP 连接(TCP 的三次握手)
将报文段的数据分割成以报文段为单位的数据包进行管理,并为它们编号 <序号,确认号>
流量控制 & 拥塞控制
网络层
寻找路由
ip层数据包分片<1500, 576>
数据链路层
ARP协议
- 查表
- 广播
物理层比特流传输
断开TCP连接
四次挥手
9 ARP
- 把32bit的IP地址变换成48bit的以太网地址
- ARP发送一份称为ARP请求的以太网数据帧给以太网上的每个主机。这个过程称为广播。ARP请求数据帧中包含目的主机的IP地址,其意思是“如果你是这个IP地址的拥有者,请回答你的硬件地址。”
- 目的主机的ARP层收到这个广播报文后,识别出这是发送端在寻问它的IP地址,于是发送一个ARP应答。这个ARP应答包含IP地址及对应的硬件地址;
- ARP高速缓存在它的运行过程中非常关键,我们可以用arp命令对高速缓存进行检查和操作
arp -a # 查看本机arp
11 Rip & OSPF
常见的路由选择协议有:RIP协议、OSPF协议。
- RIP协议(路由信息协议):
- 基于距离向量的路由选择协议
- 路由的度量标准是跳数,最大跳数是15跳,如果大于15跳,它就会丢弃数据包
- 仅和相邻路由器交换当前路由器所知道的全部信息
- 应用层协议,使用UDP传输数据,端口520
- OSPF协议(开放最短路由优先):
- 链路状态路由选择协议,底层是迪杰斯特拉(Dijkstra)算法
- 路由的度量标准是带宽,延迟
- 向本自治系统中所有路由器发送与本路由器相邻的所有路由器的链路状态,但这只是路由器知道的部分信息。
- 网络层协议,直接IP数据报传输. 端口89。
12 csma/cd ppp
ppp协议是点到点的数据链路层协议,采用点对点传输网络拓扑构型主要有4种:星形、树型、环型和网状型。ppp协议主要是在广域网链路层中使用。
csma/cd是广播信道的数据链路层协议,它是早期共享型以太网所使用的一种传输协议,而以太网是局域网组网的一种协议,所以csma/cd只是用于局域网范围内,并且一般采用的传输网络拓扑构型是总线型。