如果目的IP地址和自己的IP(自己定)地址不在一个网段,Linux会直接把包发到网关。即使目的IP实际上和自己是在同一个局域网,也看不到的。
HUB工作在物理层,单纯的广播信号。
集线器工作在2层,可以学习记录,没个MAC地址发哪个口。记录表定期刷新。
ARP协议:当源机器知道目标机器的时候,可以将目标地址放入包里面,如果不知道呢?一个广播的网络里面接入了 N 台机器,我怎么知道每个 MAC 地址是谁呢?这就是ARP 协议,也就是已知 IP 地址,求 MAC 地址的协议。
ARP协议,知道IP查MAC,就是直接靠吼。例如新机器入网时。
内核里面就有ARP逻辑。两台电脑直连,直接就用ARP协议就行了。
第10讲 UDP协议
TCP 和 UDP 的区别
建立连接:是为了在客户端和服务端维护连接,而建立一定的数据结构来维护双方交互的状态,用这样的数据结构来保证所谓的面向连接的特性。
区别如下:
- 面向连接:
- TCP 面向连接,保证数据无差错、不丢失、不重复、且按序到达。
- UDP 面向无连接。和IP包一样,不保证不丢失,不保证按顺序到达。
- 面向字节流:
- TCP 面向字节流。发送的时候发的是一个流,将IP包整合成了流。
- UDP 基于数据报。继承了 IP 的特性,一个一个地发,一个一个地收。
- 拥塞控制
- TCP 有拥塞控,可根据环境调节速度。
- UDP 没有,全部看应用安排。
- 有无状态:
- TCP 是一个有状态服务。精确地记着发送了没有,接收到没有,发送到哪个了,应该接收哪个了。
- UDP 是无状态服务。只管发,发出去后就不管。
UDP 包头
UDP 除了端口号,再没有其他的了。
UDP特点
三个特点:
- 报文结构简单:相信网络通路默认就是很容易送达的,不用那么多控制的东西。
- 端口不做限制:监听的端口号,谁都可以传给他数据,他也可以传给任何人数据,甚至可以同时传给多个人数据。
- 没有拥塞控制:不考虑网络丢包,该怎么发就怎么发。
UDP 的三大使用场景
场景如下:
- 内网、网络状况好、或对丢包不敏感:比如内网在启动的时候自动安装操作系统。
- 可以广播,无需一对一沟通、建立连接的应用:如用到组播地址。
- 处理速度快,时延低,可以容忍少数丢包,即便网络拥塞,也要尽力尝试:如游戏、直播。DHCP、VXLAN、QUIC 等
提问
TCP和UDP有什么区别?
UDP使用场景?
第11讲 TCP协议(上)
TCP包头格式
解释:
- 源端口、目的端口:确定发给哪个运用。
- 序号:包的序号。解决乱序问题。
- 确认序号:解决不丢包问题。没有确认就要一直重发。
- 各状态位:维护双方连接状态。如 SYN 是发起一个连接,ACK 是回复,RST 是重新连接,FIN 是结束连接等。
- 窗口大小:流量控制。
TCP三次握手
为什么不是两次握手?根源是网络是不可靠的。
- 都要拿到答复才建立连接:即消息要有去有回。简单的说,就是网络通路是不可靠的,在收到回复之前会一直重发。都要拿到对方的答复,才建立连接。A申请B,第2次握手A得到了B的答复,第3次握手B得到了A的答复。
- 两次握手会有误连接:场景:A重复发送多个请求给B,B收到请求建立连接,简短的连接后双方断开,此时A之前发的其他请求包绕了一大圈回来了,B再次误打开连接。
- 三次握手还有沟通包序号的作用:A 要告诉 B自己包开始的序号,B也要告诉A自己包开始的序号。否则都默认从1开始会冲突。
- 场景:A连上B,发了1、2、3包,3丢了A重新发了一个,此时A掉线,A重新上线,发了一、二包,此时绕路的3到了,因为序号相同被误认为是三号包,即下一个包。
- 包序号:起始序号是随着时间变化的,可以看成一个 32 位的计数器,每 4微秒 加1。因为IP 包头里面有个 TTL生存时间,此时绕路的包早挂掉了。
为什么不是四次握手?
- 其实是可以的,但没必要。这样下去就套娃了。
三次握手时序图:
流程:
- 服务端主动监听某个端口,处于 LISTEN 状态。
- A请求B:客户端主动发起连接 SYN,之后处于 SYN-SENT 状态。注意到带了包序号。
- B响应A,B请求A:服务端收到发起的连接,返回 SYN,且 ACK 客户端的 SYN,之后处于 SYN-RCVD 状态。
- A响应B:客户端收到服务端发送的 SYN 和 ACK 之后,发送 ACK 的 ACK。之后处于 ESTABLISHED 状态,因为已完成一发一收。
- 服务端收到 ACK 的 ACK 之后,处于 ESTABLISHED 状态,因为它也一发一收了。
TCP 四次挥手
正常情况:
- A:B 啊,我不想玩了。
- B:哦,你不想玩了啊,我知道了。稍等下,我处理后事。
- B:A 啊,好吧,我也不玩了,拜拜。
- A:好的,拜拜。
异常情况一:
- A:B 啊,我不想玩了。A直接跑路。此时B 就算发起结束,也得不到回答,不知道怎么办了。
- B:哦,你不想玩了啊,我知道了。稍等下,我处理后事。
- B:A 啊,好吧,我也不玩了,拜拜。
- A:好的,拜拜。
异常情况一:
- A:B 啊,我不想玩了。
- B:B直接跑路。此时A得不到回答,A不知道B后面是可以结束还是要等一会儿。
四次挥手时序图:
流程:
A 告诉 B:终止。A进入 FIN_WAIT_1 状态。
B 响应 A:收到了。B进入CLOSE_WAIT 状态。
A收到消息后进入FIN_WAIT_2状态。此时B直接跑路,A会一直卡在这。TCP 没处理, Linux 可以调整 tcp_fin_timeout 这个参数,设置一个超时时间。
B 告诉 A:可以结束。B进入 LAST-ACK 状态。
A响应B: A收到消息进入TIME_WAIT状态,最后等待一段时间 (2MSL最大报文生存时间,)。保证B重发一次也能收到。
协议规定 MSL 为 2 分钟,实际应用中常用的是 30 秒,1 分钟和 2 分钟等。
B 超过了 2MSL 的时间,依然没有收到它发的 FIN 的 ACK,怎么办?
- B 继续重发 FIN,这个时候 A 再收到这个包之后直接回复RST,表示此次连接已经结束,之后的包A都不认了。A如果直接跑路呢?A端以前的端口关闭了、序号不匹配,都会直接发送RST给B,来关闭连接。
可以3次挥手吗??
- A最后直接跑路,不行:B 原来发过的很多包很可能还在路上,如果 A 的端口被一个新的应用占用了,这个新的应用会收到上个连接中 B 发过来的包,虽然序列号是重新生成的,但是这里要上一个双保险,防止产生混乱。等到原来 B 发送的所有的包都死翘翘,再空出端口来。
- 丢弃A端ACK的情况,可以:这种情况本质上还是4次挥手。A端没有发送ACK直接跑掉,B端会一直发送FIN。A端以前的端口关闭了、序号不匹配,都会直接发送RST给B,来关闭连接。由此还能引出,RST攻击,大概的意思就是穷举猜到序号,拿到端口号,然后大量发RST。
- TPC延迟确认机制,可以:TPC延迟确认机制可能导致第2、3次挥手合并为一次。
TCP的延迟确认机制 :可参见文章。在RFC 1122文档有过对延迟确认机制的描述:TCP延迟确认机制,允许接受方在接收到数据帧时不马上回复ACK,而是可以等待一段时间再回复ACK,但这个时间不得大于0.5s。这个延迟时间需要注意,它不是在接收到数据的时候开始计时,而是内核会启动一个200ms的定时器,每隔200ms就会检查一次自己是否有ACK需要发送给对方,相当于是批量发送的思想。
TCP状态机
以上两个时序图综合起来,就是下面的状态机:
图中:
- 加黑加粗的部分,是上面说到的主要流程。
- 阿拉伯数字的序号,是连接过程中的顺序。
- 大写中文数的序号,是断开过程中的顺序。
- 加粗的实线是客户端 A 的状态变迁。
- 加粗的虚线是服务端 B 的状态变迁。
提问
为什么要三次握手?二次、四次行不行?
详细讲讲TCP的三次握手、四次挥手,以及每一步可能出现的意外?
TCP有可能三次挥手吗?
第12讲 TCP协议(下):滑动窗口、流量控制、拥塞控制
TCP设计理念:
- 保证顺序性:每个包都设置ID,起始 ID 在建立连接的时候商定。
- 保证不丢包:对于发送的包都要应答。采用累计应答(cumulative acknowledgment),即应答某个之前的 ID,表示都收到了。
- 维护连接状态:发送端和接收端缓存所有发送的包和接收的包。
发送端和接收端窗口状态
发送端维护包状态:
- 已发送且已确认:可删除。
- 已发送且未确认:等待确认后,可删除。
- 未发送且可发送:等待发送。
- 未发送且不可发送:暂时不会发送。
为什么上述3、4要拆开:
- 主要是为了控制流量。
- Advertised window:在 TCP 里,接收端会给发送端报一个窗口的大小,表示自己接收端能处理的最大窗口,叫Advertised window。大小等于上述 2 + 3。
发送端保持数据结构如下:
接收端维护包状态:
- 已接收且已确认。等待应用层读取。
- 待接收且未确认。即自己能够接受的最大工作量。
- 不可接收。即超过工作量的部分,实在做不完。
MaxRcvBuffer:本地最大缓存量。
AdvertisedWindow:其实等待未确认窗口,即 MaxRcvBuffer 减去 “接收已确认”。
“待接收且未确认”里面有部分已经到达的包,但是要和第一部分连续,才可以确认,否则需要等待。上图中,8、9是已到达的包,其他未到达。
顺序问题与丢包问题
假设 4 的确认到了,不幸的是,5 的 ACK 丢了,6、7 的数据包丢了,这该怎么办呢?
顺序问题和丢包问题都有可能发生。
确认与重发的机制:
- 超时重试:对每一个发送了,但是没有 ACK 的包,都有设一个定时器,超过了一定的时间,就重新尝试。
- 自适应重传算法动态确定重传时间:时间必须大于往返时间 RTT,否则会引起不必要的重传。TCP 通过采样 RTT 的时间,然后进行加权平均,算出一个值。除了采样 RTT,还要采样 RTT 的波动范围,计算出一个估计的超时时间。
- 超时间隔加倍:每当遇到一次超时重传的时候,都会将下一次超时时间间隔设为先前值的两倍。两次超时,就说明网络环境差,不宜频繁反复发送。可能会造成超时周期过长问题。
- 快速重传:通过反馈定制重传。当接收方收到一个序号大于下一个所期望的报文段时,就检测到了数据流中的一个间格,于是发送三个冗余的 ACK,客户端收到后,就在定时器过期之前,重传丢失的报文段。如: 6、8、9 都已经接收了,就是 7 没来,那肯定是丢了,于是发送三个 6 的 ACK,要求下一个是 7。客户端收到 3 个,就会发现 7 的确又丢了,不等超时,马上重发。
- SACK方式:Selective Acknowledgment。在 TCP 头里加一个 SACK 的东西,将缓存的地图发送给发送方。例如可以发送 ACK6、SACK8、SACK9,有了地图,发送方一下子就能看出来是 7 丢了。
滑动窗口解决流量控制问题
包确认的时候,会带上一个窗口的大小。窗口大小是可变的。
发送端
假设发送已确认、发送待确认、未发送待发送、未发送不发送未A、B、C、D区,
先假设窗口大小不变。则对于发送端AdvertisedWindow
窗口:
1.B -> A多确认一个4,则C区多加一个13,D -> C。右移左边棍子,右边棍子右移。
2.C区最快时可全部发出去,此时C区为0。右移中间棍子,可随意移动。
3.B -> A又多确认一个5,则C区右边界可继续右滑,多加一个14。D -> C。右移左边棍子,右边棍子右移。
4.接收端如果处理不了那么大的窗口,则可通过确认信息来调整AdvertisedWindow
窗口大小,甚至可以调整为0。
接收端
1.假设接收端应用一直不确认消息,则整体窗口会变小。数据包 6 确认后,窗口大小缩小一个变为 8:
接收端变化:
发送端变化,左边界右滑:
2.如果应用端一直不处理,则继续缩小至0:
接收端:
发送端:
此时停止发送。
3.发送探测包
停止发送后:
- 发送探测包:发送方会定时发送窗口探测数据包,看是否有机会调整窗口的大小。
- 防止低能窗口综合征:为防止低能窗口综合征,当窗口太小的时候,不更新窗口,直到达到一定大小,或者缓冲区一半为空,才更新窗口。并不是空出一个字节来就赶快告诉发送方。
流量控制整体总结
发送端AdvertisedWindow窗口大小 = 接收端缓存窗口大小 - 接收端接收已确认 = 接收端待确认。
主要是抓住发送方AdvertisedWindow、接受方MaxRcvBuffer、接受发待接收,左边界同时右移、右边界同时右移几个关键点,其实很好理解。
即:
- 发送AdvertisedWindow = 发送B + 发送C
- 发送AdvertisedWindow = 接收B
- 即:接收B = 发送B + 发送C。发送方要不要发,由接收方待接收窗口大小决定。而接收方待接收窗口 + 接收方已接收 = 接收方总体受缓存窗口,需要应用确认了A区即接收已确认,才能腾出空间来右移新空间给接收方待接收窗口,即接收B区。
- 接收端缓存窗口(MaxRcvBuffer) = 接收B + 接收A 。
- 应用接收后:接收A减少,接收B就能增加。此时接收B、发送
AdvertisedWindow
窗口右边界同时右滑。 - 应用不接收:接收B、发送
AdvertisedWindow
窗口同步一直减少到0。即两个的左边界同时右滑.此时接收已确认、发送已确认也同步右移增大。
重传的包不会交给应用层。
滑动窗口和拥塞窗口是一个窗口。
拥塞窗口解决拥塞控制问题
拥塞窗口
前面的滑动窗口 rwnd 是怕发送方把接收方缓存塞满,而拥塞窗口 cwnd,是怕把网络塞满。
滑动窗口和拥塞窗口是同一个窗口。拥塞窗口和滑动窗口共同控制发送的速度。遵循公式 LastByteSent - LastByteAcked <= min {cwnd, rwnd} ,即下图中发送未确认 <= 取滑动窗口和拥塞窗口的最小值。
拥塞窗口:理想情况下,发送但未确认的包数量设置为通道的容量。通道的容量 = 带宽 × 往返延迟。如下图,单边容量为4,设置为8:
这个时候再增加包,多余的包会缓存到中间设备上,此时延迟会增加。延迟继续增加,则会造成重传或包丢失(处理不了就扔掉)。这是我们不愿意看到的。
拥塞控制
TCP 的拥塞控制主要来避免两种现象,包丢失和超时重传。
拥塞控制逻辑,慢慢尝试直到打满:
- ssthresh前,指数增长:
- 连接开始,cwnd 设置为一个报文段,一次只能发送一个;
- 一个确认后,cwnd 加一,于是一次能够发送两个;
- 两个的确认后,每个确认cwnd 加一于是一次能够发送四个。
- 依次类推,指数增长,直到达到 ssthresh(slow start threshold)。
- ssthresh后,线性增长:
- 每收到一个确认后,cwnd 增加 1/cwnd。
- 即批次内所有包确认完后,再加1。
- 超时重传:直到增长到出现拥塞(一种表现是丢包),归零慢启动:
- cwnd 减为 1,重新开始慢启动。可谓是一夜回到解放前。
- sshresh 设为 cwnd/2。
- 快速重传:超时重传太激进,容易造成网络卡顿。可采用快速重传。
- 接收端发现丢了一个中间包的时候,发送三次前一个包的 ACK,发送端就会快速的重传,不必等待超时再重传。
- cwnd 减半,为 cwnd/2。当三个包返回的后,cwnd = sshthresh + 3,
- sshthresh = cwnd/2。(和上面超时重传的值一致)
BBR拥塞算法
上述的控制逻辑,在时延很重要的情况下,反而降低了时延,因为把中间缓存填满了。
而且没有考虑到两个问题:
- 丢包并不代表通道满了:也可能是管子漏水。例如公网上带宽不满也会丢包,这个时候就认为拥塞了,退缩了,其实是不对的。
- 要等到将中间设备都填充满了,才发生丢包:这时降低速度已经晚了,因为 TCP 只要填满管道就可以了,不应该接着填,直到连缓存也填满。
为了优化这两个问题,后来有了TCP BBR 拥塞算法。它企图找到一个平衡点,就是通过不断的加快发送速度,将管道填满,但是不要填满中间设备的缓存,因为这样时延会增加,在这个平衡点可以很好的达到高带宽和低时延的平衡。
粘包问题
现象
- 发送端粘包,Nagle算法:在TCP的socket编程中,发送端和接收端都有成对的socket。发送端为了将多个发往接收端的包,更加高效的的发给接收端,于是采用了优化算法(Nagle算法,默认开启),将多次间隔较小、数据量较小的数据包,Delay一下,然后合并成一个数据量大的数据块,然后进行封包后一起发出去。原本应该是多个包,此时却是共用一套包头等,数据在一个包里。
- 接收端粘包,接收过快导致包在缓存中黏到了一起:TCP将接收到的数据包保存在接收缓存里,然后应用程序主动从缓存读取收到的分组。如果TCP接收数据包到缓存的速度 > 应用程序从缓存中读取数据包的速度,多个包就会被缓存,应用程序就有可能读取到多个首尾相接粘到一起的包,即接收侧有可能一下子收到多个应用层报文。
什么时候需要处理:
- 无需处理:多组数据本来就是同一块数据的不同部分。比如说一个文件的多个部分。
- 必须处理:多个分组毫不相干,甚至是并列关系。比如多个聊天记录。
如何处理:
- 发送方:发送方造成的粘包问题,关闭Nagle算法,使用TCP_NODELAY选项关闭。
- 接收方:接收方没有办法来处理粘包现象,只能将问题交给应用层来处理。
- 应用层:循环处理,应用程序从接收缓存中循环读取分组。如何判断数据长度,两种方法:
- 格式化数据:每条数据有固定的格式(开始符,结束符)。需保证开始结束符不被占用。
- 发送长度:发送每条数据时,将数据的长度一并发送。例如http的c ontent-length
UDP会产生粘包问题吗?
- TCP有:TCP基于流来传输(保证可靠传输并减少额外的开销,无需每次发包都确认),没有保护消息边界,会粘包。
- UDO没有:UDP是面向消息传输,有保护消息边界,接收方一次只接受一条独立的信息,所以不存在粘包问题。
例如,有三个数据包,大小分别为2k、4k、6k,如果采用UDP发送的话,不管接受方的接收缓存有多大,我们必须要进行至少三次以上的发送才能把数据包发送完,但是使用TCP协议发送的话,我们只需要接受方的接收缓存有12k的大小,就可以一次把这3个数据包全部发送完毕。
其实理论上来讲,就不应该是TCP的问题:TCP本来就是基于字节流而不是消息包的协议,它自己说的清清楚楚:我会把你的数据变成字节流发到对面去,而且保证顺序不会乱,但是你要自己搞定字节流解析。所以说,不是TCP的锅。
最后,粘包是个“土话”,容易引起歧义,不建议沿用。接收端:stream2datagram就是stream2datagram,发送端:TCP_NODELAY就是TCP_NODELAY,不要说什么“粘包”。
提问
TCP如何解决顺序问题和丢包问题?发送方和接收方如何协调速度?
TCP如何解决网络缓存塞满问题?
粘包问题?
第13讲 Socket套接字:epoll等(暂略)
第14讲 HTTP协议
HTTP 请求的准备
域名发送至 DNS 服务器,解析为 IP。
建立TCP连接。HTTP 协议大部分都是 1.1。1.1 协议默认开启 Keep-Alive,这样建立的 TCP 连接,就可以在多次请求中复用。
HTTP 请求的构建
请求体:
HTTP 的报文大概分为三大部分:
- 请求行:主要是四种方法:
- GET:获取资源
- POST:创建资源。
- PUT:修改资源。或上传资源。
- DELETE:删除资源。
- 首部字段:都是key-value,通过冒号分隔。例如:
- Accept-Charset:客户端字符集。
- Content-Type:正文格式,如json。
- Cache-control:缓存控制。缓存层中,资源的缓存时间数值比指定时间的数值小,那么客户端可以接受缓存的资源。
- If-Modified-Since:如果服务器的资源在某个时间之后更新了,那么客户端就应该下载最新的资源;如果没有更新,服务端会返回“304 Not Modified”的响应,那客户端就不用下载了,也会节省带宽。
- 正文实体
HTTP 请求的发送
HTTP 协议是基于 TCP 协议的。
- TCP层:保持连接。
- IP层:查看目标地址和自己是否是在同一个局域网。
- 在同一个局域网:发送 ARP 协议请求目标地址对应的 MAC 地址,然后将源 MAC 和目标 MAC 放入 MAC 头,发送出去即可。
- 不在同一个局域网:发送到网关。还要需要发送 ARP 协议,来获取网关的 MAC 地址。然后将源 MAC 和网关 MAC 放入 MAC 头,发送出去。
- 网关收到包发现 MAC 符合,取出目标 IP 地址,根据路由协议找到下一跳的路由器,获取下一跳路由器的 MAC 地址,将包发给下一跳路由器。
数据链路层:
- 目标的机器发现 MAC 地址符合,就将包收起来;- TCP 头里面还有端口号,HTTP 的服务器正在监听这个端口号。于是,目标机器自然知道是 HTTP 服务器这个进程想要这个包,于是将包发给 HTTP 服务器。HTTP 服务器的进程看到,原来这个请求是要访问一个网页,于是就把这个网页发给客户端。
- IP层:发现 IP 地址符合,解析IP头。
- TCP层:根据 IP 头中协议项,知道自己上一层是 TCP 协议,于是解析 TCP 的头。
- 里面有序列号,需要看一看这个序列包是不是我要的,如果是就放入缓存中然后返回一个 ACK,如果不是就丢弃。
- TCP 头里面还有端口号,HTTP 的服务器正在监听这个端口号。于是,目标机器自然知道是 HTTP 服务器这个进程想要这个包,于是将包发给 HTTP 服务器。
- 应用层:HTTP 服务器的进程看到,原来这个请求是要访问一个网页,于是就把这个网页发给客户端。
ARP协议:是“Address Resolution Protocol”(地址解析协议)的缩写。其作用是在以太网环境中,数据的传输所依懒的是MAC地址而非IP地址,而将已知IP地址转换为MAC地址的工作是由ARP协议来完成的。
HTTP 返回的构建
以下基于HTTP1.1。
响应体:
主要部分:
- 状态行
- 200:一切OK。
- 404:无响应。
- 首部,key-value形式
- Retry-After:客户端应该在多长时间以后再次尝试一下。“503 错误”是说“服务暂时不再和这个值配合使用”。
- Content-Type:如HTML、JSON。
- 实体
返回过程和请求过程一样,不过时反过来的。
HTTP 2.0
HTTP 1.1的缺点:
- 以纯文本的形式进行通信。每次通信都要带完整的 HTTP 的头。
- 不考虑 pipeline 模式的话,每次的过程总是像上面描述的那样一去一回。实时性、并发性较差。
HTTP2.0改进:
- HTTP 2.0 对 HTTP 的头进行一定的压缩:原来每次都要携带的大量 key value 在两端建立一个索引表,对相同的头只发送索引表中的索引。
- TCP连接拆为多个流:将一个 TCP 的连接中,切分成多个流,每个流都有自己的 ID,有优先级,流是双向的。
- 每个流拆为多个帧:还将所有的传输信息分割为更小的消息和帧,并对它们采用二进制格式编码。常见的帧:
- Header 帧,用于传输 Header 内容,并且会开启一个新的流。
- Data 帧,用来传输正文实体。多个 Data 帧属于同一个流。
拆分的好处:HTTP 2.0通过两个拆分:
- 客户端可以将多个请求分到不同的流中,然后将请求内容拆成帧,进行二进制传输。
- 帧可以打散乱序发送, 然后根据每个帧首部的流标识符重新组装。并且可以根据优先级,决定优先处理哪个流的数据。
举例,如下图,css、js、jpg三个请求,HTTP1.1是串行申请,而HTTP 2.0,就可以在一个连接里,客户端和服务端都可以同时发送多个请求或回应,而且不用按照顺序一对一对应。
HTTP 2.0 其实是将三个请求变成三个流,将数据分成帧,乱序发送到一个 TCP 连接中。
HTTP 2.0好处:
- 压缩了请求头:在客户端、服务端建立索引表,相同的头直接发送索引号。
- 解决队首阻塞问题:HTTP 2.0 成功解决了 HTTP 1.1 的队首阻塞问题。
- 单一连接实现并行请求与响应:无需要通过 HTTP 1.x 的 pipeline 机制用多条 TCP 连接来实现并行请求与响应;减少了 TCP 连接数对服务器性能的影响。
HTTP 2.0 通过头压缩、分帧、二进制编码、多路复用等技术提升性能;
QUIC协议(所谓的HTTP3.0)
HTTP 2.0 虽然大大增加了并发性,但也是基于 TCP 协议的,TCP 协议在处理包时是有严格顺序的。
两个不相关的流,前面 stream 2 的帧没有收到,后面 stream 1 的帧也会因此阻塞。
因此又从TCP切换为UDP。谷歌提出了QUIC协议。
详细略。
补充:
HTTP 1.0、1.1、2.0区别
HTTP 1.0
- 无状态,无连接
- 短连接:每次发送请求都要重新建立tcp请求,即三次握手,非常浪费性能
- 无host头域,也就是http请求头里的host,
- 不允许断点续传,而且不能只传输对象的一部分,要求传输整个对象
HTTP 1.1
- 长连接,流水线,使用connection:keep-alive使用长连接。默认keep-alive开启。
- 请求管道化。没有真正解决队首阻塞问题。
- 增加缓存处理(新的字段如cache-control)
- 增加Host字段,支持断点传输等
- 由于长连接会给服务器造成压力
HTTP 2.0
- 二进制分帧
- 头部压缩,双方各自维护一个header的索引表,使得不需要直接发送值,通过发送key缩减头部大小
- 多路复用(或连接共享),使用多个stream,每个stream又分帧传输,使得一个tcp连接能够处理多个http请求
- 服务器推送(Sever push)
HTTP 3.0
- 基于google的QUIC协议,而quic协议是使用udp实现的
- 减少了tcp三次握手时间,以及tls握手时间
- 解决了http 2.0中前一个stream丢包导致后一个stream被阻塞的问题
- 优化了重传策略,重传包和原包的编号不同,降低后续重传计算的消耗
- 连接迁移,不再用tcp四元组确定一个连接,而是用一个64位随机数来确定这个连接
- 更合适的流量控制
基于UDP实现
0RTT建连
基于UDP的多路复用
加密认证的报文
向前纠错机制
结论:
- 结论1:从HTTP/1.0到HTTP/2,都是利用TCP作为底层协议进行通信的。
- 结论2:HTTP/1.1,引进了长连接(keep-alive),减少了建立和关闭连接的消耗和延迟。
- 结论3:HTTP/2,引入了多路复用:连接共享,提高了连接的利用率,降低延迟。
现在Http1.1用的比较多,百度基本都是。Http2.0用的也多,如知乎下载各种图片资源。
补充:
HTTP 常见状态码
响应分为五类:
- 信息响应(
100–199
) - 成功响应(
200–299
) - 重定向(
300–399
) - 客户端错误(
400–499
) - 服务器错误 (
500–599
)。
常见响应:
101:Switch Protocol。升级协议,如从 http 到 websockt,此时需要反向代理支持,如 Nginx,,在 Nginx 配置 websockt 如下:
location / { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; }
200:Ok。资源请求成功,也是最常见到的状态码。
201:Created。资源创建成功,多用于 POST 请求。
204:No Content。响应不会返回 Body,一般由以下两种情况
- 与 Options/Delete 请求搭配
- 打点类。如知乎为 Delete 请求的状态码设置为 204,以下请求为取消关注。
206:Partial Content:当请求多媒体数据数据较大时,会进行分片传输。当你在B站观看视频,打开开发者工具,会发现许多 206 状态码以及响应头 Content-Range。
301:Moved Permanently。永久重定向。http 转向 https时,有时会使用 301,此时浏览器会自动缓存,下次直接自动跳转,不再请求服务器。当更新域名需要 SEO 优化时,同样使用 301 或者 308。如 B 站。
302:Found。暂时重定向。http 转向 https时,有时也会使用 302,如知乎的跳转
304:资源已被缓存。缓存没有改变,无需访问最新数据,可继续用本地的缓存。一般用作
index.html
等不带 hash 的资源,与之相关的响应头部有:ETag
last-modified
/if-modified-since
307:Temporary Redirect。暂时重定向。也可作为 http 到 https 的重定向。还有一种用途用作 HSTS,当谷歌浏览器发现某 http 资源已被加入到 HSTS 列表,浏览器内部会通过 307 作重定向。
308:Permanent Redirect。永久重定向。与 301 不同的是,当它重定向到新的地址时,并不会改变 method。
400:Bad Request。对于服务器无法理解的参数,将用 400 作为返回码。例如当 Content-Type: JSON 时,服务器解析 JSON 失败
401:Unauthorized 。当没有权限的用户请求需要带有权限的资源时,会返回 401。此时携带正确的权限凭证再试一次可以解决问题。有时认证失败也会返回 401。如密码不对。
403:Forbidden。我就是不想让你访问,不管你的权限凭证是否正确!
404:Not Found 未找到资源请求的资源,比如客户端输入的URL在服务器上不存在。
405:Method Not Allowed。我需要 POST 这条资源,你去 GET 个锤子。
413:Payload Too Large。不要给我扔这么大的 Body,我处理不过来。
418:I’m A Teapot。我是一个茶壶,我要泡咖啡,你却扔给我一个茶壶?也可以用来处理不合法的参数校验,我想要个字符串,你给了我一个整数?
422:Unprocessable Entity。常用来处理不合法的参数校验。Github 上给某个项目点赞时,故意设一个不对的参数命名,会返回422
429:Too Many Request。请求过多被限流。超过某一个 API 的 Rate Limit 规则,会被限流,返回 429 状态码。
500:Internal Server Error。服务器内部错误。很有可能是应用层未捕获错误而导致整个服务挂掉,这种情况一般不允许出现。
502:Bad Gateway。Nginx 上常见,从上游应用层未返回响应,上游应用层挂了。
503:Servlet Unavailable。由于大量流量造成服务忙,稍等一下说不定就能用了。
504:Gateway Timeout。网关超时,上游应用层迟迟未响应。
提问
HTTP1.0、1.1、2.0区别?
HTTP常见状态码?304、401、403、405?
第15讲 HTTPS协议
加密方式:
- 对称加密:加密解密用同一个密钥。
- 非对称加密:公钥加密,私钥解密。私钥加密,公钥解密。
对称加密
问题:
- 密钥不能通过网上传播,否则被截获就GG。
- 而且网上客户太多,一个用户一个密钥,会疯掉的。
非对称加密
问题:
- 黑客可以用公钥模拟信息。
- 私钥加密的信息,发到网上,谁都可以打开。所以客户端和服务端要各有一套公钥私钥。
数字证书
用途:
- 证明 A 给你的这个公钥,确实就是A的公钥。中途没有被篡改。
证书内容
- 公钥
- 证书所有者,也是公钥所有者。
- 证书的发布机构和证书的有效期
证书生成
发起请求:生成证书需要发起一个证书请求,然后将这个请求发给一个权威机构去认证,这个权威机构我们称为CA( Certificate Authority)。
openssl req -key cliu8siteprivate.key -new -out cliu8sitecertificate.req
机构盖章:将这个请求发给权威机构,权威机构会给这个证书卡一个章,我们称为 签名算法 。
如何保证签名的确实是权威机构:权威机构用 CA 的私钥进行签名。
签名过程
对信息摘要:对信息做一个 Hash 计算,得到一个 Hash 值
Hash 值加密后,作为一个签名和信息一起发出去。权威机构的签名命令:
openssl x509 -req -in cliu8sitecertificate.req -CA cacertificate.pem -CAkey caprivate.key -out cliu8sitecertificate.pem
通过证书获取网站公钥
此时获取公钥的方式变了。是从网站得到证书,验证证书,从证书中获得网站的公钥:
- 获取证书:从网站上获得一个证书。
- 查看发布机构:查看证书的发布机构CA。
- 验证签名真实:用该发布机构的CA 公钥,解密证书签名。解密成功,说明签名正确,证书真实。
- 从证书获取公钥:证书真实,此时就可以使用证书中提供的公钥,即提供商的公钥。
获取CA公钥验证证书
验证证书,需要 CA 的公钥,问题是,你怎么确定 CA 的公钥就是对的呢:
- 层层授信:CA 的公钥也需要更牛的 CA 给它签名,然后形成 CA 的证书。这样层层上去,直到全球皆知的几个著名大 CA,称为root CA。
- 如何信任大机构:各大CA机构的公钥是默认安装在操作系统里的。所以不要安装来路不明的操作系统,否则相当于裸奔。
自己给自己签名:除此之外,还有一种证书,称为Self-Signed Certificate,就是自己给自己签名。这个给人一种“我就是我,你爱信不信”的感觉。这里我就不多说了。
HTTPS 的工作模式
整体工作模式:
- 用公私钥传输对称加密的秘钥,而真正的双方大数据量的通信通过对称加密进行。
总体思路如下图:
具体过程:
- 客户端发送 Client Hello 消息:明文传输 TLS 版本信息、加密套件候选列表、压缩算法候选列表等信息。再加一个随机数,用于协商对称密钥。
- 服务端返回 Server Hello 消息:明文传输服务器选择使用的协议版本、加密套件、压缩算法等。再加一个随机数,用于协商对称密钥。
- 服务端返回一个证书:Server Certificate。
- 服务端结束信息:“Server Hello Done,我这里就这些信息了”。证书中途会不会被掉包?其实这并不会发生,因为证书里包含了网站A的信息,包括域名,浏览器把证书里的域名与自己请求的域名比对一下就知道有没有被掉包了。
- 客户端验证证书可信:从自己信任的 CA 仓库中,拿 CA 的证书里面的公钥去解密外卖网站的证书。如果能够成功,则说明外卖网站是可信的。
- 客户端通过公钥发送加密随机数:客户端计算产生随机数字 Pre-master,发送 Client Key Exchange,用证书中的公钥加密,再发送给服务器,服务器可以通过私钥解密出来。
- 客户端、服务端同时用三个随机数生成对称密钥:三个随机数,分别是:自己的、对端的,以及刚生成的 Pre-Master 随机数。通过这三个随机数,可以在客户端和服务器产生相同的对称密钥。
- 客户端提议用对称加密通信:有了对称密钥,客户端可以发送:“Change Cipher Spec,咱们以后都采用协商的通信密钥和加密算法进行加密通信了。”
- 客户端发送握手验证:发送一个 Encrypted Handshake Message,将之前已经商定好的参数等,采用协商密钥进行加密,发送给服务器用于数据与握手验证。
- 服务端提议同对称加密通信:服务器也可以发送 Change Cipher Spec,以后都采用协商的通信密钥和加密算法进行加密通信了。
- 服务端发送握手验证:也发送 Encrypted Handshake Message 的消息试试。双方握手结束后,就可通过对称密钥进行加密传输了。
这个过程除了加密解密之外,其他的过程和 HTTP 是一样的,过程也非常复杂。
上面的过程只包含了 HTTPS 的单向认证,也即客户端验证服务端的证书,是大部分的场景,也可以在更加严格安全要求的情况下,启用双向认证,双方互相验证证书。
单向认证中的三个随机数是为了保证完全随机吧,毕竟所有的机器都是伪随机的。
伪随机:利用数学递推公式产生。只要算法选择适当,可以得到相互独立和满足某种分布的伪随机数。优点是速度快,占用存储小,易于计算机实现。缺点是当实验数据较大时,所得的数会出现重复现象。
常见加密算法
- 非对称加密:
- RSA(更快速)。原理:将两个大 素数 相乘十分容易,但想要对其乘积进行 因式分解 却极其困难,因此可以将 乘积 公开作为 加密密钥。
- ECC:使用 更小的密钥。加密解密比较慢,耗费CPU资源。
- 对称加密:
- DES(安全性一般,速度慢)。
- AES(更快速,更安全。AES128, 256等,为了取代DES)。
- Hash摘要:
- MD5(安全性中等,速度更快)
- SH1(安全性更高,速度更慢)
重放与篡改
重放
- 现象:黑客截获了包也打不开了,但是它可以发送 N 次。例如发送多次扣款?
- 解决方式:通过 Timestamp 和 Nonce 随机数联合起来,然后做一个不可逆的签名来保证。Nonce 随机数保证唯一,或者 Timestamp 和 Nonce 合起来保证唯一,请求只接受一次,服务器多次收到相同的 Timestamp 和 Nonce,则视为无效即可。
篡改
签名保证不可篡改性:如果有人想篡改 Timestamp 和 Nonce,改了用签名算法解出来,就对不上了,可以丢弃了。
提问
Https原理?
什么是数组证书?如何保证真实?
如何签名?
签名方式参考:https://zhuanlan.zhihu.com/p/43789231
JSONP原理
本文参考:
- https://segmentfault.com/a/1190000011145364
- https://www.cnblogs.com/dowinning/archive/2012/04/19/json-jsonp-jquery.html
同源策略
浏览器的同源策略(SOP,Same origin policy):浏览器不能执行其他网站的脚本。用于防止XSS,CSRF攻击。网景公司提出的安全约定,一般浏览器都会实现。同源的意思是指”协议+域名+端口”三者相同。
同源策略限制的交互包括以下几种行为:
- 存储内容:Cookie、LocalStorage、IndexdDB 等存储内容。
- DOM 节点:DOM 和 Js 对象无法获得。
- AJAX 请求:不能发送。
跨域的解决方案:
- jsonp:只支持 GET,不支持 POST 请求,不安全 XSS。
- cors:需要后台配合进行相关的设置。前后端都支持跨域,origin-access….。先发一个预判断请求,同意跨域后再发其他。
- nginx代理:使用代理去避开跨域请求,需要修改 nginx、apache 等的配置
业务场景:Ajax直接请求普通文件,存在跨域无权限访问的问题。
有一个例外:可以加载其他网站的<image>
等资源。
网络安全:XSS、CSRF、SQL注入
https://segmentfault.com/a/1190000007059639
- XSS:跨站脚本攻击。(Cross-site scripting,通常简称为XSS)。XSS本质是Html注入,是SQL注入差不多。
- 预防方法:
- 请求过滤,例如过滤掉code字段后的内容。
- 字符转义,例如把
<
和>
都转义为&xx。把所有用户输入进行 HTML 转义输出即可。如果确实有需要用户输入html的要求,那么使用白名单,即收集html节点,重新构造即可。这样未能识别的就被删掉了。现在亦有不少开源的 XSS 漏洞测试软件包。
- CSRF:跨站请求伪造。(Cross-site request forgery,又称XSRF)。防御措施:
- referer验证请求来源
- token
- http请求头修改
- 图片验证码。
- SQL注入:预防方法:使用预定义语句,即preparedStatement,
#{}
。
token 如果是放在 localStorage,跨站是拿不到的,如果 cookie 设置 http-only,js 也是拿不到的。csrf 为什么有效呢?是因为 cookie 是浏览器自动携带的,这很关键。
区别:
- xss:用户过分信任网站,放任来自浏览器地址栏代表的那个网站代码在自己本地任意执行。如果没有浏览器的安全机制限制,xss代码可以在用户浏览器为所欲为;
- csrf:网站过分信任用户,放任来自所谓通过访问控制机制的代表合法用户的请求执行网站的某个特定功能。即使csrf有token保护,也可以因页面存在xss而被破解(攻击者拿javascript直接获取token就好了)。
- 一般可以先有XSS攻击,拿到用户数据,然后发起CSRF攻击
面试题:
CSRF 和 XSS 和 XXE 有什么区别,以及修复方式?
XSS是跨站脚本攻击,用户提交的数据中可以构造代码来执行,从而实现窃取用户信息等攻击。修复方式:
- 对字符实体进行转义、过滤
- 使用HTTP Only来禁止JavaScript读取Cookie值
- 输入时校验
- 浏览器与Web应用端采用相同的字符编码。
CSRF是跨站请求伪造攻击,是由于没有在关键操作执行时进行是否由用户自愿发起的确认,模仿合法用户对服务器发起请求 。修复方式:
- 筛选出需要防范CSRF的页面,然后嵌入Token、再次输入密码。
- 检验Referer来源网页
XXE是XML外部实体注入攻击,XML中可以通过调用实体来请求本地或者远程内容,和远程文件保护类似,会引发相关安全问题,例如敏感文件读取。修复方式:
- XML解析库在调用时严格禁止对外部实体的解析。
CSRF是服务器端没有对用户提交的数据进行严格的把控,导致攻击者可以利用用户的Cookie信息伪造用户请求发送至服务器。而SSRF是服务器对用户提供的可控URL地址过于信任,没有经过严格检测,导致攻击者可以以此为跳板攻击内网或其他服务器。
CSRF是跨请求伪造攻击,由客户端发起 SSRF是服务器端请求伪造,由服务器发起 重放攻击是将截获的数据包进行重放,达到身份认证等目的
5个Linux网络模型
待补充。
转载请注明来源