应用层传输层网络层

Jan 13, 2025

53 mins read

网络通信协议(互联网协议) - cls超 - 博客园

1. IP篇

ip地址划分

A类:1.0.0.1 ~ 126.255.255.254(0 - 127)

B类:128.0.0.1 ~ 191.255.255.254(128 - 192)

C类:192.0.0.1 ~ 223.255.255.254 (192 - 224)

D类 组播 VRRP协议(224 - 239)

E类 科研(240 - 255)

其中127地址可用作本地软件环回测试,进程之间通信。127.0.0.1(本机地址) 是一个不可路由私有 IP 地址

通过判断第1位地址为0还是1、第2位地址为0还是1..来判断是哪类地址。

私有ip地址范围:

A类 10.0.0.0 ~ 10.255.255.255

B类 172.16.0.0 ~ 172.31.255.255

C类 192.168.0.0 ~ 192.168.255.255

默认网关 = 路由器地址公网ip(自己电脑看是内网ip)

主机号全为0表示某个网络,主机号全为1表示某个网络下的所有主机,用于广播。

广播地址用于在同一个链路中互相连接的主机之间发送数据包。

子网掩码

子网掩码表示子网络特征的一个参数,它的网络部分全部为1,主机部分全部为0,比如255.255.255.128。

知道子网掩码就可以判断两个IP地址是否处于同一网段,将两个IP地址分别进行AND运算,比较结果是否相同,如果是的话就表明在同一网络中。

假设有一个IP地址172.16.10.1的子网掩码是255.255.255.0(对应二进制有24个1),所以可以写为172.16.10.1/24。

IP协议的作用主要有两个,一个是为每一台计算机分配IP地址(DHCP动态分配),另一个是确定哪些地址在同一个子网络

DNS解析

DNS(Domain Name System):域名系统,将域名解析成ip地址

计算机和计算机直接通信是通过ip:端口来实现的。

DNS由来:

比如说你想访问百度,但是没办法记住它的ip地址,因为就算记住了百度,还有其他很多别的网站。于是计算机帮我们将ip地址保留在系统里,C:\windows\system32\drivers\etc\hosts文件

后来有网站专门提供最新的host,但是不断更新很麻烦,所以出现了DNS服务器。

比如阿里云的:235.5.5.5,谷歌 8.8.8.8。

DNS解析流程

  • 缓存查询:首先浏览器会检查本地DNS缓存能不能查到,如果查不到就会检查操作系统缓存C盘host文件,如果还找不到就去本地DNS服务器发送请求。

  • 根DNS服务器查询:如果在本地DNS服务器缓存查不到,就会去根域名服务器请求解析(全球只有13台DNS根服务器,.com域名服务器.com .cn .cloud就是顶级域名,.代表根的意思)

  • 顶级DNS服务器查询:根DNS服务器返回给本地DNS域名服务器一个顶级DNS服务器地址。

  • NameServer查询:本地DNS服务器再向刚才获得的顶级DNS服务器发送解析请求。顶级DNS服务器会返回此域名对应的Name Server域名服务器的地址。 比如我要访问www.baidu.com,而这个域名是从baidu公司注册获得的,那么baidu公司上的服务器一定有相关信息。

  • 域名和IP映射关系返回:Name Server会查询并返回存储的域名和IP的映射关系表,连同TTL值。

  • 本地DNS服务器缓存:本地DNS服务器会根据TTL值(Time To Live 缓存有效时间)缓存这个映射关系。

  • 将IP地址返回给本地电脑:电脑根据TTL值缓存结果,完成域名解析。

递归查询:客户端发起请求后,所有的查询工作由DNS服务器来完成,直到找到最终的IP地址。

迭代查询客户端自己向每一层的DNS服务器发起查询请求,从根DNS服务器开始,到顶级DNS服务器,再到目标域名的授权DNS服务器,直到获得结果。


linux上查看DNS解析信息的指令:dig、nslookup、host

www.  zhihu. com   .
三级域 二级域 顶级域 根域

域名解析记录

主要分为A记录、MX记录、CNAME记录、NS记录和TXT记录

DNS域名详细解析过程(最全面,看这一篇就够)_dns解析-CSDN博客

文字编码

ASCII码 每个字符占1字节,不支持中文。00000000 - 11111111涵盖256种符号。

GB2312 是由于ASCII不支持中文而诞生来的,每个中文占2个字节,但是只涵盖6700多个,其他字符占1个字节。

GBK 升级了GB2312,涵盖了2w多个中文。

Unicode 每个国家都有自己的语言,于是出现了万国码,每个字符占4个字节

utf-8 由于Unicode的英文占据太多空间,所以推出可变长编码,一个英文1个字节,1个汉字3个字节

ARP协议

ARP协议的作用是将ip地址解析成mac地址,工作在网络层和数据链路层中间,在七层模型中属于数据链路层

假设在局域网内,一台交换机连接多台设备,当192.168.19.11:57101想发送给192.168.19.16:80时,arp广播会请求寻找目标mac地址在哪。

主机会通过广播发送ARP请求,包含了目标主机的IP地址

当同一个链路中的所有设备收到了ARP请求后,就会拆开请求包,去核对IP地址和自己的IP地址是否一致,如果一致的话,就将自己的MAC地址塞入ARP响应包,返回主机。

RARP协议与ARP协议相反,已知MAC地址求IP地址。

25

NAT

由于IPv4的地址非常紧缺,所以提出一种网络地址转换NAT的方法,简而言之就是把私有IP地址转换成公有IP地址。

2. TCP篇

TCP三次握手、四次挥手

TCP三次握手:

21

  • 源端口和目的端口,各占2个字节,分别写入源端口和目的端口;
  • 序号,占4个字节,TCP连接中传送的字节流中的每个字节都按顺序编号。例如,一段报文的序号字段值是 301 ,而携带的数据共有100字段,显然下一个报文段(如果还有的话)的数据序号应该从401开始;
  • 确认号,占4个字节,是期望收到对方下一个报文的第一个数据字节的序号。例如,B收到了A发送过来的报文,其序列号字段是501,而数据长度是200字节,这表明B正确的收到了A发送的到序号700为止的数据。因此,B期望收到A的下一个数据序号是701,于是B在发送给A的确认报文段中把确认号置为701;
  • 数据偏移,占4位,它指出TCP报文的数据距离TCP报文段的起始处有多远;
  • 保留,占6位,保留今后使用,但目前应都位0;
  • 紧急URG,当URG=1,表明紧急指针字段有效。告诉系统此报文段中有紧急数据;
  • 确认ACK,仅当ACK=1时,确认号字段才有效。TCP规定,在连接建立后所有报文的传输都必须把ACK置1;
  • 推送PSH,当两个应用进程进行交互式通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应,这时候就将PSH=1;
  • 复位RST,当RST=1,表明TCP连接中出现严重差错,必须释放连接,然后再重新建立连接;
  • 同步SYN,在连接建立时用来同步序号。当SYN=1,ACK=0,表明是连接请求报文,若同意连接,则响应报文中应该使SYN=1,ACK=1;
  • 终止FIN,用来释放连接。当FIN=1,表明此报文的发送方的数据已经发送完毕,并且要求释放;
  • 窗口,占2字节,指的是通知接收方,发送本报文你需要有多大的空间来接受;
  • 检验和,占2字节,校验首部和数据这两部分;
  • 紧急指针,占2字节,指出本报文段中的紧急数据的字节数;
  • 选项,长度可变,定义一些其他的可选的参数。

22

第一次握手:客户端创建传输控制块TCB,进入监听Listen状态。设置SYN = 1,发送给服务器,设置序列号为seq = x,此时客户端处于同步已发送SYN-SENT状态。

第二次握手:设置ACK = 1表示确认应答,ack = x + 1表示已收到客户端x之前的数据,希望下次数据从x+1开始,设置SYN = 1,发送给客户端,设置序列号seq = y,此时服务器处于同步已接受SYN-RCVD状态。

第三次握手:设置ACK = 1表示确认应答,ack = y + 1seq = x + 1表示接着上一个数据包seq = x继续发送。至此建立连接ESTABLISHED。

为什么TCP客户端最后还要发送一次确认呢?

防止已经失效的连接请求报文又突然传送到了服务器。

两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。

TCP四次挥手:

第一次挥手:客户端向服务端发送FIN=1,其序号为seq=u,它等于前面以传送过的数据的最后一个字节的序号加1。

第二次挥手:服务端收到释放连接请求,ACK = 1,ack = u + 1seq = v,这个序号v是等于前面已传送过的数据的最后一个字节的序号加1,然后B就进入CLOSE-WAIT(关闭等待)状态。

第三次挥手:客户端收到来自服务端的确认后,就进入了FIN-WAIT-2(终止等待2)状态满等待B发出的连接释放报文段。若已经没有要发送的数据,其应用进程就通知TCP释放连接,这时服务端发出的连接释放报文段必须使FIN = 1seq = w,还必须重复上次已发送过的确认号ack = u + 1。这时服务端就进入LAST-ACK(最后确认)状态,等待客户端的确认。

第四次挥手:客户端收到连接释放报文段后,必须确认,ACK = 1ack = w + 1seq = u + 1(因为FIN报文段要消耗1个序号),然后客户端进入TIME-WAIT(时间等待)状态。

23

现在TCP连接并没有释放掉,必须经过2MSL后,才可以进入到CLOSED状态

为什么要等待2MSL?

如果客户端发送的 ACK 报文段丢失,服务器在接收不到 ACK 的情况下会一直重发 FIN 报文段,因此客户端为了确保服务器收到了 ACK,会设置一个定时器,并在 TIME-WAIT 状态等待 2MSL 的时间,如果在此期间又收到了来自服务器的 FIN 报文段,那么客户端会重新设置计时器并再次等待 2MSL 的时间,如果在这段时间内没有收到来自服务器的 FIN 报文,那就说明服务器已经成功收到了 ACK 报文,此时客户端就可以进入 CLOSED 状态了。

2MSL是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃,这样新的连接中不会出现旧连接的请求报文

图解 TCP 四次挥手|深度解析|为什么是四次|为什么要等2MSL_tcp链接谁先发送-CSDN博客

为什么说TCP是面向字节流的协议而UDP是面向报文的协议?

UDP传输时,操作系统不会对消息进行拆分,也就是说每个UDP报文就是一个用户消息的边界。接收方读一个UDP报文就能读取到完整的用户消息。

那么如果收到了2个UDP报文,该怎么区分开?

操作系统在收到UDP报文后,会将其插入到队列里,队列的每一个元素就是一个UDP报文,当用户调用recvfrom()时就会从队列中取出一个数据。

而TCP传输时,消息会被分组成多个TCP报文。当发送数据时,数据并没有真正从网络上发送出去,只是从应用程序拷贝到了操作系统内核协议栈中。至于什么时候被发送,取决于发送窗口、拥塞窗口以及当前发送缓冲区的大小等条件。当两个消息的某个部分被分到同一个TCP报文时(比如说报文1Hello 报文2 World,变成了 报文1He 报文2lloWorld),就是粘包问题。

如何解决粘包?

  1. 固定长度的消息

    规定一个消息长度是n字节,当接收方接受满n字节,就认为完整收到了。灵活性差。

  2. 特殊字符作为边界

    在两个用户消息之间插入一个特殊的字符串,这样在接收数据时,如果读到这个特殊字符,就认为读完了一条完整的消息,比如HTTP

  3. 自定义消息结构

    首先用4个字节大小的变量来表示数据长度,数据紧跟其后。

    struct{
    	u_int32_t message_length;
    	char message_data[];
    }message;
    

3. HTTP

超文本传输协议(HTTP):一种无状态的,以请求应答方式运行,可扩展(添加自定义头)的协议。

无状态:服务器不会记忆HTTP状态,所以能减轻服务器的负担。但是在完成有关联性的操作时会非常麻烦,比如说在下单、结算、支付的时候每次都要问一遍身份信息。

对于无状态可以使用Cookie技术来解决,通过在请求和响应报文中写入Cookie信息来控制客户端的状态。

HTTP协议格式:

26

请求行报文格式:

27

响应行报文格式:

28

HTTP请求过程

  1. DNS解析:当用户输入网址后,首先DNS解析流程
  2. TCP三次握手:解析完成后,TCP三次握手建立连接,客户端发送SYN包(同步请求),服务器返回SYN-ACK包(确认响应),客户端再返回ACK包(确认收到),连接建立成功。
  3. HTTP请求:建立TCP连接后,浏览器会通过该连接发送HTTP请求报文,向服务器请求所需的资源(比如网页);
  4. HTTP响应:服务器收到请求后,返回HTTP响应报文,包括网页内容等;
  5. 网页渲染:浏览器收到HTTP响应后会根据响应内容进行页面渲染;
  6. TCP四次挥手:一旦网页渲染完成,浏览器和服务器之间的连接通过TCP四次挥手断开。客户端发送FIN包(请求断开连接)。服务器返回ACK包(确认收到断开请求)。服务器发送FIN包(请求断开连接)。客户端返回ACK包(确认断开请求),连接完全关闭。

由于HTTP天生明文的特点,整个传输过程完全透明,任何人都能够截获、修改报文。所以诞生了HTTPS协议。所以HTTP不安全,所以会引入HTTPS,通过引入SSL / TLS层。HTTPS 在 TCP 三次握手之后,还需进行 SSL/TLS 的握手过程,才可进入加密报文传输。

HTTPS

HTTPS为什么安全?

  1. 混合加密

    对称加密(通信建立前)使用xor异或,将原文与密钥异或得到密文,将密文与密钥异或得到原文。速度快。

    非对称加密(通信过程中):有公钥和私钥,公钥可以公开给任何人使用,而私钥必须保密。用公钥把数据加密传送给服务端,只能通过私钥解密。速度慢。

    • 使用公钥加密 使用私钥解密 保证内容传输的安全
    • 使用私钥加密 使用公钥解密 保证消息不会被冒充

    TLS中使用混合加密方式,非对称加密的主要用途是通过**「私钥加密,公钥解密」的方式,来确认消息的身份**,也就是常说的数字签名算法。

  2. 摘要算法

    为了保证内容不被篡改,我们需要对内容计算出一个指纹,对方收到后也计算出一个指纹,通过比对,如果相同则没有被篡改。

    摘要算法(哈希函数)计算内容的哈希值,哈希值是唯一的,且无法推导出内容。

    通过哈希算法可以保证内容不被篡改,但无法保证内容和哈希值不会被替换,因为缺少是否来源于服务端的证明

  3. 数字证书

    防止公钥被冒充。通过权威机构CA(数字证书认证机构),将服务器公钥放在数字证书中,只要证书可信,公钥就可信。

HTTPS建立连接

SSL/TLS 协议基本流程:

  1. 客户端向服务端索要并验证服务器的公钥
  2. 双方协商生成会话密钥
  3. 采用会话密钥进行加密通信

减少HTTP请求次数:1.缓存 2.减少重定向次数 3.合并请求

如何做到缓存?

客户端会把第一次请求和响应的数据保存到本地的磁盘上,请求URL作为key,响应作为value,形成映射关系。后续发起相同请求的时候,就可以通过key查找到value。

为了防止缓存的响应不是最新的。响应头部中会有一个过期时间,如果客户端在查看的时候发现响应过期了,就会重新发送请求。

如果请求后得到的资源没有变更(通过比对唯一标识判断是否同一资源、有无更新),则服务器返回不含有包体(304 Not Modified)响应,告诉客户端仍然有效,这样就可以减少响应资源在网络中传输的延时。

缓存有两种实现方式,强制缓存和协商缓存
强制缓存指的是:浏览器判断缓存没过期,则直接使用浏览器的本地缓存。决定是否使用缓存的主动权在浏览器这边。
协商缓存指的是:与服务端协商之后,通过协商结果来判断是否使用本地缓存。

什么是重定向请求?

客户端发送一个url1请求给服务端,但是服务器的资源已经从url1迁移到url2了,这个时候服务器返回302响应码Location头部,告诉客户端资源已经迁移到url2了,这个时候客户端需要重新发送url2请求。

但是重定向请求越多,客户端就要多次发起HTTP,而且服务端这一方往往不只有一台服务器,比如源服务器上一级是代理服务器,然后代理服务器才与客户端通信,这时客户端重定向就会导致客户端与代理服务器之间需要 2 次消息传递,所以会降低网络性能。

如果重定向工作交由代理服务器完成,就能减少HTTP请求次数了。

合并请求:如果把多个访问小文件的请求合并成一个大的请求,虽然传输的总资源还是一样,但是减少请求,也就意味着减少了重复发送的 HTTP 头部。合并请求也会减少TCP连接的数量,省去了TCP握手和慢启动过程耗费的时间。

延迟发送请求:请求网页时只获取用户所看到的网页资源,当用户往下滑动页面的时候,再向服务器获取接下来的资源。达到延迟发送请求的效果。

减少HTTP响应的数据大小:无损压缩(文本、程序)、有损压缩(图片、视频)。举例子一个视频,只需要一个静态关键帧,然后使用增量数据来表达后续的帧。


五大类HTTP状态码

具体含义 常见的状态码
1xx 提示信息,表示协议处理的中间状态
2xx 服务器成功处理了客户端请求 200、204、206
3xx 重定向,资源位置发生变动,需要客户端重新发送请求 301、302、304
4xx 客户端错误,请求报文有误,服务器无法处理 400、403、404
5xx 服务器错误,服务器在处理请求时内部发生了错误 500、501、502、503

HTTP常见字段:

Host服务器域名、Content-Length数据长度、Connection可以要求服务器使用HTTP长连接机制、Content-Type告诉客户端本次数据是什么格式、Content-Encoding说明数据的压缩方法。

HTTP协议通过设置回车符、换行符作为HTTP header的边界,通过Content-Length字段作为HTTP body的边界,这两个方式都是为了解决粘包的问题。

24

HTTP长连接的特点是,只要任意一端没有明确提出要断开连接,则保持TCP连接状态。HTTP的Keep-Alive就是实现了这个功能,可以使用同一个TCP连接来发送和接收多个HTTP请求 / 应答,避免了连接建立和释放的开销

RSA 握手解析

TLS 握手是建立安全连接的过程,它可以使用不同的算法(如 RSA、ECDHE)来完成身份认证和密钥交换。

TLS是怎么解决HTTP风险的?

  • 信息加密(传输数据被加密)
  • 校验机制(校验是否有被篡改过)
  • 身份证书(证明服务端真的是服务端)

29

TLS第一次握手

客户端首先会发一个Client Hello消息,这是跟服务器打招呼。

消息中包含着TLS版本号、支持的密码套件列表,以及生成的随机数(Server Random)

TLS第二次握手

服务端收到客户端的Client Hello消息后,会确认TLS版本号是否支持,和从密码套件列表中选择一个密码套件,以及生成随机数(Client Random)

然后返回Server Hello消息,消息中包含着服务端确认的TLS版本号、合适的密码套件和给出的随机数。

其中密码套件的形式是「密钥交换算法 + 签名算法 + 对称加密算法 + 摘要算法

然后服务端为了证明自己的身份,会发送Server Certificate消息给客户端,包含着数字证书(RSA证书)

RSA证书就是CA颁发的数字证书,用于身份认证,包含RSA公钥、域名、数字签名(由CA私钥签名的防止篡改)、有效期、颁发者CA。

随后客户端发送Server Hello Done消息,目的是告诉客户端,我把该发送的全发送完了。至此本次打招呼完毕。

TLS第三次握手

客户端怎么校验服务端发送的数字证书是否可信?

客户端会使用hash算法获取该证书的hash值H1;

浏览器收到证书后会使用CA的公钥解密(操作系统或者浏览器已经集成了CA的公钥信息),得到一个hash值H2。

通过比较H1和H2,如果值相同则为可信赖的证书,否则不可信。

客户端验证完证书后,会生成一个新随机数(Pre-Master Secret)。现在双方共享了三个随机数了**,分别是 Client Random、Server Random、Pre-Master Secret**。

其中Pre-Master Secret会被服务端的RSA公钥加密,然后通过Client Key Exchange发给服务器,服务器会用RSA私钥解密,得到Pre-Master Secret。至此双方根据这三个随机数生成会话密钥

生成会话密钥之后,客户端会发一个Change Cipher Spec,告诉服务端开始使用加密方式发送消息。

然后客户端再发一个Encrypted Handshake Message(Finished)消息,把之前发送的所有数据做个摘要,再用会话密钥加密一下,让服务器做个验证(验证之前握手信息是否被篡改,加密通信是否可用)。

Change Cipher Spec之前传输的TLS握手数据都是明文,之后都是密文

TLS第四次握手

服务器同样发送Change Cipher SpecEncrypted Handshake Message消息。

如果双方都验证加密和解密没有任何问题,就完成了握手,就可以使用会话密钥加密HTTP请求和响应了。

RSA算法的缺陷

  1. 私钥泄露导致历史通信被破解(缺乏前向安全性)

    RSA 密钥协商是基于服务器的私钥进行解密的,如果服务器私钥泄露,过去所有用该公钥加密的会话都能被解密。

  2. 公钥被攻击者替换(中间人攻击)

    如果攻击者能拦截并替换服务器的公钥,就可以用自己的私钥解密客户端的加密信息,再用服务器的公钥重新加密,伪装成服务器。

为了解决这个问题,后面就出现了 ECDHE 密钥协商算法,现在大多数网站都用的是ECDHE密钥协商算法

ECDHE 握手解析

Diffie-Hellman(DH)密钥交换家族

依赖于离散对数,选择一个大素数p和一个原根gp和g是公开的。

密钥交换

​ Alice 选一个私钥 a,计算 A = g^a mod p,然后发送 A 给 Bob。

​ Bob 选一个私钥 b,计算 B = g^b mod p,然后发送 B 给 Alice。

计算共享密钥

​ Alice 计算:K = B^a mod p = (g^b)^a mod p

​ Bob 计算:K = A^b mod p = (g^a)^b mod p

由于从g^a mod p 计算 a是很困难的,所以很难破解密钥。

但是static DH算法中有一方的私钥是静态的,所以不具有前向安全性,可能在海量数据中暴力破解会被破解。

DHE算法就是解决这个问题,让双方的私钥在每次密钥交换时都是随机生成的、临时的。但是效率低,因为运算困难。

所以ECDHE(椭圆曲线离散对数)选用椭圆曲线上的点代替了大素数的模运算,提高了性能和安全性,因为椭圆具有乘法交换性。

「 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384」

  • 密钥协商算法使用 ECDHE;

  • 签名算法使用 RSA;

  • 握手后的通信使用 AES 对称算法,密钥长度 256 位,分组模式是 GCM;

  • 摘要算法使用 SHA384;

TLS第二次握手

因为服务端选择了ECDHE密钥协商算法,所以再发送完证书后,发送Server Key Exchange消息。

服务器做了三件事:

  1. 选择椭圆曲线和基点,公开给客户端。
  2. 生成随机数作为服务端椭圆曲线的私钥,保留在本地。
  3. 根据基点G和私钥计算出服务端的椭圆曲线公钥,公开给客户端。

为了保证这个椭圆曲线的公钥不被第三方篡改,服务端会用 RSA 签名算法给服务端的椭圆曲线公钥做个签名。

至此二次握手完成,目前已经共享了:Client RandomServer Random使用的椭圆曲线椭圆曲线基点G服务端椭圆曲线的公钥

TLS第三次握手

客户端也会生成一个随机数作为客户端椭圆曲线的私钥,然后根据服务端前面给的信息,去生成客户端的椭圆曲线公钥,然后用Client Key Exchange消息发给服务端。

至此双方都有了对方的椭圆曲线密钥,自己的椭圆曲线私钥、椭圆曲线基点G。所以双方都算出(x,y)。最后的会话密钥是用**客户端随机数 + 服务端随机数 + x(ECDHE算法算出的共享密钥)**生成。之后客户端会发送Change Cipher SpecEncrypted Handshake Message

RSA和ECDHE握手区别

  • RSA不支持前向保密,而ECDHE支持(服务端私钥不会被破解)。

  • RSA在TLS完成四次握手后才能进行数据传输,而ECDHE可以节省一个消息的往返时间,提前发出加密数据。

    因为RSA 需要服务器先解密 Pre-Master Secret,才能计算最终密钥(所以握手必须完成后才能加密数据)。

    ECDHE 的共享密钥计算不依赖私钥解密,计算完成后服务器就能直接加密数据(所以 TLS 1.3 省去了一次往返)。

  • 使用ECDHE,在第二次握手时,会出现服务器端发出的Server Key Exchange消息,而RSA中没有。

Sharing is caring!