网络协议汇总

时间:2024-02-21 22:13:46

1.HTTP协议

 1.认识URL

  平时我们俗称的 "网址" 其实就是说的 URL

URL中的字符只能是ASCII字符,但是ASCII字符比较少,而URL则常常包含ASCII字符集以外的字符,如非英语字符、汉字、特殊符号等等,所以要对URL进行转换。这个过程就叫做URL编码,或者叫URL转义,实质上就是将包含非ASCII字符的URL转换为有效的ASCII字符格式。

转义的规则如下 :
将需要转码的字符转为 16 进制,然后从右到左,取 4 ( 不足 4 位直接处理 ) ,每 2 位做一位,前面加上 % ,编码成 %XY格式,它对应于该字符在ISO-8859-1字符集里的编码值。

2.HTTP协议

 超文本传输协议(Hypertext Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。请求和响应消息的头以ASCII形式给出;而 消息内容则具有一个类似MIME的格式。

HTTP是基于客户/服务器模式,且面向连接的。典型的HTTP事务处理有如下的过程:

(1)客户与服务器建立连接;

(2)客户向服务器提出请求;

(3)服务器接受请求,并根据请求返回相应的文件作为应答;

(4)客户与服务器关闭连接。

HTTP报文格式

HTTP报文由从客户机到服务器的请求和从服务器到客户机的响应构成。

请求报文格式如下: 请求行 - 通用信息头 - 请求头 - 实体头 - 报文主体

应答报文格式如下:

状态行 - 通用信息头 - 响应头 - 实体头 - 报文主体

状态码元由3位数字组成,表示请求是否被理解或被满足。

HTTP请求

首行 : [ 方法 ] + [url] + [ 版本 ]
Header: 请求的属性 , 冒号分割的键值对 ; 每组属性之间使用 \n 分隔 ; 遇到空行表示 Header 部分结束
Body: 空行后面的内容都是 Body. Body 允许为空字符串 . 如果 Body 存在 , 则在 Header 中会有一个Content-Length属性来标识 Body 的长度 ;

HTTP响应

首行 : [ 版本号 ] + [ 状态码 ] + [ 状态码解释 ]
Header: 请求的属性 , 冒号分割的键值对 ; 每组属性之间使用 \n 分隔 ; 遇到空行表示 Header 部分结束
Body: 空行后面的内容都是 Body. Body 允许为空字符串 . 如果 Body 存在 , 则在 Header 中会有一个Content-Length属性来标识 Body 的长度 ; 如果服务器返回了一个 html 页面 , 那么 html 页面内容就是在body中 .

HTTP的方法

HTTP的状态码

1XX:信息提示

2XX:成功

3XX:重定向

4XX:客户端错误

5XX:服务器错误

HTTP常见Header

Content-Type: 数据类型 (text/html )
Content-Length: Body 的长度
Host: 客户端告知服务器 , 所请求的资源是在哪个主机的哪个端口上 ;
User-Agent: 声明用户的操作系统和浏览器版本信息 ;
referer: 当前页面是从哪个页面跳转过来的 ;
location: 搭配 3xx 状态码使用 , 告诉客户端接下来要去哪里访问 ;
Cookie: 用于在客户端存储少量信息 . 通常用于实现会话 (session) 的功能 ;

HTTP1.0和HTTP1.1的区别:

1.长连接:HTTP1.0只支持浏览器与服务器的短连接,即每次请求都要重新建立连接,服务器无法记录每个历史请求,HTTP1.1支持长连接即在一次连接下,浏览器可以向服务器发送多次请求
2.增加Host字段:HTTP1.0中认为每个服务器都绑定这唯一一个IP,所有发送的请求头URL中没有host信息,而HTTP1.1在请求和响应中都支持了host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)
3.缓存:HTTP1.1在1.0的基础上加入了一些cache的新特性,当缓存对象的Age超过Expire时变为stale对象,cache不需要直接抛弃stale对象,而是与源服务器进行重新激活(revalidation)。
4.错误提示:HTTP1.0中定义了16个状态码,对错误或警告的提示不够具体。HTTP1.1引入了一个Warning头域,增加对错误或警告信息的描述,并且还新增了24个状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。

HTTP1.X和HTTP2.0的区别

 1.增加二进制格式解析:HTTP1.X解析基于文本,而文本格式本身就具有多样性,很多场景下不方便,而引入二进制后,只有0和1组合,使解析更加方便也增强了健壮性
2.多路复用:即每个request都是是用作连接共享机制的,每个request都对应一个id,使一个连接可以有多个请求,再根据id将request归属到不同的服务端请求里
3.header压缩:HTTP1.X中,每次传输都要写点header头,占用了大量数据,因此HTTP2.0在客户端和服务端各保存了一份header fields表,每次传输时只需传输header的更新信息,将header fields表更新即可实现header传输
4.服务端推送:HTTP2.0也添加了server push功能

2.HTTPS协议

HTTPS (全称:Hypertext Transfer Protocol Secure ),是以安全为目标的 HTTP 通道,在HTTP的基础上通过传输加密和身份认证保证了传输过程的安全性 。HTTPS 在HTTP 的基础下加入SSL,HTTPS 的安全基础是 SSL,因此加密的详细内容就需要 SSL。 HTTPS 存在不同于 HTTP 的默认端口及一个加密/身份验证层(在 HTTP与 TCP 之间)。这个系统提供了身份验证与加密通讯方法。 

其保证安全性的做法是通过证书验证和对信息混合加密的方式。

对称加密

采⽤单钥 密码系统 的加密⽅法,同⼀个 密钥 可以同时⽤作信息的加密和解密,这种加密⽅法称为对称加密,也称为单密钥加密,特征:加密和解密所⽤的密钥是相同的
常⻅对称加密算法(了解): DES 3DES 、AES、TDEA、 Blowfish 、RC2等
特点:算法公开、计算量⼩、加密速度快、加密效率⾼

对称加密其实就是通过同⼀个 "密钥" , 把明⽂加密成密⽂, 并且也能把密⽂解密成明⽂. 

非对称加密 

需要两个 密钥 来进⾏加密和解密,这两个密钥是 公开密钥 (public key,简称公钥)和私有密钥
(private key,简称私钥)。
常⻅⾮对称加密算法(了解):RSA,DSA,ECDSA
特点:算法强度复杂、安全性依赖于算法与密钥但是由于其算法复杂,⽽使得加密解密速度没有对称加密解密的速度快。
⾮对称加密要⽤到两个密钥, ⼀个叫做 "公钥", ⼀个叫做 "私钥".
公钥和私钥是配对的. 最⼤的缺点就是运算速度⾮常慢,⽐对称加密要慢很多.
通过公钥对明⽂加密, 变成密⽂
通过私钥对密⽂解密, 变成明⽂
也可以反着⽤
通过私钥对明⽂加密, 变成密⽂
通过公钥对密⽂解密, 变成明⽂
数据摘要&&数据指纹
数字指纹(数据摘要),其基本原理是利⽤单向散列函数(Hash函数)对信息进⾏运算,⽣成⼀串固定⻓度的数字摘要。数字指纹并不是⼀种加密机制,但可以⽤来判断数据有没有被窜改。
摘要常⻅算法:有 MD5 、SHA1、SHA256、SHA512等,算法把⽆限的映射成有限,因此可能会有碰撞(两个不同的信息,算出的摘要相同,但是概率⾮常低)
摘要特征:和 加密算法 的区别是,摘要严格意义不是加密,因为没有解密,只不过从摘要很难反推原信息,通常⽤来进⾏数据对⽐

数据签名

摘要经过加密,就得到数字签名,签名的形成是基于⾮对称加密算法的。

混合加密技术:

混合加密技术:结合对称加密与非对称加密
服务端生成私钥,再通过私钥生成公钥,然后将公钥放在证书中颁发给客户端
使用公钥和私钥以非对称方式加密生成密钥
客户端接下来的传输数据中,都会用密钥以对称方式对信息加密,再传输给服务端 

引入证书 

CA认证
服务端在使⽤HTTPS前,需要向CA机构申领⼀份数字证书,数字证书⾥含有证书申请者信息、公钥信息等。服务器把证书传输给浏览器,浏览器从证书⾥获取公钥就⾏了,证书就如⾝份证,证明服务端公钥的权威性

 

3.UDP协议

UDP协议端格式

16 UDP 长度 , 表示整个数据报 (UDP 首部 +UDP 数据 ) 的最大长度 ;
如果校验和出错 , 就会直接丢弃 ;

UDP的特点

无连接 : 知道对端的 IP 和端口号就直接进行传输 , 不需要建立连接 ;
不可靠 : 没有确认机制 , 没有重传机制 ; 如果因为网络故障该段无法发到对方 , UDP 协议层也不会给应用层返回任何错误信息;
面向数据报 : 不能够灵活的控制读写数据的次数和数量

面向数据报

应用层交给 UDP 多长的报文 , UDP 原样发送 , 既不会拆分 , 也不会合并 ;
UDP 传输 100 个字节的数据
如果发送端调用一次 sendto, 发送 100 个字节 , 那么接收端也必须调用对应的一次 recvfrom, 接收 100 个字节; 而不能循环调用 10 recvfrom, 每次接收 10 个字节

UDP的缓冲区

UDP 没有真正意义上的 发送缓冲区 . 调用 sendto 会直接交给内核 , 由内核将数据传给网络层协议进行后续的传输动作;
UDP 具有接收缓冲区 . 但是这个接收缓冲区不能保证收到的 UDP 报的顺序和发送 UDP 报的顺序一致 ; 如果缓冲区满了, 再到达的 UDP 数据就会被丢弃

UDPsocket既能读, 也能写, 这个概念叫做 全双工。 

UDP使用注意事项

我们注意到 , UDP 协议首部中有一个 16 位的最大长度 . 也就是说一个 UDP 能传输的数据最大长度是 64K( 包含 UDP 首部).
然而 64K 在当今的互联网环境下 , 是一个非常小的数字 .
如果我们需要传输的数据超过 64K, 就需要在应用层手动的分包 , 多次发送 , 并在接收端手动拼装 ;

基于UDP的应用层协议

NFS: 网络文件系统
TFTP: 简单文件传输协议
DHCP: 动态主机配置协议
BOOTP: 启动协议 ( 用于无盘设备启动 )
DNS: 域名解析协议

当然, 也包括你自己写UDP程序时自定义的应用层协议; 

4.TCP协议

传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流传输层通信协议 。

TCP协议段格式

/ 目的端口号 : 表示数据是从哪个进程来 , 到哪个进程去 ;
4 TCP 报头长度 : 表示该 TCP 头部有多少个 32 bit( 有多少个 4 字节 ); 所以 TCP头部最大长度是15 * 4 = 60
6 位标志位 :
        URG: 紧急指针是否有效
        ACK: 确认号是否有效
        PSH: 提示接收端应用程序立刻从 TCP 缓冲区把数据读走
        RST: 对方要求重新建立连接 ; 我们把携带 RST 标识的称为 复位报文段
        SYN: 请求建立连接 ; 我们把携带 SYN 标识的称为 同步报文段
        FIN: 通知对方 , 本端要关闭了 , 我们称携带 FIN 标识的为 结束报文段
16 位校验和 : 发送端填充 , CRC 校验 . 接收端校验不通过 , 则认为数据有问题 . 此处的检验和不光包含 TCP 首部 , 也包含TCP 数据部分 .
16 位紧急指针 : 标识哪部分数据是紧急数据 ;
40 字节头部选项 : 暂时忽略 ;

确认应答机制(ACK)

TCP 将每个字节的数据都进行了编号 . 即为序列号 .

每一个ACK都带有对应的确认序列号, 意思是告诉发送者, 我已经收到了哪些数据; 下一次你从哪里开始发.

超时重传机制

主机 A 发送数据给 B 之后 , 可能因为网络拥堵等原因 , 数据无法到达主机 B;
如果主机 A 在一个特定时间间隔内没有收到 B 发来的确认应答 , 就会进行重发

但是, 主机A未收到B发来的确认应答, 也可能是因为ACK丢失了

因此主机 B 会收到很多重复数据 . 那么 TCP 协议需要能够识别出那些包是重复的包 , 并且把重复的丢弃掉 .这时候我们可以利用前面提到的序列号, 就可以很容易做到去重的效果 .
那么 , 如果超时的时间如何确定 ?

最理想的情况下, 找到一个最小的时间, 保证 "确认应答一定能在这个时间内返回".

但是这个时间的长短 , 随着网络环境的不同 , 是有差异的 .
如果超时时间设的太长 , 会影响整体的重传效率 ;
如果超时时间设的太短 , 有可能会频繁发送重复的包 ;

TCP为了保证无论在任何环境下都能比较高性能的通信, 因此会动态计算这个最大超时时间。

Linux (BSD Unix Windows 也是如此 ), 超时以 500ms 为一个单位进行控制 , 每次判定超时重发的超时时间都是500ms 的整数倍 .
如果重发一次之后 , 仍然得不到应答 , 等待 2*500ms 后再进行重传 .
如果仍然得不到应答 , 等待 4*500ms 进行重传 . 依次类推 , 以指数形式递增 .
累计到一定的重传次数 , TCP 认为网络或者对端主机出现异常 , 强制关闭连接

连接管理机制

在正常情况下, TCP要经过三次握手建立连接, 四次挥手断开连接。

服务端状态转化

[CLOSED -> LISTEN] 服务器端调用 listen 后进入 LISTEN 状态 , 等待客户端连接 ;
[LISTEN -> SYN_RCVD] 一旦监听到连接请求 ( 同步报文段 ), 就将该连接放入内核等待队列中 , 并向客户端发送SYN 确认报文 .
[SYN_RCVD -> ESTABLISHED] 服务端一旦收到客户端的确认报文 , 就进入 ESTABLISHED 状态 , 可以进行读写数据了.
[ESTABLISHED -> CLOSE_WAIT] 当客户端主动关闭连接 ( 调用 close), 服务器会收到结束报文段 , 服务器返回确认报文段并进入CLOSE_WAIT;
[CLOSE_WAIT -> LAST_ACK] 进入 CLOSE_WAIT 后说明服务器准备关闭连接 ( 需要处理完之前的数据 ); 当服务器真正调用close 关闭连接时 , 会向客户端发送 FIN, 此时服务器进入 LAST_ACK 状态 , 等待最后一个ACK到来 ( 这个 ACK 是客户端确认收到了 FIN)
[LAST_ACK -> CLOSED] 服务器收到了对 FIN ACK, 彻底关闭连接

客户端状态转化

[CLOSED -> SYN_SENT] 客户端调用 connect, 发送同步报文段 ;
[SYN_SENT -> ESTABLISHED] connect 调用成功 , 则进入 ESTABLISHED 状态 , 开始读写数据 ;
[ESTABLISHED -> FIN_WAIT_1] 客户端主动调用 close , 向服务器发送结束报文段 , 同时进入FIN_WAIT_1;
[FIN_WAIT_1 -> FIN_WAIT_2] 客户端收到服务器对结束报文段的确认 , 则进入 FIN_WAIT_2, 开始等待服务器的结束报文段;
[FIN_WAIT_2 -> TIME_WAIT] 客户端收到服务器发来的结束报文段 , 进入 TIME_WAIT, 并发出 LAST_ACK;
[TIME_WAIT -> CLOSED] 客户端要等待一个 2MSL(Max Segment Life, 报文最大生存时间 ) 的时间 , 才会进入CLOSED 状态 .

滑动窗口

刚才我们讨论了确认应答策略 , 对每一个发送的数据段 , 都要给一个 ACK 确认应答 . 收到 ACK 后再发送下一个数据段 . 这样做有一个比较大的缺点, 就是性能较差 . 尤其是数据往返的时间较长的时候 .

既然这样一发一收的方式性能较低 , 那么我们一次发送多条数据 , 就可以大大的提高性能 ( 其实是将多个段的等待时间重叠在一起了).

 

窗口大小指的是无需等待确认应答而可以继续发送数据的最大值 . 上图的窗口大小就是 4000 个字节 ( 四个段).
发送前四个段的时候 , 不需要等待任何 ACK, 直接发送 ;
收到第一个 ACK , 滑动窗口向后移动 , 继续发送第五个段的数据 ; 依次类推 ;
操作系统内核为了维护这个滑动窗口 , 需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答 ; 只有确认应答过的数据, 才能从缓冲区删掉 ;
窗口越大 , 则网络的吞吐率就越高 ;

那么如果出现了丢包 , 如何进行重传 ? 这里分两种情况讨论 .
情况一 : 数据包已经抵达 , ACK 被丢了

这种情况下, 部分ACK丢了并不要紧, 因为可以通过后续的ACK进行确认;

情况二: 数据包就直接丢了

当某一段报文段丢失之后 , 发送端会一直收到 1001 这样的 ACK, 就像是在提醒发送端 " 我想要的是 1001"一样;
如果发送端主机连续三次收到了同样一个 "1001" 这样的应答 , 就会将对应的数据 1001 - 2000 重新发送 ;
这个时候接收端收到了 1001 之后 , 再次返回的 ACK 就是 7001 ( 因为 2001 - 7000) 接收端其实之前就已经收到了, 被放到了接收端操作系统内核的 接收缓冲区 ; 

这种机制被称为 "高速重发控制"(也叫 "快重传")

流量控制

接收端处理数据的速度是有限的 . 如果发送端发的太快 , 导致接收端的缓冲区被打满 , 这个时候如果发送端继续发送 ,就会造成丢包, 继而引起丢包重传等等一系列连锁反应 . 因此TCP 支持根据接收端的处理能力 , 来决定发送端的发送速度 . 这个机制就叫做 流量控制 (Flow Control) ;
接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 " 窗口大小 " 字段 , 通过 ACK 端通知发送端 ;
窗口大小字段越大 , 说明网络的吞吐量越高 ;
接收端一旦发现自己的缓冲区快满了 , 就会将窗口大小设置成一个更小的值通知给发送端 ;
发送端接受到这个窗口之后 , 就会减慢自己的发送速度 ;
如果接收端缓冲区满了 , 就会将窗口置为 0; 这时发送方不再发送数据 , 但是需要定期发送一个窗口探测数据段, 使接收端把窗口大小告诉发送端

拥塞控制

虽然 TCP 有了滑动窗口这个大杀器 , 能够高效可靠的发送大量的数据 . 但是如果在刚开始阶段就发送大量的数据 , 仍然可能引发问题.
因为网络上有很多的计算机 , 可能当前的网络状态就已经比较拥堵 . 在不清楚当前网络状态下 , 贸然发送大量的数据 ,是很有可能引起雪上加霜的.
TCP 引入 慢启动 机制 , 先发少量的数据 , 探探路 , 摸清当前的网络拥堵状态 , 再决定按照多大的速度传输数据 ;

 

此处引入一个概念程为 拥塞窗口
发送开始的时候 , 定义拥塞窗口大小为 1;
每次收到一个 ACK 应答 , 拥塞窗口加 1;
每次发送数据包的时候 , 将拥塞窗口和接收端主机反馈的窗口大小做比较 , 取较小的值作为实际发送的窗口;
像上面这样的拥塞窗口增长速度 , 是指数级别的 . " 慢启动 " 只是指初使时慢 , 但是增长速度非常快 .
为了不增长的那么快 , 因此不能使拥塞窗口单纯的加倍 .
此处引入一个叫做慢启动的阈值
当拥塞窗口超过这个阈值的时候 , 不再按照指数方式增长 , 而是按照线性方式增长

TCP 开始启动的时候 , 慢启动阈值等于窗口最大值 ;
在每次超时重发的时候 , 慢启动阈值会变成原来的一半 , 同时拥塞窗口置回 1
少量的丢包 , 我们仅仅是触发超时重传 ; 大量的丢包 , 我们就认为网络拥塞 ;
TCP 通信开始后 , 网络吞吐量会逐渐上升 ; 随着网络发生拥堵 , 吞吐量会立刻下降 ;
拥塞控制 , 归根结底是 TCP 协议想尽可能快的把数据传输给对方 , 但是又要避免给网络造成太大压力的折中方案 .
TCP 拥塞控制这样的过程 , 就好像 热恋的感觉
延迟应答
如果接收数据的主机立刻返回 ACK 应答 , 这时候返回的窗口可能比较小 .