网络协议原理解析
Posted fuguy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络协议原理解析相关的知识,希望对你有一定的参考价值。
TCP 和 UDP 的区别
概括:TCP是一个面向连接的、可靠的、基于字节流的传输层协议。
- TCP是安全可靠的传输协议,提现出来一个是有状态,另一个是可控制。TCP 会精准记录哪些数据发送了,哪些数据被对方接收了,哪些没有被接收到,而且保证数据包按序到达,不允许半点差错。这是有状态。当意识到丢包了或者网络环境不佳,TCP 会根据具体情况调整自己的行为,控制自己的发送速度或者重发。这是可控制
- 面向连接。所谓的连接,指的是客户端和服务器的连接,在双方互相通信之前,TCP 需要三次握手建立连接,而 UDP 没有相应建立连接的过程
- 面向字节流。UDP 的数据传输是基于数据报的,这是因为仅仅只是继承了 IP 层的特性,而 TCP 为了维护状态,将一个个 IP 包变成了字节流
- UDP 不止支持一对一的传输方式,同样支持一对多,多对多,多对一的方式,也就是说 UDP 提供了单播,多播,广播的功能。
TCP
什么是三次握手?
三次握手其实指的是三次信息交换过程,确认通信是否具备通信的能力(发送消息和接收消息),而且三次信息交换都需要加上编号
从最开始双方都处于++CLOSED++状态。然后服务端开始监听某个端口,进入了++LISTEN++状态。
然后客户端主动发起连接,发送 ++SYN++ , 自己变成了++SYN-SENT++状态。服务端接收到,返回++SYN++和++ACK++(对应客户端发来的SYN),自己变成了++SYN-REVD++。
之后客户端再发送++ACK++给服务端,自己变成了++ESTABLISHED++状态;服务端收到++ACK++之后,也变成了++ESTABLISHED++状态。
从图中可以看出: ++SYN++ 是需要消耗一个序列号的,下次发送对应的 ++ACK++ 序列号要加1
为什么需要建立三次握手?
如果我们假设一种场景,如果是建立二次握手,客户端在接受了 ++ACK+SYN++,建立好了连接。然后客户端接着断开了,这就带来了连接资源的浪费
三次握手过程中可以携带数据么?
第三次握手的时候,可以携带。前两次握手不能携带数据。
如果前两次握手能够携带数据,那么一旦有人想攻击服务器,那么他只需要在第一次握手中的 SYN 报文中放大量数据,那么服务器势必会消耗更多的时间和内存空间去处理这些数据,增大了服务器被攻击的风险。
第三次握手的时候,客户端已经处于++ESTABLISHED++状态,并且已经能够确认服务器的接收、发送能力正常,这个时候相对安全了,可以携带数据。
如果服务端和客户端同时打开会怎样?
- 客户端发送 ++SYN++,服务端也发送 ++SYN++
- 客户端和服务端同时发送 ++SYN+ACK++
- 最后双方同时进入 ++ESTABLISHED++
半连接队列和 SYN Flood 洪水攻击的关系
三次握手前,服务端的状态从CLOSED变为LISTEN, 同时在内部创建了两个队列:半连接队列和全连接队列,即SYN队列和ACCEPT队列
半连接队列
当客户端发送++SYN++到 服务端端,服务器端发送 ++ACK+SYN++,状态由++LISTEN++进入到++SYN-REVD++,此时这个连接就被推入了SYN队列,也就是半连接队列
全连接队列
当客户端返回ACK, 服务端接收后,三次握手完成。这个时候连接等待被具体的应用取走,在被取走之前,它会被推入另外一个 TCP 维护的队列,也就是全连接队列(Accept Queue)
SYN Flood 攻击原理
SYN Flood 属于典型的 DoS/DDoS 攻击。其攻击的原理很简单,就是用客户端在短时间内伪造大量不存在的 IP 地址,并向服务端疯狂发送SYN。对于服务端而言,会产生两个危险的后果:
- 处理大量的SYN包并返回对应ACK, 势必有大量连接处于SYN_RCVD状态,从而占满整个半连接队列,无法处理正常的请求。
- 由于是不存在的 IP,服务端长时间收不到客户端的ACK,会导致服务端不断重发数据,直到耗尽服务端的资源。
如何应对 SYN Flood 攻击?
- 增加半连接队列容量
- 减少SYN+ACK重复次数
- 利用 SYN Cookie 技术,在服务端接收到SYN后不立即分配连接资源,而是根据这个SYN计算出一个Cookie,连同第二次握手回复给客户端,在客户端回复ACK的时候带上这个Cookie值,服务端验证 Cookie 合法之后才分配连接资源
- Syn Cache技术,这种技术是在收到SYN数据报文时不急于去分配TCB,而是先回应一个SYN ACK报文,并在一个专用HASH表(Cache)中保存这种半开连接信息,直到收到正确的回应ACK报文再分配TCB。在FreeBSD系统中这种Cache每个半开连接只需使用160字节,远小于TCB所需的736个字节
- 使用SYN Proxy防火墙,防火墙中都提供一种SYN代理的功能,其主要原理是对试图穿越的SYN请求进行验证后才放行
TCP 的 keep-alive?
TCP 的 keep-alive 用来探测对端的连接有没有失效
TCP 报文头部的字段
一个TCP连接都是四元组,由源IP、源端口、目标IP、目标端口唯一确定一个连接。其中源IP和目标IP在IP协议中处理
序列号
即Sequence number, 指的是本报文段第一个字节的序列号。
从图中可以看出,序列号是一个长为 4 个字节,也就是 32 位的无符号整数,表示范围为 0 ~ 2^32 - 1。如果到达最大值了后就循环到0
ISN(初始序列号)
每 4s 加 1,溢出则回到 0,如果 ISN 被攻击者预测到,要知道源 IP 和源端口号都是很容易伪造的,当攻击者猜测 ISN 之后,直接伪造一个 RST 后,就可以强制连接关闭的,这是非常危险的
RST 攻击
A和服务器B之间建立了TCP连接,此时C伪造了一个TCP包发给B,使B异常的断开了与A之间的TCP连接,就是RST攻击了
窗口大小
占用两个字节,也就是 16 位,但实际上是不够用的。因此 TCP 引入了窗口缩放的选项,作为窗口缩放的比例因子,这个比例因子的范围在 0 ~ 14,比例因子可以将窗口的值扩大为原来的 2 ^ n 次方。
校验和
占用两个字节,防止传输过程中数据包有损坏,如果遇到校验和有差错的报文,TCP 直接丢弃之,等待重传。
TCP流量控制和拥塞控制
TCP 采用大小可控制的流量窗口来控制,发送窗口在连接建立时由双方商定。但在通信的过程中,接收方可根据自己的资源情况,随时动态地调整对方的发送窗口上限值
- 接收端窗口 rwnd(也称为通知窗口):接收端缓冲区大小,表示接收方的接收能力。接收端将此窗口值放在 TCP 报文的首部中的窗口字段,传送给发送端。
- 拥塞窗口cwnd (congestion window):发送端缓冲区大小
- 发送窗口swnd:发送窗口的上限值 = Min [rwnd, cwnd]
- 当 rwnd < cwnd 时,是接收端的接收能力限制发送窗口的最大值
流量控制
流量控制:就是让发送方的发送速率不要太快,让接收方来得及接收。
拥塞
拥塞是指:数据发送速度超出网络所能承受的极限,经常造成路由器丢包的现象
慢启动(慢开始)
刚开始的时候,不知道网络的情况,如果做的太激进,发包太急,那么疯狂丢包,造成雪崩式的网络灾难
在每收到一个对新的报文段的确认后,将拥塞窗口增加至多一个 MSS(最大报文长度的值) 的数值(一开始窗口大小为1,发送一个,接收一个,增长为2;发送两个,得到两个确认,增长为4......因此,窗口是指数增长的)
慢启动阈值
因为拥塞窗口是指数增长的,为防止后期增长过快,需要另外一个变量---慢开始门限(阈值),它的阈值叫做慢启动阈值,到底阀值后,以前一个 RTT 下来,cwnd翻倍,现在cwnd只是增加 1 而已
快速重传
在 TCP 传输的过程中,如果发生了丢包,即接收端发现数据段不是按序到达的时候,接收端的处理是重复发送之前的 ACK,在收到发送端的报文后,接收端回复一个 ACK 报文,那么在这个报文首部的可选项中,就可以加上SACK这个属性,通过left edge和right edge告知发送端已经收到了哪些区间的数据报。因此,即使第 5 个包丢包了,当收到第 6、7 个包之后,接收端依然会告诉发送端,这两个包到了。剩下第 5 个包没到,就重传这个包。这个过程也叫做选择性重传(SACK,Selective Acknowledgment)
快速恢复
发送端收到三次重复 ACK 之后,发现丢包,觉得现在的网络已经有些拥塞了,自己会进入快速恢复阶段
- 拥塞阈值降低为 cwnd 的一半
- cwnd 的大小变为拥塞阈值
- cwnd 线性增加
TCP之Nagle算法&&延迟ACK
Nagle算法
为了解决小分组的数量,从而减小网络拥塞的出现,其中小分组的定义是小于MSS(最大报文长度)的任何分组
算法规则
- 当第一次发送数据时不用等待,就算是 1byte 的小包也立即发送
- 后面发送满足下面条件之一就可以发了:
- 数据包大小达到最大段大小(Max Segment Size, 即 MSS)
- 之前所有包的 ACK 都已接收到
延迟ACK
如果tcp对每个数据包都发送一个ack确认,那么只是一个单独的数据包为了发送一个ack代价比较高,所以tcp会延迟一段时间,如果这段时间内有数据发送到对端,则捎带发送ack,总结来说就是延迟合并发送ack。
以下场景不适合延迟
- 需要调整窗口大小
- 乱序需要重发的时候
如果同时使用Nagle和延迟ACK
使用Nagle,两次写数据长度小于MSS,当第一次写数据到达对端后,对端延迟ack,不发送ack,而本端因为要发送的数据长度小于MSS,所以nagle算法起作用,数据并不会立即发送,而是等待对端发送的第一次数据确认ack;这样的情况下,需要等待对端超时发送ack,然后本段才能发送第二次写的数据,从而造成延迟,产生性能问题
HTTP(超文本传输协议)
HTTP 报文
GET 和 POST 有什么区别?
首先最直观的是语义上的区别。
而后又有这样一些具体的差别:
- 从缓存的角度,GET 请求会被浏览器主动缓存下来,留下历史记录,而 POST 默认不会。
- 从编码的角度,GET 只能进行 URL 编码,只能接收 ASCII 字符,而 POST 没有限制。
- 从参数的角度,GET 一般放在 URL 中,因此不安全,POST 放在请求体中,更适合传输敏感信息。
- 从幂等性的角度,GET是幂等的,而POST不是。(幂等表示执行相同的操作,结果也是相同的)
- 从TCP的角度,GET 请求会把请求报文一次性发出去,而 POST 会分为两个 TCP 数据包,首先发 header 部分,如果服务器响应 100(continue), 然后发 body 部分。(火狐浏览器除外,它的 POST 请求只发一个 TCP 包)
HTTPS 详解
简介
HTTPS 协议之所以是安全的是因为 HTTPS 协议会对传输的数据进行加密,而加密过程是使用了非对称加密实现。但其实,HTTPS 在内容传输的加密上使用的是对称加密,非对称加密只作用在证书验证阶段
首先需要理解对称加密和非对称加密的概念,然后讨论两者应用后的效果如何。
对称加密是最简单的方式,指的是加密和解密用的是同样的密钥。
而对于非对称加密,如果有 A、 B 两把密钥,如果用 A 加密过的数据包只能用 B 解密,反之,如果用 B 加密过的数据包只能用 A 解密.
对称加解密过程
接着我们来谈谈浏览器和服务器进行协商加解密的过程。
首先,浏览器会给服务器发送一个随机数 client_random 和一个加密的方法列表。
服务器接收后给浏览器返回另一个随机数 server_random 和加密方法。
现在,两者拥有三样相同的凭证: client_random、server_random 和加密方法。
接着用这个加密方法将两个随机数混合起来生成密钥,这个密钥就是浏览器和服务端通信的暗号
影响
如果只是采用对称加密的方式,如果遇到中间人攻击,那么第三方可以在中间获取到client_random、server_random和加密方法,由于这个加密方法同时可以解密,那么第三方就可以很成功的拿到数据,对其进行加解密
思考?
是否可以采用非对称加密的方式进行加解密呢?实际上非对称加密需要的计算量非常大,对于稍微大一点的数据即使用最快的处理器也非常耗时,这样数据传输的时间大大加长,造成网络的阻塞
是否需要证书?
如果只是采用的非对称加密算法,黑客如果采用 DNS 劫持,将目标地址替换成黑客服务器的地址,然后黑客自己造一份公钥和私钥,照样能进行数据传输.
为了获取这个证书,服务器运营者需要向第三方认证机构获取授权,这个第三方机构也叫CA(Certificate Authority), 认证通过后 CA 会给服务器颁发数字证书, 假设不存在认证机构,任何人都可以制作证书,这带来的安全风险便是经典的“中间人攻击”问题。
HTTPS 是怎么做的呢?
HTTPS 的整体过程分为证书验证和数据传输阶段
证书验证阶段
浏览器向服务器发送client_random和加密方法列表。
服务器接收到,返回server_random、加密方法以及公钥。
++浏览器接收,接着生成另一个随机数pre_random, 并且通过证书中的公钥对随机数进行加密传输到服务端,传给服务器。++
服务器用公钥解密这个被加密后的pre_random
现在浏览器和服务器有三样相同的凭证:client_random、server_random和pre_random。然后两者用相同的加密方法混合这三个随机数,生成最终的密钥。
CA证书认证过程
首先会读取证书中的明文内容。CA 进行数字证书的签名时会保存一个 Hash 函数,来这个函数来计算明文内容得到信息A,然后用公钥解密明文内容得到信息B,两份信息做比对,一致则表示认证合法
当然有时候对于浏览器而言,它不知道哪些 CA 是值得信任的,因此会继续对CA链进行查找,查找 CA 的上级 CA,以同样的信息比对方式验证上级 CA 的合法性。一般根级的 CA 会内置在操作系统当中,当然如果向上找没有找到根级的 CA,那么将被视为不合法
数据传输阶段
浏览器和服务器尽管用一样的密钥进行通信,即使用对称加密。
结论
回头比较一下和单纯的使用非对称加密, 这种方式做了什么改进呢?本质上是防止了私钥加密的数据外传。单独使用非对称加密,最大的漏洞在于服务器传数据给浏览器只能用私钥加密,这是危险产生的根源。利用对称和非对称加密结合的方式,就防止了这一点,从而保证了安全
HTTPS 它在 HTTP 和 TCP 的传输中建立了一个 SSL 安全层,利用对称加密和非对称机密结合数字证书认证的方式,让传输过程的安全性大大提高
HTTP1.O
HTTP 1.0规定浏览器与服务器只保持短暂的连接,浏览器的每次请求都需要与服务器建立一个TCP连接,服务器完成请求处理后立即断开TCP连接,服务器不跟踪每个客户也不记录过去的请求。但是,这也造成了一些性能上的缺陷
HTTP 1.1
在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟。一个包含有许多图像的网页文件的多个请求和应答可以在一个连接中传输,但每个单独的网页文件的请求和应答仍然需要使用各自的连接
HTTP1.1也扩充了请求头来改进和扩充1.1的功能.例如,HTTP 1.0不支持Host请求头字段,WEB浏览器无法使用主机头名来明确表示要访问服务器上的哪个WEB站点,这样就无法使用WEB服务器在同一个IP地址和端口号上配置多个虚拟WEB站点。可以设置Connection请求头的值为Keep-Alive时,客户端通知服务器返回本次请求结果后保持连接;Connection请求头的值为close时,客户端通知服务器返回本次请求结果后关闭连接。HTTP 1.1还提供了与身份认证、状态管理和Cache缓存等机制相关的请求头和响应头。HTTP/1.0不支持文件断点续传
HTTP1.1 如何解决 HTTP 的队头阻塞问题?
什么是 HTTP 队头阻塞?
从前面的小节可以知道,HTTP 传输是基于请求-应答的模式进行的,报文必须是一发一收,但值得注意的是,里面的任务被放在一个任务队列中串行执行,一旦队首的请求处理太慢,就会阻塞后面请求的处理。这就是著名的HTTP队头阻塞问题。
并发连接
对于一个域名允许分配多个长连接,那么相当于增加了任务队列,不至于一个队伍的任务阻塞其它所有任务。在RFC2616规定过客户端最多并发 2 个连接,不过事实上在现在的浏览器标准中,这个上限要多很多,Chrome 中是 6 个
域名分片
一个域名不是可以并发 6 个长连接吗?那我就多分几个域名。
比如 content1.sanyuan.com 、content2.sanyuan.com。
这样一个sanyuan.com域名下可以分出非常多的二级域名,而它们都指向同样的一台服务器,能够并发的长连接数更多了,事实上也更好地解决了队头阻塞的问题。
HTTP2.O
在性能提升方面,添加了新的功能:
- 头部压缩
- 多路复用
另外还有:
- 设置请求优先级
- 服务器推送
头部压缩
头部压缩主要采用了 HPACK 算法,在HTTP通信的过程中,每一次请求都会发送相同的头部,以及相同的键值信息,这个时候还是存在非常大的优化空间的
Hpack算法
HTTP1.x的header中的字段很多时候都是重复的,例如method:get、status:200等等,随着网页增长到需要数十到数百个请求,这些请求中的冗余标头字段不必要地消耗带宽,从而显著增加了延迟
思想原理
通信的双方各拥有一本字典,记录着某些字符对应的文本内容
过程
消息发送端和消息接受端共同维护一份静态表和一份动态表(这两个合起来充当字典的角色),
每次请求时,发送方根据字典的内容以及一些特定指定编码压缩消息头部,
接收方根据字典进行解码,并且根据指令来判断是否需要更新动态表
静态表
- name和value都可以完全确定,这种已知键值对的方式,直接用一个字符来表示
- 只能够确定name:比如:authority、cookie,首先将name部分先用一个字符(比如cookie)来表示,同时,根据情况判断是否告知服务端,将 cookie: xxxxxxx 添加到动态表中(我们这里默认假定是从客户端向服务端发送消息)
动态表
动态表最初是一个空表,当每次解压头部的时候,有可能会添加条目(比如前面提到的cookie,当解压过一次cookie时,cookie: xxxxxxx就有可能被添加到动态表了,至于是否添加要根据后面提到的指令判断)
动态表允许包含重复的条目,也就是可能出现完全相同的键值对
为了限制解码器的需求,动态表大小有严格限制的
索引地址空间
静态表和动态表一起组成一个索引地址空间。设静态表长度为s,动态表长度为k,那么最终的索引空间如下
- 索引1-s是静态表,s-s+k是动态表,
- 新的条目从在动态表的开头插入,从动态表末尾移除
- 字段的name使用索引值表示,字段的value直接使用原有字面的值的八位字节序列或者使用静态哈夫曼编码表示
- 字符串编码过的数据,如果标志位H为"0",则编码数据是字符串文字的原始八位字节;如果H是"1",则编码数据是字符串文字的huffman编码,剩下7位表示数据长度Length,以及8位字节序列表示的原有字面字符串
优势
可以在传输的过程,简化消息内容,从而降低消息的大小
头部压缩可以减小请求的头部大小
二次压缩的压缩率会更高(压缩体积越减越小)
多路复用
而 HTTP/2 便从 HTTP 协议本身解决了队头阻塞问题。注意,这里并不是指的TCP队头阻塞,而是HTTP队头阻塞,两者并不是一回事。TCP 的队头阻塞是在数据包层面,单位是数据包,前一个报文没有收到便不会将后面收到的报文上传给 HTTP,而HTTP 的队头阻塞是在 HTTP 请求-响应层面,前一个请求没处理完,后面的请求就要阻塞住。两者所在的层次不一样。
之前解决HTTP队头堵塞问题一般采用并发连接,或者是多域名的方式,这也会引发相互竞争资源的问题,而HTTP2从协议本身去解决这个问题。
原来Headers + Body的报文格式如今被拆分成了一个个二进制的帧,用Headers帧存放头部字段,Data帧存放请求体数据。分帧之后,服务器看到的不再是一个个完整的 HTTP 请求报文,而是一堆乱序的二进制帧。这些二进制帧不存在先后关系,因此也就不会排队等待。
服务器推送
在 HTTP/2 当中,服务器已经不再是完全被动地接收请求,响应请求,它也能新建 stream 来给客户端发送消息,当 TCP 连接建立之后,比如浏览器请求一个 html 文件,服务器就可以在返回 HTML 的基础上,将 HTML 中引用到的其他资源文件一起返回给客户端,减少客户端的等待。
以上是关于网络协议原理解析的主要内容,如果未能解决你的问题,请参考以下文章
https网络编程——HTTP协议的简介HTTP报文结构和代理HTTP解析方法(哈希加速协议状态机)