1:各层协议
1-1:OSI 与 TCP/IP 各层结构和功能,以及相应的协议
功能:
- 应用层:针对特定应用的协议,为应用程序提供服务并规定应用程序中通信相关的细节。包括文件传输、电子邮件、远程登录等协议。
- 表示层:将来自下一层的数据转换为上层能够处理的格式。负责数据转换、格式化、文本压缩等。
- 会话层:负责建立和断开通信连接(数据流动的逻辑通路),以及数据的分割等数据传输相关的管理。
- 传输层:管理两个节点之间的数据传输。
- 网络层:地址管理和路由选择。
- 数据链路层:互联设备之间传送和识别帧。
- 物理层:以二进制形式在在物理媒体上传输数据。
协议:
(1)应用层:TELNET、FTP、TFTP、SMTP、SNMP、HTTP、BOOTP、DHCP、DNS、SSH
(2)表示层:文本:ASCII,EBCDIC;图形:TIFF,JPEG,GIF,PICT;声音:MIDI,MPEG,QUICKTIME
(3)会话层:NFS、SQL、RPC 、X-WINDOWS、ASP(APPTALK 会话协议)、SCP
(4)传输层:TCP、UDP、SPX
(5)网络层:IP、IPX、ICMP、RIP、OSPF(Open Shortest Path First 开放式最短路径优先)
(6)数据链路层:SDLC、HDLC、PPP、STP(Spanning Tree Protocol)、帧中继、ARP(可放在链路层,也可放在网络层)、RARP
(7)物理层:EIA/TIA RS-232、EIA/TIA RS-449、V.35、RJ-45
1-2:网络层与数据链路层有什么关系呢?
两台主机之间的数据传输,总是在⼀段⼀段的链路上传送的。在两个相邻节点之间传送数据时,数据链路层将⽹络层交下来的 IP 数据报组装成帧,在两个相邻节点间的链路上传送帧。每⼀帧包括数据和必要的控制信息(如同步信息,地址信息,差错控制等)。
在接收数据时,控制信息使接收端能够知道⼀个帧从哪个⽐特开始和到哪个⽐特结束。这样,数据链路层在收到⼀个帧后,就可从中提出数据部分,上交给⽹络层。
1-3:网络层的路由算法
静态路由算法:由网络管理员手工配置路由信息。
距离-向量路由算法(RIP):在距离-向量路由算法中,所有结点都定期地将它们的整个路由选择表传送给所有与之直接相邻的结点。这种路由选择表包含:
- 每条路径的目的地(另一结点)。
- 路径的代价(也称距离)。
在这种算法中,所有结点都必须参与距离向量交换,以保证路由的有效性和一致性,也就是说,所有的结点都监听从其他结点传来的路由选择更新信息,并在下列情况下更新它们的路由选择表:
- 被通告一条新的路由,该路由在本结点的路由表中不存在,此时本地系统加入这条新的路由。
- 发来的路由信息中有一条到达某个目的地的路由,该路由与当前使用的路由相比,有较短的距离(较小的代价)。此种情况下,就用经过发送路由信息的结点的新路由替换路由表中到达那个目的地的现有路由。
链路状态路由算法:链路状态路由算法要求每个参与该算法的结点都具有完全的网络拓扑信息,它们执行下述两项任务:
- 主动测试所有邻接结点的状态。两个共享一条链接的结点是相邻结点,它们连接到同一条链路,或者连接到同一广播型物理网络。
- 定期地将链路状态传播给所有其他结点。
在一个链路状态路由选择中,一个结点检查所有直接链路的状态,并将所得的状态信息发送给网上的所有其他结点,而不是仅送给那些直接相连的结点。每个结点都用这种方式从网上所有其他的结点接收包含直接链路状态的路由选择信息。
每当链路状态报文到达时,路由结点便使用这些状态信息去更新自己的网络拓扑和状态“视野图”,一旦链路状态发生变化,结点就对更新的网络图利用 Dijsktra 最短路径算法重新计算路由,从单一的源出发计算到达所有目的结点的最短路径。层次路由: 当网络规模扩大时,路由器的路由表成比例地增大。这不仅会消耗越来越多的路由器缓冲区空间,而且需要用更多 CPU 时间来扫描路由表,用更多的带宽来交换路由状态信息。因此路由选择必须按照层次的方式进行。
因特网将整个互联网划分为许多较小的自治系统(注意一个自治系统中包含很多局域网),每个自治系统有权自主地决定本系统内应采用何种路由选择协议。如果两个自治系统需要通信,那么就需要一种在两个自治系统之间的协议来屏蔽这些差异。据此,因特网把路由选择协议划分为两大类 - 一个自治系统内部所使用的路由选择协议称为内部网关协议(IGP), 也称域内路由选择,具体的协议有 RIP 和 OSPF 等。 - 自治系统之间所使用的路由选择协议称为外部网关协议(EGP), 也称域间路由选择,用在不同自治系统的路由器之间交换路由信息,并负责为分组在不同自治系统之间选择最优的路径。具体的协议有 BGP 。使用层次路由时, OSPF 将一个自治系统再划分为若干区域(Area), 每个路由器都知道在本区域内如何把分组路由到目的地的细节,但不用知道其他区域的内部结构。
2:报文结构与状态码
2-1:状态码
分类 | 分类描述 |
---|---|
1** | 信息,服务器收到请求,需要请求者继续执行操作 |
2** | 成功,操作被成功接收并处理 |
3** | 重定向,需要进一步的操作以完成请求 |
4** | 客户端错误,请求包含语法错误或无法完成请求 |
5** | 服务器错误,服务器在处理请求的过程中发生了错误 |
2-2:HTTP 请求组成
一个HTTP请求报文由请求行、请求头部、空行、请求数据四个部分组成。
如图所示
请求示例如下:
1 | GET /hello.txt HTTP/1.1 |
2-3:HTTP响应组成
HTTP响应报文也由响应行、响应头、响应体三部分组成。
如图所示
响应示例如下:
1 | HTTP/1.1 200 OK |
3:计算机网络-HTTP-HTTP 的 1.0-3.0
3-1:HTTP-1.0
1.0的HTTP版本,是一种无状态,无连接的应用层协议。HTTP1.0规定浏览器和服务器保持短暂的链接。
浏览器每次请求都需要与服务器建立一个TCP连接,服务器处理完成以后立即断开TCP连接(无连接),服务器不跟踪每个客户单,也不记录过去的请求(无状态)。
这种无状态性可以借助Cookie/Session机制来做身份认证和状态记录。
3-1-1:HTTP1.0存在的问题- 无法复用连接:每次发送请求,都需要进行一次TCP连接,而TCP的连接释放过程又是比较费事的。这种无连接的特性会使得网络的利用率变低
- 队头阻塞(head of line blocking):由于HTTP1.0规定下一个请求必须在前一个请求响应到达之后才能发送,假设前一个请求响应一直不到达,那么下一个请求就不发送,后面的请求就阻塞了。
3-1-2:什么是无连接和无状态
无连接:
- 每一个访问都是无连接,服务器挨个处理访问队列里的访问,处理完一个就关闭连接,这事儿就完了,然后处理下一个新的。
- 无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。
无状态:
- 协议对于事务处理没有记忆能力。
- 对同一个url请求没有上下文关系。
- 每次的请求都是独立的,它的执行情况和结果与前面的请求和之后的请求是无直接关系的,它不会受前面的请求应答情况直接影响,也不会直接影响后面的请求应答情况。
- 服务器中没有保存客户端的状态,客户端必须每次带上自己的状态去请求服务器。
3-2:HTTP-1.1
3-2-1:HTTP/1.1 相对于 HTTP1.0 改善
长连接:HTTP1.1增加Connection字段,通过设置Keep-Alive保持HTTP连接不断开。避免每次客户端与服务器请求都要重复建立释放建立TCP连接,提高了网络的利用率。
如果客户端想关闭HTTP连接,可以在请求头中携带Connection:false来告知服务器关闭请求。
管道化:HTTP1.1支持请求管道化(pipelining)。基于HTTP1.1的长连接,使得请求管线化成为可能。管线化使得请求能够“并行”传输。(这里的“并行”并不是真正意义上的并行传输)
需要注意的是:服务器必须按照客户端请求的先后顺序依次回送相应的结果,以保证客户端能够区分出每次请求的响应内容。
也就是说,HTTP管道化可以让我们把先进先出队列从客户端(请求队列)迁移到服务端(响应队列)
可见,HTTP1.1还是无法解决队头阻塞的问题。同时“管道化”技术存在各种各样的问题,所以很多浏览器要么根本不支持它,要么直接默认关闭,并且开启的条件很苛刻……而且好像实际也没有什么用处。
此外,HTTP1.1还加入了缓存处理(强缓存和协商缓存)新的字段如cache-control,支持断点传输,以及增加了Host字段(使得一个服务器能够用来创建多个Web站点)。
3-2-2:HTTP1.1 缺点
如上一节所说,HTTP1.1虽然进行了一定的改善,但是还是存在“队头阻塞”的问题
3-3:HTTP2.0
3-3-1:HTTP2.0 做了什么优化
二进制分帧:
HTTP2.0
通过在应用层和传输层之间增加一个二进制分帧层,突破了HTTP1.1
的性能限制、改进传输性能。多路复用(连接共享):
- 流(stream) :已建立连接上的双向字节流。
- 消息 :与逻辑消息对应的完整的一系列数据帧。
- 帧(frame) :HTTP2.0通信的最小单位,每个帧包含头部,至少也会标识出当前所属的流(stream_id)
所有HTTP2.0通信都在一个TCP链接上完成,这个链接可以承载任意流量的双向数据流。每个数据流以消息的形式发送,而消息由一或多个帧组成。这些帧可以乱序发送,然后再根据每个帧头部的流标识符(Stream_id)重新封装。
多路复用(连接共享)可能会导致关键字被阻塞,HTTP2.0里每个数据流都可以设置优先级和依赖,优先级高的数据流会被服务器优先处理和返回客户端,数据流还可以依赖其他的子数据流。
可见,HTTP2.0实现了真正的并行传输,它能够在一个TCP上进行任意数量的HTTP请求。而这个强大的功能基于“二级制分帧”的特性。头部压缩:在
HTTP1.x
中,头部元数据都是以纯文本的形式发送的,通常会给每个请求增加500~800字节的负荷。比如说Cookie,默认情况下,浏览器会在每次请求的时候,把Cookie附在header上面发送给服务器。(由于Cookie比较大且每次都重复发送,一般不存储信息,只是用来做状态记录和身份认证)。HTTP2.0
使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields
表,既避免了重复header的传输,又减小了需要传输的大小。高效的压缩算法可以很大的压缩header,减少发送包的数量从而降低延迟。服务器推送:服务器除了最初请求的响应外,服务器还可以额外向客户端推送资源,而无需客户端明确的需求。
3-3-2:HTTP2.0 有哪些缺陷
HTTP2.0主要的问题在于:多个 HTTP 请求在复用一个 TCP 连接,下层的 TCP 协议是不知道有多少个HTTP 请求的。所以一旦发生了丢包现象,就会触发 TCP 的重传机制,这样在一个 TCP 连接中的所有的HTTP 请求都必须等待这个丢了的包被重传回来 。
- HTTP1.1 中的管道( pipeline)传输中如果有一个请求阻塞了,那么队列后请求也统统被阻塞住了。
- HTTP2.0 多个请求复用一个TCP连接,一旦发生丢包,就会阻塞住所有的 HTTP 请求。
3-4:HTTP3.0
HTTP3.0,也称作HTTP over QUIC。HTTP3.0的核心是QUIC(读音quick)协议,由Google在 2015年提出的SPDY v3演化而来的新协议,传统的HTTP协议是基于传输层TCP的协议,而QUIC是基于传输层UDP上的协议,可以定义成:HTTP3.0基于UDP的安全可靠的HTTP2.0协议
3-4-1:HTTP3.0 做了哪些优化
- 减少了TCP三次握手及TLS握手时间:不管是HTTP1.0/1.1还是
HTTPS
,HTTP2.0
,都使用了TCP进行传输。HTTPS
和HTTP2
还需要使用TLS协议来进行安全传输。这就出现了两个握手延迟,而基于UDP协议的QUIC,因为UDP本身没有连接的概念,连接建立时只需要一次交互,半个握手的时间。 - 多路复用丢包的线头阻塞问题:QUIC保留了HTTP2.0多路复用的特性,在之前的多路复用过程中,同一个TCP连接上有多个stream,假如其中一个stream丢包,在重传前后的stream都会受到影响,而QUIC中一个连接上的多个stream之间没有依赖。所以当发生丢包时,只会影响当前的stream,也就避免了线头阻塞问题。
- 优化重传策略:以往的TCP丢包重传策略是:在发送端为每一个封包标记一个编号(sequence number),接收端在收到封包时,就会回传一个带有对应编号的ACK封包给发送端,告知发送端封包已经确实收到。当发送端在超过一定时间之后还没有收到回传的ACK,就会认为封包已经丢失,启动重新传送的机制,复用与原来相同的编号重新发送一次封包,确保在接收端这边没有任何封包漏接。
这样的机制就会带来一些问题,假设发送端总共对同一个封包发送了两次(初始+重传),使用的都是同一个sequence number:编号N。之后发送端在拿到编号N封包的回传ACK时,将无法判断这个带有编号N的ACK,是接收端在收到初始封包后回传的ACK。这就会加大后续的重传计算的耗时。 - QUIC为了避免这个问题,发送端在传送封包时,初始与重传的每一个封包都改用一个新的编号,unique packet number,每一个编号都唯一而且严格递增,这样每次在收到ACK时,就可以依据编号明确的判断这个ACK是来自初始封包或者是重传封包。
- 流量控制:通过流量控制可以限制客户端传输资料量的大小,有了流量控制后,接收端就可以只保留相对应大小的接收 buffer ,优化记忆体被占用的空间。但是如果存在一个流量极慢的stream ,光一个stream就有可能占用掉接收端所有的资源。QUIC为了避免这个潜在的HOL Blocking,采用了连线层(connection flow control)和Stream层的(streamflow control)流量控制,限制单一Stream可以占用的最大buffer size。
- 连接迁移:TCP连接基于四元组(源IP、源端口、目的IP、目的端口),切换网络时至少会有一个因素发生变化,导致连接发生变化。当连接发生变化时,如果还使用原来的TCP连接,则会导致连接失败,就得等原来的连接超时后重新建立连接,所以我们有时候发现切换到一个新网络时,即使新网络状况良好,但内容还是需要加载很久。
如果实现得好,当检测到网络变化时立刻建立新的TCP连接,即使这样,建立新的连接还是需要几百毫秒的时间。QUIC的连接不受四元组的影响,当这四个元素发生变化时,原连接依然维持。QUIC连接不以四元组作为标识,而是使用一个64位的随机数,这个随机数被称为Connection lD,对应每个stream,即使IP或者端口发生变化,只要Connection ID没有变化,那么连接依然可以维持。
3-5:一个TCP 连接是否会在一个HTTP 请求完成后断开?什么情况下会断开?
HTTP1.0会断开,而在HTTP1.1之后不会。
在请求头中携带Connection:false后会断开连接。
4:计算机网络-HTTP 与 HTTPS 前世今生
4-1:什么是 HTTPS
简单来说,HTTPS 协议 =”SSL/TSL+HTTP 协议” 构建的可进行加密传输、身份认证的网络协议,是 HTTP 的安全版。
4-2:HTTPS 解决了 HTTP 的哪些问题?(为什么要 HTTPS)
HTTP(超文本传输协议)被用于在 Web 浏览器和网站服务器之间,以明文方式传递信息,不提供任何方式的数据加密,因此使用 HTTP 协议传输隐私信息(如:银行卡号、密码等支付信息)非常不安全。
为了解决这一安全缺陷,网景公司设计了 SSL(Secure Sockets Layer)协议,在 HTTP 的基础上加入了 SSL(Secure Sockets Layer)协议,SSL 依靠 SSL 证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。从而诞生了 HTTPS(安全套接字层超文本传输协议)。
4-2-1:加密算法
- 加密算法:发送方进行加密的算法。
- 解密算法:接收方进行解密的算法。
- 密钥 (key):只有发送方和接收方理解的消息
- 对称密钥加密 (Symmetric Key Cryptography):加密与解密使用相同密钥。
- 非对称密钥加密 (Asymmetric Key Cryptography):加密与解密使用不同密钥。
4-2-1-1:HTTPS 对称加解密的过程
4-2-1-2:HTTPS 非对称加密过程
4-2-2:数字证书认证机构的流程
- 首先,服务器的运营人员向数字认证机构提出公开密钥的申请。数字证书认证机构在判明提出申请者的身份之后,会对已申请的公开密钥做数字签名。
- 然后分配这个已签名的公开密钥,并将该公开密钥放入公钥证书后绑定在一起。
- 服务器会将这份由数字证书认证机构颁发的公钥证书发送给客户端,用来进行公开密钥加密方式通信。公钥证书也可叫做数字证书或直接称为证书。
- 客户端在接收到证书后,使用数字认证机构的公开密钥,对那张证书上的数字签名进行验证,一旦验证通过,客户端便可明确两件事:一,认证服务器的公开密钥是真实有效的数字证书认证机构。二,服务器的公开密钥是值得信赖的。
- 认证机关的公开密钥必须安全的转交给客户端。使用通信方式时,如何安全转交是一件很困难的事,因此,多数浏览器开发商发布版本时,会事先在内部植入常用认证机关的公开密钥。
4-3:HTTP 与 HTTPS 建立请求过程
4-3-1:HTTP 请求过程
客户端的浏览器首先要通过网络与服务器建立连接,该连接是通过 TCP 来完成的,一般 TCP 连接的端口号是80。 建立连接后,客户机发送一个请求给服务器,请求方式的格式为:
服务器接到请求后,给予相应的响应信息,其格式为:
4-3-2:HTTPS 请求过程(加密过程)
- TCP 三次同步握手
- 客户端验证服务器数字证书
- DH 算法协商对称加密算法的密钥、hash 算法的密钥
- SSL 安全加密隧道协商完成
- 网页以加密的方式传输,用协商的对称加密算法和密钥加密,保证数据机密性;用协商的hash算法进行数据完整性保护,保证数据不被篡改。
4-3-3:HTTP 与 HTTPS 区别
- 连接端口 :HTTP 标准端口是 80,而 HTTPS 的标准端口是 443。
- 传输方式 :HTTP 是超文本传输协议,信息是明文传输,而 HTTPS 是 SSL 加密传输协议。
- 工作耗时 :HTTP 耗时 = TCP 握手,而 HTTPS 耗时 = TCP 握手 + SSL 握手。
- 显示形式 :HTTP 的 URL 以
http://
开头,而 HTTPS 的 URL 以https://
开头。 - 费用 :HTTP 无需费用,而 HTTPS 需要到 CA 申请证书。
- 安全性 :HTTP 的连接很简单,是无状态的;HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 HTTP 协议安全。
4-3-4:为何不所有的网站都使用 HTTPS
技术方面
- 相同网络环境下,HTTPS协议会使页面的加载时间延长近50%,增加10%到20%的耗电。此外,HTTPS协议还会影响缓存,增加数据开销和功耗。
- HTTPS协议的安全是有范围的,在黑客攻击、拒绝服务攻击、服务器劫持等方面几乎起不到什么作用。
- 最关键的,SSL 证书的信用链体系并不安全。特别是在某些国家可以控制 CA 根证书的情况下,中间人攻击一样可行。
成本方面
- SSL的专业证书需要购买,功能越强大的证书费用越高。个人网站、小网站可以选择入门级免费证书。
- SSL 证书通常需要绑定 固定IP,为服务器增加固定IP会增加一定费用;
- HTTPS 连接服务器端资源占用高较高多,相同负载下会增加带宽和服务器投入成本;
5:各种协议的端口号
TCP:
TCP | 协议名称 | 端口号 | 套接字 | 作用 |
---|---|---|---|---|
Telnet | 专司终端模拟 | 23 | Tcp 23 | 它为用户提供了在本地计算机上完成远程主机工作的能力 |
SMTP | 简单邮件传输协议 | 25 | Tcp 25 | 它帮助每台计算机在发送或中转信件时找到下一个目的地 |
HTTP | 超文本传输协议 | 80 | Tcp 80 | 超文本传输协议,是我们浏览网页、看在线视频、听在线音乐等必须遵循的规则 |
FTP | 文件传输协议 | 20、21 | Tcp 20和Tcp21 | 文件传输协议 FTP [ File Transfer Protocol ]使得主机间可以共享文件 |
DNS | 域名系统 | 53 | Tcp 53 | 因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串 |
HTTPS | 超文本传输安全协议 | 443 | Tcp 443 | HTTPS是以安全为目标的HTTP通道,简单讲是HTTP的安全版 |
SSH | 安全壳协议 22 | Tcp 22 | SSH | 为建立在应用层和传输层基础上的安全协议 |
POP3 | 邮局协议版本 | 3 | 110 | Tcp 110 |
NTP | 网络时间协议 | 123 | Tcp 123 | 它是用来同步网络中各个计算机时间的协议 |
IMAP4 | 第四版因特网信息存取协议 | 143 | Tcp 143 | IMAP4协议与POP3协议一样也是规定个人计算机如何访问互联网上的邮件服务器进行收发邮件的协议,但是IMAP4协议同POP3协议相比更高级 |
UDP:
UDP | 协议名称 | 端口号 | 套接字 | 作用 |
---|---|---|---|---|
SNMP | 简单网络管理协议 | 161 | UDP 161 | 该协议能够支持网络管理系统,用以监测连接到网络上的设备是否有任何引起管理上关注的情况 |
TFTP | 简单文件传输协议 | 69 | UDP 69 | TCP/IP协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议,提供不复杂、开销不大的文件传输服务 |
DNS | 域名系统 | 53 | UDP 53 | 因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串 |
BooTPS/DHCP | 动态主机配置协议 | 67 | UDP 67 | 主要有两个用途:给内部网络或网络服务供应商自动分配IP地址,给用户或者内部网络管理员作为对所有计算机作中央管理的手段 |
6:拆包粘包
6-1:什么是 TCP 粘包?
TCP粘包就是指发送方发送的若干包数据到达接收方时粘成了一包,从接收缓冲区来看,后一包数据的头紧接着前一包数据的尾,出现粘包的原因是多方面的,可能是来自发送方,也可能是来自接收方。
6-2:粘包的原因
发送方原因
TCP默认使用Nagle算法(主要作用:减少网络中报文段的数量),而Nagle算法主要做两件事:
(1) 只有上一个分组得到确认,才会发送下一个分组。
(2) 收集多个小分组,在一个确认到来时一起发送。Nagle算法造成了发送方可能会出现粘包问题。
接收方原因
TCP接收到数据包时,并不会马上交到应用层进行处理,或者说应用层并不会立即处理。实际上,TCP将接收到的数据包保存在接收缓存里,然后应用程序主动从缓存读取收到的分组。这样一来,如果TCP接收数据包到缓存的速度大于应用程序从缓存中读取数据包的速度,多个包就会被缓存,应用程序就有可能读取到多个首尾相接粘到一起的包。
6-3:粘包解决方案
发送方
对于发送方造成的粘包问题,可以通过关闭Nagle算法来解决,使用TCP_NODELAY选项来关闭算法。接收方
接收方没有办法来处理粘包现象,只能将问题交给应用层来处理。应用层
应用层的解决办法简单可行,不仅能解决接收方的粘包问题,还可以解决发送方的粘包问题。解决办法:循环处理。应用程序从接收缓存中读取分组时,读完一条数据,就应该循环读取下一条数据,直到所有数据都被处理完成,但是如何判断每条数据的长度呢?
(1) 格式化数据:每条数据有固定的格式(开始符,结束符),这种方法简单易行,但是选择开始符和结束符时一定要确保每条数据的内部不包含开始符和结束符。
(2) 发送长度:发送每条数据时,将数据的长度一并发送,例如规定数据的前4位是数据的长度,应用层在处理时可以根据长度来判断每个分组的开始和结束位置。
6-4:UDP 会不会产生粘包问题呢?
TCP为了保证可靠传输并减少额外的开销(每次发包都要验证),采用了基于流的传输,基于流的传输不认为消息是一条一条的,是无保护消息边界的(保护消息边界:指传输协议把数据当做一条独立的消息在网上传输,接收端一次只能接受一条独立的消息)。
UDP则是面向消息传输的,是有保护消息边界的,接收方一次只接受一条独立的信息,所以不存在粘包问题。
举个例子:有三个数据包,大小分别为2k、4k、6k,如果采用UDP发送的话,不管接受方的接收缓存有多大,我们必须要进行至少三次以上的发送才能把数据包发送完,但是使用TCP协议发送的话,我们只需要接受方的接收缓存有12k的大小,就可以一次把这3个数据包全部发送完毕。
7:计算机网络-HTTP-Cookie
7-1:Cookie 作用
Cookie 实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用 response 向客户端浏览器颁发一个 Cookie。客户端会把 Cookie 保存起来。
当浏览器再请求该网站时,浏览器把请求的网址连同该 Cookie 一同提交给服务器。服务器检查该 Cookie,以此来辨认用户状态。服务器还可以根据需要修改 Cookie 的内容。
7-2:会话 Cookie 和持久 Cookie
若不设置过期时间,则表示这个 Cookie 的生命期为浏览器会话期间,关闭浏览器窗口,Cookie 就消失。这种生命期为浏览器会话期的 Cookie 被称为会话 Cookie。
会话 Cookie 一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。
若设置了过期时间,浏览器就会把 Cookie 保存到硬盘上,关闭后再次打开浏览器,这些 Cookie 仍然有效直到超过设定的过期时间。存储在硬盘上的 Cookie 可以在浏览器的不同进程间共享。
这种称为持久 Cookie。
7-3:Cookie 如何防止攻击
在HTTP头部上配置“set-Cookie”,有两个属性可以防止XSS攻击
- httponly :这个属性可禁止JavaScript访问Cookie,故可以保护Cookie不被嵌入的恶意代码所获取。
- secure :这个属性告诉客户端浏览器仅当在https请求时发送Cookie
1 | response.setHeader("Set-Cookie", "Cookiename=httponlyTest;Path=/;Domain=domainvalue;Max-Age=seconds;HTTPOnly"); |
8:计算机网络-HTTP-Session
8-1:如何保存 Session
当程序需要为某个客户端的请求创建一个 Session时,服务器首先检查这个客户端的请求里是否已包含了 SessionId,如果已包含则说明以前已经为此客户端创建过 Session,服务器就按照 SessionId 把这个 Session 检索出来使用(检索不到,会新建一个)。如果客户端请求不包含 SessionId,则为此客户端创建一个 Session 并且生成一个与此 Session 相关联的 SessionId。
SessionId 的值是一个既不会重复,又不容易被找到规律以仿造的字符串,这个 SessionId 将被在本次响应中返回给客户端保存。
8-2:如何实现 Session 跟踪呢?
通过Cookie中的SessionId来跟踪Session
8-3:Cookie 被禁⽤怎么办
如果客户端禁用了 Cookie,通常有两种方法实现 Session 而不依赖 Cookie。
URL 重写:就是把 SessionId 直接附加在 URL 路径的后面。
表单隐藏字段:就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把 SessionId 传递回服务器。比如:
1
2
3
4<form name="testform" action="/xxx">
<input type="hidden" name="SessionId" value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">
<input type="text">
</form>
8-4-1:Cookie 被禁用了,Session 还能用么
可以
9:计算机网络-HTTP-Cookie 与 Session
- Cookie数据存放在客户的浏览器上,Session数据放在服务器上。
- Cookie不是很安全,别人可以分析存放在本地的Cookie并进行Cookie欺骗,考虑到安全应当使用Session。
- Session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用Cookie(Cookies不会占用服务器资源,是存在客服端内存或者一个Cookie的文本文件中)。
- 单个Cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个Cookie。
- 可以考虑将登陆信息等重要信息存放为Session,其他信息如果需要保留,可以放在Cookie中。
9-1:Cookie 和 Session 的区别
- 存储位置不同
- Cookie的数据信息存放在客户端浏览器上。
- Session的数据信息存放在服务器上。
- 存储容量不同
- 单个Cookie保存的数据<=4KB,一个站点最多保存20个Cookie。
- 对于Session来说并没有上限,但出于对服务器端的性能考虑,Session内不要存放过多的东西,并且设置Session删除机制。
- 存储方式不同
- Cookie中只能保管ASCII字符串,并需要通过编码方式存储为Unicode字符或者二进制数据。
- Session中能够存储任何类型的数据,包括且不限于string,integer,list,map等。
- 隐私策略不同
- Cookie对客户端是可见的,别有用心的人可以分析存放在本地的Cookie并进行Cookie欺骗,所以它是不安全的。
- Session存储在服务器上,对客户端是透明的,不存在敏感信息泄漏的风险。
- 服务器压力不同
- Cookie保管在客户端,不占用服务器资源。对于并发用户十分多的网站,Cookie是很好的选择。
- Session是保管在服务器端的,每个用户都会产生一个Session。假如并发访问的用户十分多,会产生十分多的Session,耗费大量的内存。
- 浏览器支持不同
- 假如客户端浏览器不支持Cookie:
- Cookie是需要客户端浏览器支持的,假如客户端禁用了Cookie,或者不支持Cookie,则会话跟踪会失效。关于WAP上的应用,常规的Cookie就派不上用场了。
- 运用Session需要使用URL地址重写的方式。一切用到Session程序的URL都要进行URL地址重写,否则Session会话跟踪还会失效。
- 假如客户端支持Cookie:
- Cookie既能够设为本浏览器窗口以及子窗口内有效,也能够设为一切窗口内有效。
- Session只能在本窗口以及子窗口内有效。
- 假如客户端浏览器不支持Cookie:
10:计算机网络-HTTP-token
Token 是在服务端产生的。如果前端使用用户名/密码向服务端请求认证,服务端认证成功,那么服务端会返回 Token 给前端。前端可以在每次请求的时候带上 Token 证明自己的合法地位。如果这个 Token 在服务端持久化(比如存入数据库),那它就是一个永久的身份令牌。
10-1:token 的验证流程
- 用户通过用户名和密码发送请求。
- 程序验证。
- 程序返回一个签名的token 给客户端。
- 客户端储存token,并且每次用于每次发送请求。
- 服务端验证token并返回数据。
10-2:Token 和 Session 实现的区别
- Session 是一种记录服务器和客户端会话状态的机制,使服务端有状态化,可以记录会话信息。而 Token 是令牌,访问资源接口(API)时所需要的资源凭证。Token 使服务端无状态化,不会存储会话信息。
- Session 和 Token 并不矛盾,作为身份认证 Token 安全性比 Session 好,因为每一个请求都有签名还能防止监听以及重放攻击,而 Session 就必须依赖链路层来保障通讯安全了。如果你需要实现有状态的会话,仍然可以增加 Session 来在服务器端保存一些状态。
- 所谓 Session 认证只是简单的把 User 信息存储到 Session 里,因为 SessionID 的不可预测性,暂且认为是安全的。而 Token ,如果指的是 OAuth Token 或类似的机制的话,提供的是 认证 和 授权 ,认证是针对用户,授权是针对 App 。其目的是让某 App 有权利访问某用户的信息。这里的 Token 是唯一的。不可以转移到其它 App上,也不可以转到其它用户上。Session 只提供一种简单的认证,即只要有此 SessionID ,即认为有此 User 的全部权利。是需要严格保密的,这个数据应该只保存在站方,不应该共享给其它网站或者第三方 App。所以简单来说:如果你的用户数据可能需要和第三方共享,或者允许第三方调用 API 接口,用 Token 。如果永远只是自己的网站,自己的 App,用什么就无所谓了。
11:计算机网络-HTTP-url/uri
- 统一资源标识符(Uniform Resource Identifier,URI) 是一个用于标识某一互联网资源名称的字符串。
- 统一资源定位系统(uniform resource locator,URL)是因特网的万维网服务程序上用于指定信息位置的表示方法。
11-1:URI 和 URL 的区别是什么?
- URI 标记了一个网络资源,仅此而已;URL 标记了一个 WWW 互联网资源(用地址标记),并给出了他的访问地址。
- URI 包括 URL 和 URN 两个类别,URL 是 URI 的子集,所以 URL 一定是 URI,而 URI 不一定是 URL。
- 让 URI 能成为 URL 的是那个 “访问机制”,“网络位置”。e.g.
http://
orhttps://
。
12:计算机网络-综合应用-输入网址
12-1:输入网址过程
客户端获取 URL - > DNS 解析 - > TCP 连接 - > 发送 HTTP 请求 - > 服务器处理请求 - > 返回报文 - > 浏览器解析渲染页面 - > TCP 断开连接。
详见->打开一个网站发生了什么?
12-2:为什么域名要分级设计
域名结构的层次化,有利于域名的分类和识别,在多层次、结构化的网络中,有利于实现域名的分级查询和管理。
13:TCP
13-1:什么是 TCP 连接?
TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,它完成传输层所指定的功能。
- 面向连接:一定是「一对一」才能连接,不能像 UDP 协议可以一个主机同时向多个主机发送消息,也就是一对多是无法做到的;
- 可靠的:无论的网络链路中出现了怎样的链路变化,TCP 都可以保证一个报文一定能够到达接收端;
- 字节流:消息是「没有边界」的,所以无论我们消息有多大都可以进行传输。并且消息是「有序的」,当「前一个」消息没有收到的时候,即使它先收到了后面的字节,那么也不能扔给应用层去处理,同时对「重复」的报文会自动丢弃。
13-2:为什么需要 TCP 协议?
IP 层是「 不可靠 」的,它只负责数据包的发送,但它不保证数据包能够被接收、不保证网络包的按序交付、也不保证网络包中的数据的完整性。
如果需要 保障网络数据包的可靠性 ,那么就需要 由上层(传输层)的 TCP 协议来负责 。
因为 TCP 是一个工作在传输层的可靠数据传输的服务,它能确保接收端接收的网络包是无损坏、无间隔、非冗余和按序的。
13-3:TCP 首部
转至TCP首部
源端口和目的端口:各占2个字节,分别写入源端口和目的端口。
序号:占4字节。序号范围是【0,2^32 - 1】,共2^32 (即4294967296)个序号。序号增加到2^32-1后,下一个序号就又回到0。也就是说,序号使用mod 2^32运算。
TCP是面向字节流的。在一个TCP连接中传送的字节流中的每一个字节都按顺序编号。整个要传送的字节流的起始序号必须在连接建立时设置。首部中的序号字段值则是指的是本报文段所发送的数据的第一个字节的序号。例如,一报文段的序号是301,而接待的数据共有100字节。这就表明:本报文段的数据的第一个字节的序号是301,最后一个字节的序号是400。显然,下一个报文段(如果还有的话)的数据序号应当从401开始,即下一个报文段的序号字段值应为401。这个字段的序号也叫“报文段序号”。
确认号: 占4字节,是期望收到对方下一个报文段的第一个数据字节的序号。例如,B正确收到了A发送过来的一个报文段,其序号字段值是501,而数据长度是200字节(序号501~700),这表明B正确收到了A发送的到序号700为止的数据。因此,B期望收到A的下一个数据序号是701,于是B在发送给A的确认报文段中把确认号置为701。注意,现在确认号不是501,也不是700,而是701。
总之:若确认号 = N,则表明:到序号N-1为止的所有数据都已正确收到。
数据偏移: 占4位,它指出TCP报文段的数据起始处距离TCP报文段的起始处有多远。这个字段实际上是指出TCP报文段的首部长度。由于首部中还有长度不确定的选项字段,因此数据偏移字段是必要的,但应注意,“数据偏移”的单位是32位字(即以4字节的字为计算单位)。由于4位二进制数能表示的最大十进制数字是15,因此数据偏移的最大值是60字节,这也是TCP首部的最大字节(即选项长度不能超过40字节)。
保留: 占6位,保留为今后使用,但目前应置为0 。
下面有6个控制位,用来说明本报文段的性质。
紧急URG(URGent): 当URG=1时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快发送(相当于高优先级的数据),而不要按原来的排队顺序来传送。例如,已经发送了很长的一个程序要在远地的主机上运行。但后来发现了一些问题,需要取消该程序的运行,因此用户从键盘发出中断命令。如果不使用紧急数据,那么这两个字符将存储在接收TCP的缓存末尾。只有在所有的数据被处理完毕后这两个字符才被交付接收方的应用进程。这样做就浪费了很多时间。
当URG置为1时,发送应用进程就告诉发送方的TCP有紧急数据要传送。于是发送方TCP就把紧急数据插入到本报文段数据的最前面,而在紧急数据后面的数据仍然是普通数据。这时要与首部中紧急指针(Urgent Pointer)字段配合使用。
确认ACK(ACKnowledgment): 仅当ACK = 1时确认号字段才有效,当ACK = 0时确认号无效。TCP规定,在连接建立后所有的传送的报文段都必须把ACK置为1。
推送 PSH(PuSH): 当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应。在这种情况下,TCP就可以使用推送(push)操作。这时,发送方TCP把PSH置为1,并立即创建一个报文段发送出去。接收方TCP收到PSH=1的报文段,就尽快地(即“推送”向前)交付接收应用进程。而不用再等到整个缓存都填满了后再向上交付。
复位RST(ReSeT): 当RST=1时,表明TCP连接中出现了严重错误(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立传输连接。RST置为1还用来拒绝一个非法的报文段或拒绝打开一个连接。
同步SYN(SYNchronization): 在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使SYN=1和ACK=1,因此SYN置为1就表示这是一个连接请求或连接接受报文。
终止FIN:(FINis,意思是“完”“终”) 用来释放一个连接。当FIN=1时,表明此报文段的发送发的数据已发送完毕,并要求释放运输连接。
窗口: 占2字节。窗口值是【0,2^16-1】之间的整数。窗口指的是发送本报文段的一方的接受窗口(而不是自己的发送窗口)。窗口值告诉对方:从本报文段首部中的确认号算起,接收方目前允许对方发送的数据量(以字节为单位)。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。总之,窗口值作为接收方让发送方设置其发送窗口的依据。
例如,发送了一个报文段,其确认号是701,窗口字段是1000.这就是告诉对方:“从701算起,我(即发送方报文段的一方)的接收缓存空间还可接受1000个字节数据(字节序号是701~1700),你在给我发数据时,必须考虑到这一点。”
总之:窗口字段明确指出了现在允许对方发送的数据量。窗口值经常在动态变化。
检验和: 占2字节。检验和字段检验的范围包括首部和数据这两部分。和UDP用户数据报一样,在计算检验和时,要在TCP报文段的前面加上12字节的伪首部。伪首部的格式和UDP用户数据报的伪首部一样。但应把伪首部第4个字段中的17改为6(TCP的协议号是6);把第5字段中的UDP中的长度改为TCP长度。接收方收到此报文段后,仍要加上这个伪首部来计算检验和。若使用TPv6,则相应的伪首部也要改变。
紧急指针: 占2字节。紧急指针仅在URG=1时才有意义,它指出本报文段中的紧急数据的字节数(紧急数据结束后就是普通数据) 。因此,在紧急指针指出了紧急数据的末尾在报文段中的位置。当所有紧急数据都处理完时,TCP就告诉应用程序恢复到正常操作。值得注意的是,即使窗口为0时也可以发送紧急数据。
选项: 长度可变,最长可达4字节。当没有使用“选项”时,TCP的首部长度是20字节。
13-4:TCP 作用
通过TCP协议, 能够更加稳定的将数据传递到目的地。
13-5:TCP 数据包的大小
由于以太网最大的数据帧是1518字节,刨去以太网帧的帧头(DMAC(目的MAC地址)6Bytes+SMAC(源MAC地址)6Bytes+Type域2Bytes)14Bytes和帧尾CRC校验部分4Bytes,那么剩下承载上层协议的地方也就是Data域最大就只能有1500Bytes这个值我们就把它称之为MTU。
IP数据包在以太网数据包的负载里面,它也有自己的头信息,最少需要20字节,所以IP数据包的负载最多为1480字节。
TCP数据包在IP数据包的负载里面。它的头信息最少也需要20字节,因此TCP数据包的最大负载是1480-20= 1460字节。由于IP和TCP协议往往有额外的头信息,所以TCP负载实际为1400字节左右。
13-6:TCP 数据包的编号(SEQ)
一个包1400字节,那么一次性发送大量数据,就必须分成多个包。比如,一个10MB的文件,需要发送7100多个包。
发送的时候, TCP协议为每个包编号(sequence number,简称SEQ) ,以便接收的一方按照顺序还原。万一发生丢包,也可以知道丢失的是哪一个包。
第一个包的编号是一个随机数。为了便于理解,这里就把它称为1号包。假定这个包的负载长度是100字节,那么可以推算出下一个包的编号应该是101,这就是说,每个数据包都可以得到两个编号:自身的编号,以及下一个包的编号。接收方由此知道,应该按照什么顺序将它们还原成原始文件。
13-7:TCP 数据包的组装
IP报文在传输过程中可能会分成多个片,在整个传输过程中是不会组装的(路由器也没有这个能力组装),IP报文分片的组装统一在对端主机上进行。对端主机在接收到IP数据报分片后会对其进行排序,形成一个完整的IP数据报之后上传给传输层。但IP数据报之间是不保证有序的。
13-8:如何唯一确定一个 TCP 连接呢?
TCP 四元组可以唯一的确定一个连接,四元组包括如下:
- 源地址
- 源端口
- 目的地址
- 目的端
源地址和目的地址的字段(32位)是在 IP 头部中,作用是通过 IP 协议发送报文给对方主机。
源端口和目的端口的字段(16位)是在 TCP 头部中,作用是告诉 TCP 协议应该把报文发给哪个进程。
13-9:有一个 IP 的服务器监听了一个端口,它的 TCP 的最大连接数是多少?
服务器通常固定在某个本地端口上监听,等待客户端的连接请求。
因此,客户端 IP 和 端口是可变的,其理论值计算公式如下:
对 IPv4,客户端的 IP 数最多为 2 的 32 次方,客户端的端口数最多为 2 的 16 次方,也就是服务端单机最大 TCP 连接数,约为 2 的 48 次方。
13-9-1:服务端最大并发 TCP 连接数远不能达到理论上限
当然,服务端最大并发 TCP 连接数远不能达到理论上限。
首先主要是文件描述符限制,Socket 都是文件,所以首先要通过 ulimit 配置文件描述符的数目;另一个是内存限制,每个 TCP 连接都要占用一定内存,操作系统的内存是有限的。
13-10:什么是 Mss
- MTU:一个网络包的最大长度,以太网中一般是1500字节。
- MSS:除去IP和TCP头部之后,一个网络包所能容纳的TCP数据的最大长度。
13-10-1:既然 IP 层会分片,为什么 TCP 层还需要 MSS 呢?
如果TCP的整个报文(头部+数据)交给IP进行分片的话:
当IP层有一个超过MTU大小的数据(TCP头部+TCP数据)要发送,那么IP层就要进行分片,把数据分片成若干片,保证每一个分片都小于MTU。把一份IP数据进行分片以后,由目标主机的IP层进行重新组装后,再交给上一层传输层。但是如果一个IP分片丢失整个IP报文的所有分片都要重传,因为IP层没有超时重传机制,它由传输层的TCP来负责超时和重传。当接收方发现TCP报文(头部+数据)的某一片丢失后,则不会响应ACK给对方,那么发送方的TCP在超时后,就会重发整个TCP报文(头部+数据)。所以,由IP层进行分片传输的话是非常没有效率的。
为了达到最佳的传输效能,TCP协议在建立连接的时候通常需要协商双方的MSS值,当TCP层发现数据超过MSS后,则就会先进行分片,当然由它形成的IP包的长度也就会大于MTU,自然也就不用IP分片。
经过TCP分片后,如果一个TCP分片丢失后,进行重发时也是以MSS为单位,而不用重传所有的分片,大大增大了重传的效率。
13-11:如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP还设有一个保活计时器,服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
14:TCP 的三次握手
14-1:TCP 三次握手流程
详见三次握手
14-2:TCP 为什么要三次握⼿
- 第一种说法是:为了防止已失效的连接请求报文段突然又传送到了 B,因而产生错误。
- 第二种说法是:为了实现可靠数据传输, TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤。
如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认。
14-3:TCP 为什么要传回syn?
接收端传回发送端所发送的 SYN 是为了告诉发送端,我接收到的信息确实就是你所发送的信号了。
SYN 是 TCP/IP 建立连接时使用的握手信号。在客户机和服务器之间建立正常的 TCP 网络连接时,客户机首先发出一个 SYN 消息,服务器使用 SYN-ACK 应答表示接收到了这个消息,最后客户机再以 ACK(Acknowledgement[汉译:确认字符 ,在数据通信传输中,接收站发给发送站的一种传输控制字符。它表示确认发来的数据已经接受无误。 ])消息响应。这样在客户机和服务器之间才能建立起可靠的TCP连接,数据才可以在客户机和服务器之间传递。
14-4:TCP 传了 SYN,为什么还要传 ACK?
双方通信无误必须是两者互相发送信息都无误。传了 SYN,证明发送方到接收方的通道没有问题,但是接收方到发送方的通道还需要 ACK 信号来进行验证。
14-5:为什么客户端和服务端的初始序列号 ISN 是不相同的?
因为网络中的报文会延迟、复制重发、有可能丢失,这样会造成不同连接之间产生相互影响,所以为了避免相互影响,客户端和服务端的初始序列号是随机且不同的。
14-6:什么是 SYN 攻击?如何避免 SYN 攻击?
SYN泛洪主要发生在OSI的第四层,利用了这个TCP三次握手的特性。
A(攻击者)发送TCP SYN,SYN是TCP三次握手中的第一个数据包,而当这个服务器返回ACK以后,A不再进行确认,那这个连接就处在了一个挂起的状态,也就是半连接的意思,那么服务器收不到再确认的一个消息,还会重复发送ACK给A。这样一来就会更加浪费服务器的资源。A就对服务器发送非法大量的这种TCP连接,由于每一个都没法完成握手的机制,所以它就会消耗服务器的内存最后可能导致服务器死机,就无法正常工作了。更进一步说,如果这些半连接的握手请求是恶意程序发出,并且持续不断,那么就会导致服务端较长时间内丧失服务功能——这样就形成了DoS攻击。这种攻击方式就称为SYN泛洪攻击。
如何去防范这种SYN攻击呢?
其实最常用的一个手段就是优化主机系统设置。比如降低SYN timeout时间,使得主机尽快释放半连接的占用或者采用SYN cookie设置,如果短时间内收到了某个IP的重复SYN请求,我们就认为受到了攻击。我们合理的采用防火墙设置等外部网络也可以进行拦截。
14-7:如何对三次握手进行性能优化
客户端:
可以根据网络的稳定性和目标服务器的繁忙程度修改重试次数,调整客户端的三次握手时间上限。比如内网中通讯时,就可以适当调低重试次数,尽快把错误暴露给应用程序。服务端:
- 把 tcp_syncookies 设置为 1,仅在队列满时再启用。
- 当网络繁忙、不稳定时,报文丢失就会变严重,此时应该调大重发次数。反之则可以调小重发次数。
- tcp_abort_on_overflow 设为 0 可以提高连接建立的成功率,只有你非常肯定 accept 队列会长期溢出时,才能设置为 1 以尽快通知客户端。
- listen 函数的 backlog 参数就可以设置 accept队列的大小。事实上,backlog 参数还受限于 Linux 系统级的队列长度上限,当然这个上限阈值也可以通过 somaxconn 参数修改。
14-8:如何绕过三次握手发送数据
三次握手建立连接造成的后果就是,HTTP 请求必须在一次 RTT(Round Trip Time,从客户端到服务器一个往返的时间)后才能发送,Google 对此做的统计显示,三次握手消耗的时间,在 HTTP 请求完成的时间占比在 10% 到 30% 之间。
因此,Google 提出了TCP fast open 方案(简称TFO),客户端可以在首个 SYN 报文中就携带请求,这节省了 1 个 RTT 的时间。
14-8-1:TCP Fast Open 的过程
为了让客户端在 SYN 报文中携带请求数据,必须解决服务器的信任问题。因为此时服务器的 SYN 报文还没有发给客户端,客户端是否能够正常建立连接还未可知,但此时服务器需要假定连接已经建立成功,并把请求交付给进程去处理,所以服务器必须能够信任这个客户端。
TFO 到底怎样达成这一目的呢?它把通讯分为两个阶段,第一阶段为首次建立连接,这时走正常的三次握手,但在客户端的 SYN 报文会明确地告诉服务器它想使用 TFO 功能,这样服务器会把客户端 IP 地址用只有自己知道的密钥加密(比如 AES 加密算法),作为Cookie 携带在返回的 SYN+ACK 报文中,客户端收到后会将 Cookie 缓存在本地。
之后,如果客户端再次向服务器建立连接,就可以在第一个 SYN 报文中携带请求数据,同时还要附带缓存的 Cookie。很显然,这种通讯方式下不能再采用经典的“先 connect 再write 请求”这种编程方法,而要改用 sendto 或者 sendmsg 函数才能实现。
服务器收到后,会用自己的密钥验证 Cookie 是否合法,验证通过后连接才算建立成功,再把请求交给进程处理,同时给客户端返回 SYN+ACK。虽然客户端收到后还会返回 ACK,但服务器不等收到 ACK 就可以发送 HTTP 响应了,这就减少了握手带来的 1 个 RTT 的时间消耗。
15:TCP 的四次挥手
15-1:TCP 四次挥手流程
详见–>四次挥手
15-2:TCP 为什么要四次挥手
因为当服务端收到客户端的 SYN 连接请求报文后,可以直接发送 SYN+ACK 报文。其中 ACK 报文是用来应答的,SYN 报文是用来同步的。但是关闭连接时,当服务端收到 FIN 报文时,很可能并不会立即关闭 SOCKET,所以只能先回复一个 ACK 报文,告诉客户端,“你发的 FIN 报文我收到了”。只有等到我服务端所有的报文都发送完了,我才能发送 FIN 报文,因此不能一起发送。故需要四次挥手。
15-3:TCP 第四次挥手后会立即断开吗?
不会,需要经过时间等待计时器设置的时间 2MSL 后,客户端才进入 CLOSED 状态。
15-3-1:为什么需要 TIME_WAIT 状态?(已经主动关闭连接了为啥还要保持资源一段时间呢?)
两个理由:
- 保证客户端发送的最后一个 ACK 报文段能够到达服务端。
这个 ACK 报文段有可能丢失,使得处于 LAST-ACK 状态的 B 收不到对已发送的 FIN+ACK 报文段的确认,服务端超时重传 FIN+ACK 报文段,而客户端能在 2MSL 时间内收到这个重传的 FIN+ACK 报文段,接着客户端重传一次确认,重新启动 2MSL 计时器,最后客户端和服务端都进入到 CLOSED 状态,若客户端在 TIME-WAIT 状态不等待一段时间,而是发送完 ACK 报文段后立即释放连接,则无法收到服务端重传的 FIN+ACK 报文段,所以不会再发送一次确认报文段,则服务端无法正常进入到 CLOSED 状态。 - 防止 “已失效的连接请求报文段” 出现在本连接中。
客户端在发送完最后一个 ACK 报文段后,再经过 2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失,使下一个新的连接中不会出现这种旧的连接请求报文段。
15-3-2:为什么 TIME_WAIT 等待的时间是 2MSL?
将TIME_WAIT的时长设置为 2MSL,是因为报文在链路中的最大生存时间为MSL(Maximum Segment Lifetime),超过这个时长后报文就会被丢弃。TIME_WAIT的时长则是:最后一次ACK传输到服务器的时间 + 服务器重传FIN 的时间,即为 2MSL。
15-3-3:TIME_WAIT 过多有什么危害?
- 在高并发短连接的TCP服务器上,当服务器处理完请求后立刻主动正常关闭连接。这个场景下会出现大量socket处于TIME_WAIT状态。如果客户端的并发量持续很高,此时部分客户端就会显示连接不上。
- 大量的time_wait状态也会占用系统一定的fd,内存和cpu资源,当然这个量一般比较小,并不是主要危害。
15-3-4:如何优化 TIME_WAIT?
调整系统内核参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
net.ipv4.tcp_fin_timeout 修改系默认的 TIMEOUT 时间
其余优化:
net.ipv4.tcp_keepalive_time = 1200
#表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为20分钟。
net.ipv4.ip_local_port_range = 1024 65000
#表示用于向外连接的端口范围。缺省情况下很小:32768到61000,改为1024到65000。
net.ipv4.tcp_max_syn_backlog = 8192
#表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。
net.ipv4.tcp_max_tw_buckets = 5000
#表示系统同时保持TIME_WAIT套接字的最大数量,如果超过这个数字,TIME_WAIT套接字将立刻被清除并打印警告信息。
默认为180000,改为5000。对于Apache、Nginx等服务器,上几行的参数可以很好地减少TIME_WAIT套接字数量,但是对于 Squid,效果却不大。此项参数可以控制TIME_WAIT套接字的最大数量,避免Squid服务器被大量的TIME_WAIT套接字拖死。简单来说,就是打开系统的TIMEWAIT重用和快速回收。
调整短链接为长链接
15-4:如何对四次挥手进行优化
16:重传机制
转至TCP重传机制
TCP要保证所有的数据包都可以到达(避免数据在传输过程中丢失),所以必须要有重传机制。
16-1:常见的重传机制
- 超时重传
- 快速重传
- SACK
- D-SACK
所有重传的机制都需要依赖通过序列号seq与确认应答ack。
16-2:超时重传
发送端发了1,2,3,4,5一共五份数据,接收端收到了1,2,于是回ack 3,然后收到了4(注意此时3还没收到),此时的TCP会怎么办?因为SeqNum和Ack是以字节数为单位,所以ack的时候,不能跳着确认,只能确认最大的连续收到的包,不然,发送端就以为之前的都收到了。
超时重传的处理方式:接收端不再回ack(直到收到数据3),发送端死等ack 3,当发送端发现收不到3的ack超时后,会重传3。一旦接收端收到3后,会ack 回 4——意味着3收到了,期待下一个数据4。
16-3:什么时候会发生超时重传
- 数据包丢失:发送端发送的数据未到达接收端。
- 确认应答丢失:接送端发送的确认报文未到达发送端。
16-4:超时重传存在的问题
因为要死等3,所以会导致4和5即便已经收到了,而发送端也完全不知道成功发送了哪些包。因为没有收到ack,所以,发送方可能会悲观地认为4和5也丢了,所以有可能也会导致4和5的重传。
对此有两种选择:
- 一种是仅重传timeout的包。也就是第3份数据。
- 另一种是重传timeout后所有的数据,也就是第3,4,5这三份数据。
除此之外:
- 当超时时间 RTO 较大时,重发就慢,丢了很长时间才重发,没有效率,性能差。
- 当超时时间 RTO 较小时,会导致可能并没有丢就重发,于是重发的就快,会增加网络拥塞,导致更多的超时,更多的超时导致更多的重发。
16-5:快速重传
快速重传不以时间驱动,而以数据驱动重传。
接收端如果没有收到期望的数据,而收到后续乱序的包,也给客户端回复 ACK,只不过是重复的 ACk,回复相同的ACK三次以后触发快速重传。
也就是说,如果,包没有连续到达,就ack最后那个可能被丢了的包,如果发送方连续收到3次相同的ack,就重传。快速重传的好处是不用等超时了再重传。
比如:如果发送方发出了1,2,3,4,5份数据,第一份先到送了,于是就ack回2,结果2因为某些原因没收到,3到达了,于是还是ack回2,后面的4和5都到了,但是还是ack回2,因为2还是没有收到,于是发送端收到了三个ack=2的确认,知道了2还没有到,于是就马上重转2。然后,接收端收到了2,此时因为3,4,5都收到了,于是ack回6。
16-6:快速重传的问题
快速重传只解决了一个问题,就是超时的问题,它依然面临一个艰难的选择:是重传之前的一个包还是重传所有包?对于上面的示例来说,是重传#2呢还是重传#2,#3,#4,#5呢?因为发送端并不清楚这连续的3个ack(2)是谁传回来的?也许发送端发了20份数据,是#6,#10,#20传来的呢。这样,发送端很有可能要重传从2到20的这堆数据(这就是某些TCP的实际的实现)。
16-7:SACK 方法
SACK(Selective Acknowledgment),在快速重传的基础上,返回最近收到的报文段的序列号范围,这样客户端就知道,哪些数据包已经到达服务器了。
如下图,发送方收到了三次同样的 ACK 确认报文,于是就会触发快速重发机制,通过 SACK 信息发现只有 200~299 这段数据丢失,则重发时,就只选择了这个 TCP 段进行重复。
16-8:D-SACK
DSACK,即重复 SACK,这个机制是在 SACK 的基础上,额外携带信息,告知发送方有哪些数据包自己重复接收了。DSACK 的目的是帮助发送方判断,是否发生了包失序、ACK 丢失、包重复或伪重传。让 TCP 可以更好的做网络流控。
16-9:D-SACK 好处
- 可以让「发送方」知道,是发出去的包丢了,还是接收方回应的 ACK 包丢了;
- 可以知道是不是「发送方」的数据包被网络延迟了;
- 可以知道网络中是不是把「发送方」的数据包给复制了;
17:滑动窗口与流量控制
17-1:引入窗口概念的原因
在确认应答策略中,对每一个发送的数据段,都要给一个ACK确认应答,收到ACK后再发送下一个数据段,这样做有一个比较大的缺点,就是性能比较差,尤其是数据往返的时间长的时候。
设想在发送端发送数据的速度很快而接收端接收速度却很慢的情况下,为了保证数据不丢失,显然需要进行流量控制,协调好通信双方的工作节奏。
17-2:什么是窗口
所谓滑动窗口,可以理解成接收端所能提供的缓冲区大小。TCP利用一个滑动的窗口来告诉发送端对它所发送的数据能提供多大的缓冲区。由于窗口由16位bit所定义,所以接收端TCP能最大提供65535个字节的缓冲。由此,可以利用窗口大小和第一个数据的序列号计算出最大可接收的数据序列号。
滑动窗口本质上是描述接受端的TCP数据报缓冲区大小的数据,发送端根据这个数据来计算自己最多能发送多长的数据。如果发送端收到接受端的窗口大小为0的TCP数据报,那么发送端将停止发送数据,等到接受端发送窗口大小不为0的数据报的到来。
17-3:窗口大小由哪一方决定?
TCP规定窗口的大小是由接收端通告的,通过采取慢启动和拥塞避免算法等机制来使带宽和性能取得最佳。
17-4:发送方的窗口
- TCP协议的两端分别为发送端A和接端B,由于是全双工协议,因此A和B应该分别维护着一个独立的发送缓冲区和接收缓冲区,由于对等性(A发B收和B发A收),我们以A发送B接收的情况作为例子。
- 发送窗口是发送缓存中的一部分,是可以被TCP协议发送的那部分,其实应用层需要发送的所有数据都被放进了发送端的发送缓冲区。
- 发送窗口中相关的有四个概念:已发送并收到确认的数据(不在发送窗口和发送缓冲区之内)、已发送但未收到确认的数据(位于发送窗口之中)、允许发送但尚未发送的数据以及发送窗口外发送缓冲区内暂时不允许发送的数据。
- 每次成功发送数据之后,发送窗口就会在发送缓冲区中按顺序移动,将新的数据包含到窗口中准备发送。
17-5:流量控制
所谓流量控制,主要是接收端传递信息给发送端,使其不要发送数据太快,是一种端到端的控制。主要的方式就是返回的ACK中会包含自己的接收窗口的大小,并且利用大小来控制发送方的数据发送。
17-6:TCP 是如何解决窗口关闭时,潜在的死锁现象呢?
如果B已经告诉A自己的缓冲区已满,于是A停止发送数据;等待一段时间后,B的缓冲区出现了富余,于是给A发送报文告诉A我的rwnd大小为400,但是这个报文不幸丢失了,于是就出现A等待B的通知||B等待A发送数据的死锁状态。为了处理这种问题,TCP引入了持续计时器(Persistence timer),当A收到对方的零窗口通知时,就启用该计时器,时间到则发送一个1字节的探测报文,对方会在此时回应自身的接收窗口大小,如果结果仍为0,则重设持续计时器,继续等待。
18:拥塞控制
18-1:什么是拥塞控制
拥塞控制是防止发送方发的太快,使得网络来不及处理,从而导致网络拥塞。
常用的方法就是:
- 慢开始
- 拥塞避免
- 快重传
- 快恢复
18-2:为什么要有拥塞控制呀,不是有流量控制了吗?
- 拥塞控制:拥塞控制是作用于网络的,它是防止过多的数据注入到网络中,避免出现网络负载过大的情况。
- 流量控制:流量控制是作用于接收者的,它是控制发送者的发送速度从而使接收者来得及接收,防止分组丢失。
流量控制虽然可以高效可靠的传送大量的数据,但是如果在刚开始阶段就发送大量的数据,可能会导致网络拥堵,因为网络上的计算机太多了。
18-3:什么是拥塞窗口?和发送窗口有什么关系呢?
发送方维持一个叫做拥塞窗口cwnd(congestion window)的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送方让自己的发送窗口等于拥塞窗口,另外考虑到接受方的接收能力,发送窗口可能小于拥塞窗口。
发送方控制拥塞窗口的原则是:只要网络没有出现拥塞。拥塞窗口就可以再增大一些,以便把更多的分组发送出去,这样就可以提高网络的利用率。但只要网络出现拥塞或有可能出现拥塞,就必须把拥塞窗口减小一些,以减少注入到网络中的分组数,以便缓解网络出现的拥塞。
18-4:那么怎么知道当前网络是否出现了拥塞呢?
当网络发生拥塞时,路由器就要丢弃分组。因此只要发送方没有按时收到应当到达的确认报文,就可以猜想网络出现了拥塞。
18-5:拥塞控制算法
慢开始:慢开始算法的思路就是,不要一开始就发送大量的数据,先探测一下网络的拥塞程度,也就是说由小到大以指数级逐渐增加拥塞窗口的大小。
为了防止cwnd增长过大引起网络拥塞,还需设置一个慢开始门限ssthresh状态变量。ssthresh的用法如下:当cwnd<ssthresh时,使用慢开始算法。
当cwnd>ssthresh时,改用拥塞避免算法。
当cwnd=ssthresh时,慢开始与拥塞避免算法任意。
注意,这里的“慢”并不是指cwnd的增长速率慢,而是指在TCP开始发送报文段时先设置cwnd=1,然后逐渐增大,这当然比按照大的cwnd一下子把许多报文段突然注入到网络中要“慢得多”。
拥塞避免:拥塞避免算法让拥塞窗口缓慢增长,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍。这样拥塞窗口按线性规律缓慢增长。
无论是在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞,就把慢开始门限ssthresh设置为出现拥塞时的发送窗口大小的一半(但不能小于2)。然后把拥塞窗口cwnd重新设置为1,执行慢开始算法。这样做的目的就是要迅速减少主机发送到网络中的分组数,使得发生拥塞的路由器有足够时间把队列中积压的分组处理完毕。
示例如下:
- 拥塞窗口cwnd初始化为1个报文段,慢开始门限初始值为16。
- 执行慢开始算法,指数规律增长到第4轮,即cwnd=16=ssthresh,改为执行拥塞避免算法,拥塞窗口按线性规律增长。
- 假定cwnd=24时,网络出现超时(拥塞),则更新后的ssthresh=12,cwnd重新设置为1,并执行慢开始算法。当cwnd=12=ssthresh时,改为执行拥塞避免算法。
关于 乘法减小(Multiplicative Decrease)和加法增大(Additive Increase):
“乘法减小”指的是无论是在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞,就把慢开始门限ssthresh设置为出现拥塞时的发送窗口大小的一半,并执行慢开始算法,所以当网络频繁出现拥塞时,ssthresh下降的很快,以大大减少注入到网络中的分组数。“加法增大”是指执行拥塞避免算法后,使拥塞窗口缓慢增大,以防止过早出现拥塞。常合起来成为AIMD算法。
注意:“拥塞避免”并非完全能够避免了阻塞,而是使网络比较不容易出现拥塞。
快重传:快重传要求接收方在收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方,可提高网络吞吐量约20%)而不要等到自己发送数据时捎带确认。快重传算法规定,发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待设置的重传计时器时间到期。
示例如下:
快恢复:当发送方连续收到三个重复确认时,就执行“乘法减小”算法,把ssthresh门限减半(为了预防网络发生拥塞)。但是接下去并不执行慢开始算法。考虑到如果网络出现拥塞的话就不会收到好几个重复的确认,所以发送方现在认为网络可能没有出现拥塞。所以此时不执行慢开始算法,而是将cwnd设置为ssthresh减半后的值,然后执行拥塞避免算法,使cwnd缓慢增大。
快重传和快恢复示例如下:
也有的快重传实现是把开始时的拥塞窗口cwnd值再增大一点,即等于 ssthresh + 3 X MSS 。这样做的理由是:既然发送方收到三个重复的确认,就表明有三个分组已经离开了网络。这三个分组不再消耗网络 的资源而是停留在接收方的缓存中。可见现在网络中并不是堆积了分组而是减少了三个分组。因此可以适当把拥塞窗口扩大了些。
在采用快恢复算法时,慢开始算法只是在TCP连接建立时和网络出现超时时才使用。采用这样的拥塞控制方法使得TCP的性能有明显的改进。
接收方根据自己的接收能力设定了接收窗口rwnd,并把这个窗口值写入TCP首部中的窗口字段,传送给发送方。因此,接收窗口又称为通知窗口。因此,从接收方对发送方的流量控制的角度考虑,发送方的发送窗口一定不能超过对方给出的接收窗口rwnd 。
- 发送方窗口的上限值 = Min [ rwnd, cwnd ]
- 当rwnd < cwnd 时,是接收方的接收能力限制发送方窗口的最大值。
- 当cwnd < rwnd 时,则是网络的拥塞限制发送方窗口的最大值。
19:TCP 传输数据优化方案
19-1:TCP 传输数据优化
20:TCP/UDP 如何保证可靠传输方式
20-1:TCP 如何保证稳定传输
- 确认和重传:接收方收到报文就会确认,发送方发送一段时间后没有收到确认就重传。
- 数据校验
- 数据合理分片和排序:tcp会按MTU合理分片,接收方会缓存未按序到达的数据,重新排序后再交给应用层。
- 流量控制:控制发送者的发送速度从而使接收者来得及接收,防止分组丢失。
- 拥塞控制:防止过多的数据注入到网络中,避免出现网络负载过大的情况。
20-2:UDP 如何做可靠传输
UDP不属于连接协议,具有资源消耗少,处理速度快的优点,所以通常音频,视频和普通数据在传送时,使用UDP较多,因为即使丢失少量的包,也不会对接受结果产生较大的影响。
传输层无法保证数据的可靠传输,只能通过应用层来实现了。实现的方式可以参照tcp可靠性传输的方式,只是实现不在传输层,实现转移到了应用层。
最简单的方式是在应用层模仿传输层TCP的可靠性传输。下面不考虑拥塞处理,可靠UDP的简单设计。
- 添加seq/ack机制,确保数据发送到对端
- 添加发送和接收缓冲区,主要是用户超时重传。
- 添加超时重传机制。
详细说明:送端发送数据时,生成一个随机seq=x,然后每一片按照数据大小分配seq。数据到达接收端后接收端放入缓存,并发送一个ack=x的包,表示对方已经收到了数据。发送端收到了ack包后,删除缓冲区对应的数据。时间到后,定时任务检查是否需要重传数据。
21:TCP 与 UDP/IP 比较
21-1:TCP 与 UDP 区别
TCP | UDP | |
---|---|---|
可靠性 | 可靠 | 不可靠 |
连接性 | 面向连接 | 无连接 |
报文 | 面向字节流 | 面向报文 |
效率 | 传输效率低 | 传输效率高 |
双工性 | 全双工 | 一对一、一对多、多对一、多对多 |
流量控制 | 滑动窗口 | 无 |
拥塞控制 | 慢开始、拥塞避免、快重传、快恢复 | 无 |
传输速度 | 慢 | 快 |
应用场景 | 对效率要求低,对准确性要求高或者要求有连接的场景 | 对效率要求高,对准确性要求低 |
21-2:TCP 和 UDP 应用场景
21-3:TCP 与 IP 的区别
网络中的传输流程是:IP层接收由更低层(网络接口层例如以太网设备驱动程序)发来的数据包,并把该数据包发送到更高层—TCP层;相反,IP层也把从TCP接收来的数据包传送到更低层。
TCP和IP的关系是:IP提供基本的数据传送,而高层的TCP对这些数据包做进一步加工,如提供端口号等等。
22:ARQ 协议
22-1:什么是 ARQ 协议
自动重传请求(Automatic Repeat-reQuest,ARQ)是OSI模型中数据链路层的错误纠正协议之一。它包括停止等待ARQ协议和连续ARQ协议,错误侦测(Error Detection)、正面确认(Positive Acknowledgment)、逾时重传(Retransmission after Timeout)与负面确认继以重传(Negative Acknowledgment and Retransmission)等机制。
22-2:什么是停止等待 ARQ 协议
停等ARQ协议相当于发送窗口和接收窗口大小均为1的滑动窗口协议。即发送方发送一个帧后,必须接收到一个确认帧(ACK)才能发送下一个。
22-3: 什么是连续 ARQ 协议
为了克服停等ARQ协议长时间等待ACK的缺点,这个协议会连续发送一组数据包,然后再等待这些数据包的ACK。