阅读 30

常见网络协议浅析

本文主要围绕TCP、UDP、HTTP以及HTTPS服务进行描述。同时,针对TCP服务中的三次握手四次挥手、HTTPS服务的加密算法及过程也会进行阐述。

TCP服务

TCP全名为传输控制协议(Transmission Control Protocol),在OSI模型(由七层组成,分别为物理层、数据链路层、网络层、传输层、会话层、表示层、应用层)中属于传输层协议,而HTTP、SMTP等协议便是基于TCP构建。

首先TCP是面向连接的协议,而在使用TCP传输数据之前需要进行三次握手。客户端只有与服务端成功建立连接后才互相发送数据。那么接下来,我们来感受一下三次握手。

三次握手
1. 我们首先要明白为什么要进行三次握手?

在没有三次握手的前提下:只要服务端发出确认报文段后,便认为TCP建立,但是由于客户端发出的第一个报文在某个节点滞留时间过长,而服务端并不知道,便客户端发送确认报文,对于客户端来说,此报文已失效,导致服务端一直等待客户端的响应,即死锁状态。

所以说,三次握手的主要作用就是客户端与服务端都要知道对方自己接收发送能力。

2.三次握手的过程

首先建立连接之前TCP客户端和服务端都处于关闭状态(CLIOSED),在客户端主动打开连接,服务端被动打开连接(LISTEN状态):

  1. 客户端给服务端发送一个SYN报文(这时,服务端知道客户端的发送能力和自己的接收能力),客户端进入同步已发送状态(SYN_SENT);
  2. 服务端收到客户端SYN报文后,应答客户端一个SYN+ACK报文(这时,客户端知道自己的发送/接收能力和服务端的接收/发送能力),服务端进入同步已接收状态(SYN_RCVD);
  3. 客户端接收到SYN+ACK报文后,应答服务端一个ACK报文,服务端接收到ACK报文(这时,服务端知道自己的发送/接收能力和客户端的接收/发送能力),客户端和服务端都进入已创建状态(ESTABLISHED)便可以建立连接,即三次握手完成。

当然,如果是HTTPS协议,握手过程中,还会进行数字证书的验证以及加密密钥的生成。

客户端发送能力 客户端接收能力 服务端发送能力 服务端接收能力
第一握手 S知道 S知道
第二握手 B知道/S知道 B知道 B知道 B知道/S知道
第三握手 B知道/S知道 B知道/S知道 B知道/S知道 B知道/S知道

注:B:客户端,S:服务端。

四次挥手

说完三次握手,我们再了解一下四次挥手。首先TCP连接是双向传输的对等模式,就是说双方都可以同时向对方发送或接收数据。

首先,客户端和服务端都处于已创建状态(ESTABLISHED),此时,客户端主动关闭TCP连接:

  1. 客户端发送FIN报文,此时客户端处于终止等待1状态(FIN_WAIT_1);
  2. 服务端收到后,向客户端返回ACK报文,此时服务端进入关闭等待状态(CLOSE_WAIT);
  3. 若服务端也要关闭连接,则向客户端发送FIN报文,此时服务端进入最后确认状态(LAST_ACK);
  4. 客户端收到服务端报文,则向服务端返回ACK报文,此时客户端进入时间等待状态(TIME_WAIT),服务端进入关闭状态(CLOSED);
  5. 经过时间等待计时器设置的时间2MSL(最长报文段寿命Maximum Segment Lifetime)后,此时客户端进入关闭状态(CLOSED)。

注:为什么客户端关闭需要等待2MSL呢?

  1. 首先要确保服务端是否已经收到客户端的报文;
  2. 1MSL后,服务端还没有收到,这时服务端会重新发送FIN报文;
  3. 2MSL后,若客户端没有收到服务端重新发送的报文,则说明客户端最后1次发送的报文,服务端已经收到,即客户端可以进入关闭状态。

UDP服务

UDP称用户数据包协议(User Datagram Protocol),与TCP同属网络传输层。与TCP最大的不同是UDP是无连接的,同时,TCP连接是1对1的,即一个客户端想与另一个TCP服务通信,需要创建新的TCP连接;而UDP则是可以多个通信。由于UDP在网络环境差的情况下丢包严重,所以它常常应用于对偶尔丢失一俩个包不严重的场景,即视频、音频、DNS域名转换等场景。

TCP和UDP区别
类型 面向连接 传输可靠性 传输方式 传输效率 首部字节 应用场景
TCP 面向连接 可靠 字节流 20-60 要求通信数据可靠(如文件传输、邮件传输)
UDP 无连接 不可靠 数据报文段 8个字节 要求通信速度高(如域名转换、视频通话)

HTTP服务

HTTP全称是超文本传输协议(HyperText Transfer Protocol),构建在TCP之上,属于应用层协议,更多信息请查看HTTP-MDN

1. 特点
  1. 无连接:限制每次连接只处理一个请求,请求完成后断开连接。
  2. 无状态:服务器根据客户端请求,发送数据返回,发送完成后,不会记录任何信息。
  3. 简单快速:客户端向服务器发送请求时,只需传送请求方法和路径。
  4. 灵活:http允许传输任意类型的数据对象。
  5. 支持B/S模式。
2. HTTP报文

http的请求报文由请求行请求头以及请求体组成。

  • 请求行:请求方法 + 请求路径 + 协议版本
  • 请求头:以键值对的方式组成,如Content-Type:"text/html;charset=utf-8"
  • 请求体:需要发送给服务器的信息
2.1. 请求方法
方法 介绍
GET 请求一个指定资源的表示形式,被用于获取数据
HEAD 请求一个与GET请求的响应相同的响应,但没有响应体
POST 将实体提交到指定的资源,通常导致在服务器上的状态变化或副作用
PUT 更新指定资源
DELETE 删除指定资源
OPTIONS 描述目标资源的通信选项
2.2. 状态码
  • 1xx:服务器接收到信息,需要请求者继续执行操作
  • 2xx:操作被成功接收并处理
  • 3xx:重定向,需要进一步的操作以完成请求
  • 4xx:客户端错误,请求包含语法错误或无法完成请求
  • 5xx:服务器错误,服务器在处理请求过程中发生错误

常见状态码:

  • 100 Continue:继续,客户端继续其请求
  • 301 Moved Permanently:永久移动,请求资源被永久的移动到新URI,返回新URI,浏览器会自动重定向新的URI,接下来后面的任何请求都会使用新的URI代替
  • 302 Found:临时移动,资源只是临时移动,客户端继续使用原来的URI
  • 304 Not Modified:未修改,请求资源未发生更改,服务端返回304时,不会返回任何资源。
3. 附加:http缓存

浏览器缓存主要分为强缓存(本地缓存)和协商缓存(弱缓存)。

强缓存

强缓存由http请求头里的ExpiresCache-Control两个字段控制,用来表示资源的到期时间或缓存时间。

  • Expires Expires是HTTP/1.0的规范,它的值是一个绝对时间的GMT格式的时间字符串。由于是服务端返回的绝对时间,所以若客户端与服务端时间偏差较大时,会导致缓存混乱。

    Expires: Wed Mar 11 2020 15:15:15 GMT
    复制代码
  • Cache-Control Cache-Control是HTTP/1.1的规定,有以下多个字段:

    • max-age:代表资源的有效期,单位为s,如:Cache-Control:max-age=6000。
    • no-cache:强制确认缓存,需要使用缓存协商。
    • no-store:禁止使用缓存,每次客户端请求,都会下载完整的响应内容。
    • public:可以被任何中间人(中间代理、CDN)对其进行缓存。
    • private:专用某单个用户,中间人不能对其进行缓存。
    • must-revalidate:缓存验证确认,使用一个缓存时,必须先验证它的状态,已过期的缓存将不被使用。
  • Pragma

    Pragma是HTTP/1.0中定义的一个header属性,请求中包含Pragma的效果跟在头信息中定义Cache-Control: no-cache相同,但是HTTP的响应头没有明确定义这个属性,所以它不能拿来完全替代HTTP/1.1中定义的Cache-control头。通常定义Pragma以向后兼容基于HTTP/1.0的客户端。

协商缓存

协商缓存是由服务器来确定缓存资源是否可用,客户端需要通过某种标示来和服务器进行通信,来获知当前主要是否可用缓存访问。

协商缓存主要由两组头字段控制:

  • EtagIf-None-Match 浏览器在首次请求资源后,服务端会返回Etag字段,在再次请求该资源时,客户端发送If-None-Match字段,服务端通过If-None-Match的值,来判断是否命中缓存。
  • Last-ModifiedIf-Modified-Since 同样,浏览器首次请求资源时,服务端返回Last-Modified字段,代表资源文件最后修改的时间(也是绝对时间),再次请求时,客户端发送If-Modified-Since字段,服务端通过对比最后修改时间,来判断是否命中缓存。

在这两组头字段中,服务端会优先对比Etag/If-None-Match,若一致,继续对比Last-ModifiedIf-Modified-Since。如果命中缓存,响应头中还会返回Etag,但是不会返回资源内容以及Last-Modified字段。

Etag的优势:

  1. 首先资源文件的内容经过多次更改,最后内容依旧和之前的一致,这时Etag是不变的,但是它的最后修改时间会随着文件更改而变化,而我们可以通过Etag避免资源文件重新获取;
  2. Last-Modified只能精确到一秒,无法对在秒以下修改的内容进行判断。

如何计算缓存的优先级?

  1. 若存在Cache-Control:max-age,首先会判断Cache-Control,即Cache-control: max-age=N,就代表缓存Ns后过期;
  2. 若不存在Cache-Control,这个时候会判断Expires和头里面的Date判断缓存是否有效;
  3. max-ageExpires都不存在,就找Last-Modified,缓存时间就等于头里面的Date减去Last-Modified的值除以10。

HTTPS服务

由于HTTP传输的数据是以明文的形式存在,那一旦被中间人监控到,就很容易被篡改。HTTPS是基于HTTP协议的,通过SSL/TLS提供加密处理数据,验证以及保证数据的完整性。既然说到加密,就不得说一下加密的不同算法,以及它们之间的安全性对比。

1. 对称加密

对称加密也可以称为共享密钥加密,加密和解密通过相同密钥的一种加密方式。

处理过程如下:

  1. 客户端通过密码加密数据,然后将密文发送给服务端;
  2. 服务端通过密钥解密,得到原始数据,这样就可以避免数据被第三方窃听。

那么问题来了:A如何把密钥交给B呢?

  1. A又在网络上把向B发送密钥;
  2. 但是,密钥同样可能会被第三方窃听,这样导致数据也可以被第三方窃听。

既然密钥被窃听,那么我们可不可以先加密密钥再进行发送呢?问题又回到最初了,加密的密钥如何发送呢?所以解决这个问题,可以使用密钥交换协议非对称加密

2. 密钥交换协议

密钥交换协议是一种可以在通信双方之间安全交换密钥的方法。通过讲双方共有的秘密数值隐藏在公开数值相关的运算中,来实现双方之间密钥的安全交换。

在讲解处理过程之前,首先了解一下这个算法的概念。

假设有一种方法可以合成两个密钥。即密钥P和密钥S,合成后得到密钥P-S。这种合成有三个特点:

  1. 即使持有密钥P和合成密钥P-S,也无法将密钥S单独取出来,即密钥之间可以合成,不可以分解;
  2. 不管是怎样合成的密钥,都可以继续与其他密钥继续合成。比如上面的密钥P-S还可以与密钥K继续合成,得到新的合成密钥P-S-K;
  3. 密钥的合成与合成顺序无关,只与哪些密钥有关。比如密钥A和密钥B合成后,得到合成密钥A-B,再与密钥C合成,得到新的合成密钥A-B-C;或者密钥A和密钥C合成后,得到合成密钥A-C,再继续与密钥B合成,得到新的合成密钥A-C-B。此时密钥A-B-C和密钥A-C-B是一样的。

接下来,我们看下来客户端和服务端是如何进行交换密钥的:

  1. 首先A生成密钥P,然后A把密钥P发送给B;
  2. A和B各自准备自己的私钥SA和SB;
  3. A利用密钥P和私钥SA合成密钥P-SA,B利用密钥P和私钥SB合成密钥P-SB;
  4. A将密钥P-SA发送给B,B也就密钥P-SB发送给A;
  5. A和B用自己的私钥与对方发送的合成密钥合成新的密钥SA-P-SB;
  6. 此时,密钥SA-P-SB,便可以当成加密和解密的密钥来使用。

那么,整个过程中,密钥P,密钥P-SA,密钥P-SB都有可能会被第三方窃听,由于密钥是无法分解的,所以第三方是无法通过窃听到的密钥合成出P-SA-SB,因此此方法是安全的。

3. 非对称加密

非对称加密也可以叫公开密钥加密。加密和解密使用不同的密钥进行加密。

处理过程如下:

  1. 首先由接收方B生成公钥和私钥,然后把公钥发送给A;
  2. A通公钥进行加密数据,B得到密文后,通过私钥解密。

对比对称加密,使用非对称加密不会出现密钥分配的问题。在使用对称加密的时候,密钥的需求数量会随着发送人数的增多而急剧增多,即:2个需要2个,3个人就需要3个,4个人就需要6个......n个人就需要n*(n-1)/2个。而非对称加密只需要接受方保存私钥即可。

那么问题来了:如果第三方也准备了公钥和私钥呢?

  1. 在B把公钥发送给A时,第三方把自己的公钥给了A,A又不知情;
  2. 接下来A用第三方的公钥加密发送,第三方用自己的私钥再解密;
  3. 第三方用B的公钥加密发送B,B用自己的私钥解密;
  4. 这个过程中,A和B都不知道数据已经被第三方窃听了。

非对称加密的可靠性出现的问题就是:A无法判断收到的公钥是B的还是第三方?接下来,我们就需要用数字证书来解决这个问题。

非对称加密必须满足下面的条件:

  1. 可以使用某个数值对数据加密计算;
  2. 使用另一个数值时对加密数据进行计算,就可以把数据恢复原样;
  3. 无法从一个密钥推算出另一个密钥。

通过上面的满足条件,我们可以发现非对称加密的整个加密和解密过程都比较耗时,所以说这种加密算法不适用持续发送零碎数据的情况,这个时候就要用到混合加密。

4. 混合加密

通过对称加密和非对称加密的分析,在混合加密的过程中,需要用处理速度快的对称加密算法对数据进行加密,加密的密钥则是由非对称加密的公钥进行处理。

处理过程如下:

  1. B首先将公钥发送给A,A接收到后,用公钥加密对数据处理的密钥,然后发送给B;
  2. B用自己的私钥解密后,得到对数据处理的密钥;
  3. 接下来,A用自己的密钥加密数据和B进行通信。
5. 消息认证码

密文在传输过程中可能会被篡改,这会导致解密后的内容发送变化,而消息认证码可以预防这种情况的发生。

首先A发生密文给B,但是这个过程中,可能会被第三方篡改,而B得到密文后无法感知密文的正确性,从而解密后得到了错误的消息。为了解决这一问题,可以通过消息认证码处理:

  1. A生成一个用于制作消息认证码的密钥,通过安全的方式将密钥发送给B;
  2. A在发送消息时,将密文和密钥生成一个hash值,这个值便是消息认证码MAC(Message Authentication Code);
  3. B在接收到消息时,通过密钥和接收到密文也生成MAC,与A发送的MAC做对比,便可以知道密文是否被篡改。

然而,这种方法也有缺点。在使用消息认证码的过程中,AB双方都可以对消息进行加密并算出MAC。所以,无法证明原本的消息是A生成的还是B生成的。那么,接下来,可以通过数字签名来解决。

6. 数字签名

数字签名不仅可以实现消息认证码的认证和检测篡改功能,还可以预防事后否认问题的发生。

首先,看一下数字签名的特征,假设A向B发生消息:

  1. 在发送前A给消息加上数字签名,而数字签名只能由A生成;
  2. B接收到后,验证数字签名,就可以确认发送者是否是A。

接下来,来看一下数字签名是怎么生成的。首先,数字签名的生成使用的是非对称加密,在非对称加密中,加密使用的是公钥,解密是私钥,任何人都可以通过公钥进行数据加密,但只有私钥的持有者才可以解密。然而在数字签名的生成过程正好是相反的:

  1. 首先A准备好公钥和私钥,A将公钥发送给B;
  2. 然后A通过私钥加密信息,加密后的信息就是数字签名,A将消息和签名都发送给B;
  3. B使用公钥对签名解密,通过判断解密后的信息与消息对比是否一致,来确认消息。

虽然解决了预防事后否认的问题,但是还是存在问题:B在接收公钥时,无法确认公钥的生成者是否是A。所以接下来,使用数字证书来解决。

7. 数字证书

同样,A持有公钥和私钥,如何安全的将公钥发送给B:

  1. 首先A需要向认证中心(Certification Authority,CA)申请发行证书,证明公钥确实由自己生成;
  2. A将公钥和包含邮箱信息的个人资料发送给认证中心;
  3. 认证中心对资料进行确认,使用认证中心自己的私钥,根据A的资料生成数字签名,将数字签名和资料整理起来;
  4. 然后将这个文件发送给A,这个文件便是A的数字证书;
  5. A将数字证书发送给B;
  6. B接收到数字证书并确认后,获取认证中心的公钥;
  7. B通过公钥对证书中的签名进行验证,判断是否为认证中心给的签名;
  8. B确认成功后,从证书中取出A的公钥,这样子,A的公钥就传给B了,接下来就可以继续通信了。

这个时候,来看一下第三方是否还有机会发送自己公钥,第三方只有自己去认证中心登记自己的公钥,然而第三方是无法使用A的邮箱地址的,所以无法获得A的证书。这样子就保证了公钥的安全性。

总结

以上便是笔者在学习网络协议过程中做的笔记,可能有些知识点没有说到或说的很详细,还请各位包涵。当然,做为一名前端开发人员,我认为对于网络协议部分的了解不止于此!