TCP/IP数据处理流程
应用层:决定向用户提供应用服务时通信的活动。TCP/IP 协议族内预存了各类通用的应用服务。比如:FTP、DNS、HTTP 协议。
传输层:传输层对上层应用层,提供处于网络连接中的两台计算机之间的数据传输。在传输层有两个性质不同的协议,分别是 TCP (Transmission Control Protocol,传输控制协议) 和 UDP (User Data Protocol,用户数据报协议)
网络层:网络层用来处理在网络上流动的数据包。数据包是网络传输的最小数据单位。该层规定了通过怎样的路径到达对方计算机,并把数据包传送给对方。与对方计算机通过多台计算机或网络设备进行传输时,网络层所起的作用就是在众多的选项内选择一条传输路线。
数据链路层: 在物理层提供比特流服务的基础上,建立相邻结点之间的数据链路,通过差错控制提供数据帧 (Frame)在信道上无差错的传输,并进行各电路上的动作系列。数据的单位称为帧 (frame)
物理层:物理层建立在物理通信介质的基础上,作为系统和通信介质的接口,用来实现数据链路实体间透明的比特 (bit) 流传输。只有该层为真实物理通信,其它各层为虚拟通信。
客户端在应用层 (HTTP 协议) 发出一个 HTTP 请求。
为了方便传输,在传输层 (TCP 协议) 把从应用层处收到的数据 (HTTP 请求报文) 进行分割,并在各个报文上打上标记序号及端口号后转发给网络层。
在网络层 (IP 协议),增加作为通信目的地的 MAC 地址后转发给数据链路层。这样,发送给服务端的请求就准备齐全了。
当服务端在链路层接收到数据时,按序往上层发送,一直到应用层。当传输到应用层时,才算真正的接收到由客户端发送过来的请求
邮件发送举例子
下图以用户 a 向用户 b 发送邮件为例子:
数据处理流程
① 应用程序处理
首先应用程序会进行编码处理,这些编码相当于 OSI 的表示层功能;
编码转化后,邮件不一定马上被发送出去,这种何时建立通信连接何时发送数据的管理功能,相当于 OSI 的会话层功能。
② TCP 模块的处理
TCP 根据应用的指示,负责建立连接、发送数据以及断开连接。TCP 提供将应用层发来的数据顺利发送至对端的可靠传输。为了实现这一功能,需要在应用层数据的前端附加一个 TCP 首部。
③ IP 模块的处理
IP 将 TCP 传过来的 TCP 首部和 TCP 数据合起来当做自己的数据,并在 TCP 首部的前端加上自己的 IP 首部。IP 包生成后,参考路由控制表决定接受此 IP 包的路由或主机。
④ 网络接口(以太网驱动)的处理
从 IP 传过来的 IP 包对于以太网来说就是数据。给这些数据附加上以太网首部并进行发送处理,生成的以太网数据包将通过物理层传输给接收端。
⑤ 网络接口(以太网驱动)的处理
主机收到以太网包后,首先从以太网包首部找到 MAC 地址判断是否为发送给自己的包,若不是则丢弃数据。
如果是发送给自己的包,则从以太网包首部中的类型确定数据类型,再传给相应的模块,如 IP、ARP 等。这里的例子则是 IP 。
⑥ IP 模块的处理
IP 模块接收到 数据后也做类似的处理。从包首部中判断此 IP 地址是否与自己的 IP 地址匹配,如果匹配则根据首部的协议类型将数据发送给对应的模块,如 TCP、UDP。这里的例子则是 TCP。
另外吗,对于有路由器的情况,接收端地址往往不是自己的地址,此时,需要借助路由控制表,在调查应该送往的主机或路由器之后再进行转发数据。
⑦ TCP 模块的处理
在 TCP 模块中,首先会计算一下校验和,判断数据是否被破坏。然后检查是否在按照序号接收数据。***检查端口号,确定具体的应用程序。数据被完整地接收以后,会传给由端口号识别的应用程序。
⑧ 应用程序的处理
接收端应用程序会直接接收发送端发送的数据。通过解析数据,展示相应的内容。
IP报文传输过程包括:
1. Host sends packet to default gateway(主机将数据包发送到默认网关)
2. Packet placed in frame(数据包被封装入帧)
3. Router receives frame(路由器接到帧)
4. Router finds destination network in route table(路由器在路由表中发现目标网络)
5. Router chooses next hop toward destination(路由器选择一个更接近目标的下一跳)
6. MAC address of next hop determined(下一跳的MAC地址被确定)
7. Packet placed in frame(数据包被封装入帧)
8. Repeats steps 2 through 7 as necessary(如果需要的话,重复步骤2~7)
9. Router receives frame(路由器接到帧)
10. Router finds network directly connected(路由器发现直连网络)
11. MAC address of end host determined(最终主机的MAC地址被确定)
12. Packet placed in frame to final destination(帧中的数据包被发送到最终主机)
主机A与主机B通信,A Ping B,图:A-交换机-B,AB同一子网
(1)ping数据包用的是ICMP协议,IP协议的一个子协议,位于三层,包含A的IP,B的IP,三层进行IP封装成包,进入二层
(2)A,B处于相同子网,查看缓存中对与目的对应得B第2层mac地址,如果存在,直接进行第2层封装成帧,经物理层信号编码,以0101010010这样的bits流传输在网络介质上。
(3)如果不存在B的MAC,则发送ARP广播请求B的MAC,ARP数据包经物理层进入交换机端口,需要进行源端口号学习,目的端口查找,B响应ARP请求,交换机又可以学习一目的MAC地址与哪个端口对应,在下次传送数据时就不再需要对所有端口进行广播了。B通过ARP单播把B的MAC响应给A,此时再返回(2)的处理过程。
(4)当再进行A与B之间数据包转发,将直接用B的MAC地址封装,数据转发得以高速交换
封装:所谓封装是指在发送方发生的自上而下的过程——在每一层为应用数据添加上特定的头部/尾部信息(PDU,Protocol Data Unit,协议数据单元)Application(应用程序)→segment(数据段)→packet(数据包)→frame(数据帧)→bit(比特,二进制位)
解封装:所谓解封装是指在接收方发生的自下而上的过程——逐层的去掉头部以及尾部信息
什么是Keep-Alive模式
HTTP协议采用“请求-应答”模式,当使用普通模式,即非KeepAlive模式时,每个请求/应答客户和服务器都要新建一个连接,完成 之后立即断开连接(HTTP协议为无连接的协议);当使用Keep-Alive模式(又称持久连接、连接重用)时,Keep-Alive功能使客户端到服 务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了建立或者重新建立连接。
1、http keep-alive:
在一次tcp连接中可以连续发送多次数据,即可以保持一段时间的tcp连接,在这个保持的通道上有多个request、多个response。而不用每发一次数据就要重新进行三次握手连接,发完一次数据就要立即进行四次挥手释放连接。 这样可以提高性能和吞吐率。
2、tcp keep-alive:
为了检测tcp的连接状况。经过设定的时间之后,服务器会发出检测包去确认tcp连接是否还在。如果出现了问题就关闭连接。
keep-alive 是指在一次 TCP 连接中完成多个 HTTP 请求,但是对每个请求仍然要单独发 header;所谓的 polling 是指从客户端(一般就是浏览器)不断主动的向服务器发 HTTP 请求查询是否有新数据。这两种模式有一个共同的缺点,就是除了真正的数据部分外,服务器和客户端还要大量交换 HTTP header,信息交换效率很低。它们建立的“长连接”都是伪.长连接,只不过好处是不需要对现有的 HTTP server 和浏览器架构做修改就能实现。
WebSocket 解决的第一个问题是,通过第一个 HTTP request 建立了 TCP 连接之后,之后的交换数据都不需要再发 HTTP request了,使得这个长连接变成了一个真.长连接。但是不需要发送 HTTP header就能交换数据显然和原有的 HTTP 协议是有区别的,所以它需要对服务器和客户端都进行升级才能实现。在此基础上 WebSocket 还是一个双通道的连接,在同一个 TCP 连接上既可以发也可以收信息。此外还有 multiplexing 功能,几个不同的 URI 可以复用同一个 WebSocket 连接。这些都是原来的 HTTP 不能做到的。
keep-alive 只是一种为了达到复用tcp连接的“协商”行为,双方并没有建立正真的连接会话,服务端也可以不认可,也可以随时(在任何一次请求完成后)关闭掉。WebSocket 不同,它本身就规定了是正真的、双工的长连接,两边都必须要维持住连接的状态。
tcp相关延时主要包括:
1、tcp连接时建立握手;
2、tcp慢启动拥塞控制;
3、数据聚集的Nagle算法;
4、用于捎带确认的tcp延迟确认算法;
5、TIME_WAIT时延和端口耗尽。
对上面的延时影响,相应的优化方法有:
1、http使用“持久化连接”,http 1.0中使用connection:keep-alive, http 1.1默认使用持久化连接;
2、调整或禁止延迟确认算法(HTTP协议具有双峰特征的请求-应答行为降低了捎带确认的可能);
3、设置TCP_NODELAY禁止Nagle算法,提高性能;
4、TIME_WAIT设置为一个较小的值。
https://www.cnblogs.com/kekukele/p/4843655.html
根据上述分析,我们可以修改linux的tcp参数,通过vi 打开/etc/sysctl.conf,添加如下参数
net.ipv4.ip_local_port_range=1024 65000net.ipv4.tcp_tw_reuse=1net.ipv4.tcp_fin_timeout=15
其中,
net.ipv4.ip_local_port_range在服务器收到很多连接时,系统的可用端口将很快被耗尽。通过修改net.ipv4.ip_local_port_range参数,可以将可用端口的范围改大.
net.ipv4.tcp_tw_reuse当服务器需要在大量TCP连接之间切换时,会产生大量处于TIME_WAIT状态的连接。TIME_WAIT意味着连接本身是关闭的,但资源还没有释放。将net_ipv4_tcp_tw_reuse设置为1是让内核在安全时尽量回收连接,这比重新建立新连接要便宜得多。
net.ipv4.tcp_fin_timeout这是处于TIME_WAIT状态的连接在回收前必须等待的最小时间。改小它可以加快回收。
什么时候会TIME_WAIT
TIME_WAIT的意思是结束了这次连接
TCP在关闭的时候有个四次挥手的过程,主动关闭方在四次挥手的最后一个ACK发送之后会变成TIME_WAIT状态。
主动关闭方
跟握手不同,挥手可以由客户端发起,也可以是服务端发起。发起关闭的一端我们称之为主动关闭方,另一端称之为被动关闭方。
四次挥手
主动关闭方会发送一个FIN给被动关闭方,表示数据已经发送完毕。被动关闭方接收到FIN,响应一个ACK。它的接收作为一个文件结束符(end-of-file)传递给接收端应用进程(放在所有已排队等候该应用进程接收的任何其他数据之后)。FIN意味着接收端在相应连接上再无额外的数据可以接收了。
一段时间后,被动关闭方的应用进程收到了文件结束符,发送完所有需要发送的内容,也会发送一个FIN给主动关闭方。接收到这个最终的FIN的主动关闭方也需要响应一个ACK。
TIME_WAIT状态维持多久
主动关闭方响应完最后一次ACK之后,会在TIME_WAIT这个状态维持2MSL。
MSL
MSL全称是maximum segment lifetime,最长分节生命期。MSL是任何IP数据报能够在因特网存活的最长时间。我们知道,这个时间是有限的,因为每个数据报都含有一个限跳(hop limit)的8位字段,它的最大值是255(简单的讲就是不同经过超过255个路由器)。尽管这个跳数限制而不是真正的时间限制,我们仍然假设最大限跳的分组在网络中存在的时间不可能超过MSL秒。
MSL的具体值通常为30秒或者是2分钟。
为什么需要TIME_WAIT
可靠地实现了TCP全双工连接的终止
我们知道,TCP是比较可靠的。当TCP向另一端发送数据时,他要求对端返回一个确认(如同我们关闭时候的FIN和ACK)。如果没有收到确认,则会重发。
回忆一下我们最终的那个FIN与ACK,被动关闭方发送FIN,并等待主动关闭方返回的ACK。我们假设最终的ACK丢失,被动关闭方将需要重新发送它的最终那个FIN,主动关闭方必须维护状态信息(TIME_WAIT),以允许它重发最终的那个ACK。
如果没有了这个状态,当他第二次收到FIN时,会响应一个RST(也是一种类型的TCP分节),会被服务器解释成一个错误。
为了TCP打算执行必要的工作以彻底终止某个连接两个方向上的数据流(即全双工关闭),那么他必须要正确处理连接终止四个分节中任何一个分节丢失的情况。
允许老的重复分节在网络中的消逝(为什么需要2MSL)
首先,存在这样的情况,某个路由器崩溃或者两个路由器之间的某个链接断开时,路由协议需要花费数秒到数分钟的时间才能稳定找出另一条通路。在这段时间内,可能发生路由循环(路由器A把分组发送给B,B又发送回给A),这种情况我们称之为迷途。假设迷途的分组是一个TCP分节,在迷途期间,发送端TCP超时并重传该分组,重传分组通过某路径到达目的地,而后不久(最多MSL秒)路由循环修复,早先迷失在这个循环中的分组最终也被送到目的地。这种分组被称之为重复分组或者漫游的重复分组,TCP必须要正确处理这些重复的分组。
我们假设ip1:port1和ip2:port2 之间有一个TCP连接。我们关闭了这个链接,过一段时间后在相同IP和端口之间建立了另一个连接。TCP必须防止来自之前那个连接的老的重复分组在新连接上出现。为了做到这一点,TCP将不复用处于TIME_WAIT状态的连接。2MSL的时间足以让某个方向上的分组存活MSL秒后被丢弃,另一个方向上的应答也最多存活MSL秒后被丢弃。
TCP FLAG 标记
基于标记的TCP包匹配经常被用于过滤试图打开新连接的TCP数据包。
TCP标记和他们的意义如下所列:
* F : FIN - 结束; 结束会话
* S : SYN - 同步; 表示开始会话请求
* R : RST - 复位;中断一个连接
* P : PUSH - 推送; 数据包立即发送
* A : ACK - 应答
* U : URG - 紧急
* E : ECE - 显式拥塞提醒回应
* W : CWR - 拥塞窗口减少
示例:
三次握手Three-way Handshake
一个虚拟连接的建立是通过三次握手来实现的
1. (B) --> [SYN] --> (A)
假如有服务器A、客户机B. 当B要和A通信时,B首先向A发一个SYN (Synchronize) 标记的包,告诉A请求建立连接.
注意: 一个 SYN包就是仅SYN标记设为1的TCP包(参见TCP包头Resources). 只有当A收到B发来的SYN包,才可建立连接,除此之外别无他法。
2. (B) <-- [SYN/ACK] <--(A)
接着,A收到后会发一个对SYN包的确认包(SYN/ACK)回去,表示对第一个SYN包的确认,并继续握手操作.
注意: SYN/ACK包是仅SYN 和 ACK 标记为1的包.
3. (B) --> [ACK] --> (A)
B收到SYN/ACK 包,B发一个确认包(ACK),通知A连接已建立。至此,三次握手完成,一个TCP连接完成。
注意: ACK包就是仅ACK 标记设为1的TCP包.
特别注意:需要注意的是当三此握手完成、连接建立以后,TCP连接的每个包都会设置ACK位
第一次握手:建立连接时,客户端发送syn包(syn=x)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
TCP四次挥手
四次握手Four-way Handshake
四次握手用来关闭已建立的TCP连接
1. (B) --> ACK/FIN --> (A)
2. (B) <-- ACK <-- (A)
3. (B) <-- ACK/FIN <-- (A)
4. (B) --> ACK --> (A)
四次挥手过程说明:
1、客户端发送断开TCP连接请求的报文,其中报文中包含seq序列号,并且还将报文中的FIN字段置为1,表示需要断开TCP连接。(FIN=1,seq=x,x序列号为客户端发送的上一个数据包中的确认号值。ACK=1, ack确认号为服务器发送的上一个数据包中的序列号+该数据包所带的数据的大小)
2、服务端会回复客户端发送的TCP断开请求报文,其包含seq序列号,而且会产生ACK字段,ACK字段数值是在客户端发过来的seq序列号基础上加1进行回复,以便客户端收到信息时,知晓自己的TCP断开请求已经得到验证。(ACK=1,ack=x+1,seq=第1步中的确认号值)
3、服务端在回复完客户端的TCP断开请求后,不会马上进行TCP连接的断开,服务端会先确保断开前,所有传输到A的数据是否已经传输完毕,一旦确认传输数据完毕,就会将回复报文的FIN字段置1,ACK为1。并且产生seq序列号。(FIN=1,ACK=1,ack确认号为客户端发送的上一个数据包中的序列号+该数据包所带数据的大小,seq=z,z序列号为服务器发送的上一个数据包中的确认号值)
4、客户端收到服务端的TCP断开请求后,会回复服务端的断开请求,包含seq字段和ack字段,ack字段会在服务端的TCP断开请求的seq基础上加1,第3步数据包中的序列号+1,从而完成服务端请求的验证回复。(FIN=1,ACK=z+1,seq=h,h为第3步中的确认号值)
四次挥手(Four-Way-Wavehand)的简单说法
1.意义:当被动方收到主动方的FIN报文通知时,它仅仅表示主动方没有数据再发送给被动方了。但未必被动方所有的数据都完整的发送给了主动方,所以被动方不会马上关闭SOCKET,它可能还需要发送一些数据给主动方后,再发送FIN报文给主动方,告诉主动方同意关闭连接,所以这里的ACK报文和FIN报文多数情况下都是分开发送的。
2.原理:
1)第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
2)第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入。
3)第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
4)第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手
至此TCP断开的4次挥手过程完毕。 在tcp四次挥手中, 被动关闭端在close socket后, 给主动关闭端发一个FIN包, 此时便进入到了LAST_ACK状态, 也就是等待最后一个ACK的回应。 那么LAST_ACK的持续时间, 实际上就是发FIN到接受ACK的时间, 在这段之间内, 被动关闭端的socket处于waiting for the lask ack的状态
注意: 由于TCP连接是双向连接, 因此关闭连接需要在两个方向上做。ACK/FIN 包(ACK 和FIN 标记设为1)通常被认为是FIN(终结)包。然而,由于连接还没有关闭, FIN包总是打上ACK标记。没有ACK标记而仅有FIN标记的包不是合法的包,并且通常被认为是恶意的。
注意 : 因为FIN和SYN一样,也要占一个序号。理论上服务器在TCP连接关闭时发送的终止数据包中,只有终止位是置1,然后客户端进行确认。但是在实际的 TCP实现中,在终止数据包中,确认位和终止位是同时置为1的,确认位置为1表示对最后一次传输的数据进行确认,终止位置为1表示关闭该方向的TCP连 接。
MTU(Maximum Transmission Unit) - 一种通信协议的某一层上面所能通过的最大数据包大小.
以太网为 1500,这也是为何路由器或 PC 上默认都是 1500,因此数据包一旦超过此大小,就会进行分包,这也就有了 IP 分片的过程,这里就引入了一个问题,IP 包是不可靠的,如果 IP 分片,如何保证 TCP 可靠?
于是这里引入了 MSS,
MSS(Maximum Segment Size) - 传输控制协议的一个参数,以字节数定义一个计算机或通信设备所能接受的分段的最大数据量。注意这是不包含 IP 头及 TCP 或 UDP 头的,例如,对于
TCP,MSS = 1500 - 20(IP 头)- 20(TCP 头)
TCP 通过 MSS 提前进行分段,从而保证数据不会超过 MTU,来保证 IP 不需要再进行分片。
MSS:Maximum Segment Size ,TCP提交给IP层最大分段大小,不包含TCP Header和 TCP Option,只包含TCP Payload ,MSS是TCP用来限制application层最大的发送字节数。如果底层物理接口MTU= 1500 byte,则 MSS = 1500- 20(IP Header) -20 (TCP Header) = 1460 byte,如果application 有2000 byte发送,需要两个segment才可以完成发送,第一个TCP segment = 1460,第二个TCP segment = 540。
如何检测网络各个节点 MTU 值呢?答案是我们可以通过 ping 的不分片发送进行检测。注意,1500 是去除了以太网头以后的包大小!对于 ping,传递的是 ICMP 包,由以太网头 + IP 头+ ICMP 头 + 数据组成,只要 IP 头 + ICMP 头 + 数据不超过 1500 即可。也就是说,MTU = 1500 - 20(IP 头)- 8(ICMP 头)= 1472。下面是基于 1472 进行实际测试。
示意图
如图所示,客户端和服务端之间的通道代表TCP的传输通道,两个箭头之间的方块代表一个TCP数据包,正常情况下一个TCP包传输一个应用数据。粘包时,两个或多个应用数据包被粘合在一起通过一个TCP传输。而拆包情况下,会一个应用数据包会被拆成两段分开传输,其他的一段可能会和其他应用数据包粘合。
URI和URL的区别
URI—Uniform Resource Identifier通用资源标志符
Web上可用的每种资源如HTML文档、图像、视频片段、程序等都是一个来URI来定位的
URI一般由三部组成
①访问资源的命名机制
②存放资源的主机名
③资源自身的名称,由路径表示,着重强调于资源。
<3>URI举例
如:https://blog.csdn.net/qq_32595453/article/details/79516787
我们可以这样解释它:
①这是一个可以通过https协议访问的资源,
②位于主机 blog.csdn.net上,
③通过“/qq_32595453/article/details/79516787”可以对该资源进行唯一标识(注意,这个不一定是完整的路径)
注意:以上三点只不过是对实例的解释,以上三点并不是URI的必要条件,URI只是一种概念,怎样实现无所谓,只要它唯一标识一个资源就可以了。
URL—Uniform Resource Location统一资源定位符
URL是Internet上用来描述信息资源的字符串,主要用在各种WWW客户程序和服务器程序上,特别是著名的Mosaic。
采用URL可以用一种统一的格式来描述各种信息资源,包括文件、服务器的地址和目录等。
URL一般由三部组成
①协议(或称为服务方式)
②存有该资源的主机IP地址(有时也包括端口号)
③主机资源的具体地址。如目录和文件名等
URL的一般格式为(带方括号[]的为可选项):
protocol :// hostname[:port] / path / [;parameters][?query]#fragment
URL的格式由三部分组成:
①第一部分是协议(或称为服务方式)。
②第二部分是存有该资源的主机IP地址(有时也包括端口号)。
③第三部分是主机资源的具体地址,如目录和文件名等。
第一部分和第二部分用“://”符号隔开,
第二部分和第三部分用“/”符号隔开。
第一部分和第二部分是不可缺少的,第三部分有时可以省略。
2)对称加密与非对称加密
答:
对称密钥加密是指加密和解密使用同一个密钥的方式,这种方式存在的最大问题就是密钥发送问题,即如何安全地将密钥发给对方;而非对称加密是指使用一对非对称密钥,即公钥和私钥,公钥可以随意发布,但私钥只有自己知道。发送密文的一方使用对方的公钥进行加密处理,对方接收到加密信息后,使用自己的私钥进行解密。
由于非对称加密的方式不需要发送用来解密的私钥,所以可以保证安全性;但是和对称加密比起来,它非常的慢,所以我们还是要用对称加密来传送消息,但对称加密所使用的密钥我们可以通过非对称加密的方式发送出去。
3)三次握手与四次挥手
答:
(1). 三次握手(我要和你建立链接,你真的要和我建立链接么,我真的要和你建立链接,成功)
第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。
第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。
第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。
(2). 四次挥手(我要和你断开链接;好的,断吧。我也要和你断开链接;好的,断吧):
第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。此时TCP链接处于半关闭状态,即客户端已经没有要发送的数据了,但服务端若发送数据,则客户端仍要接收。
第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。
4)为什么 TCP 链接需要三次握手,两次不可以么?
答:“三次握手” 的目的是为了防止已失效的链接请求报文突然又传送到了服务端,因而产生错误。
正常的情况:A 发出连接请求,但因连接请求报文丢失而未收到确认,于是 A 再重传一次连接请求。后来收到了确认,建立了连接。数据传输完毕后,就释放了连接。A 共发送了两个连接请求报文段,其中第一个丢失,第二个到达了 B。没有 “已失效的连接请求报文段”。
现假定出现了一种异常情况:即 A 发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达 B。本来这是一个早已失效的报文段。但 B 收到此失效的连接请求报文段后,就误认为是 A 再次发出的一个新的连接请求。于是就向 A 发出确认报文段,同意建立连接。
假设不采用“三次握手”,那么只要 B 发出确认,新的连接就建立了。由于现在 A 并没有发出建立连接的请求,因此不会理睬 B 的确认,也不会向 B 发送数据。但 B 却以为新的运输连接已经建立,并一直等待 A 发来数据。这样,B 的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。
5)为什么要四次挥手?
答:TCP 协议是一种面向连接的、可靠的、基于字节流的运输层通信协议。TCP 是全双工模式,这就意味着,当 A 向 B 发出 FIN 报文段时,只是表示 A 已经没有数据要发送了,而此时 A 还是能够接受到来自 B 发出的数据;B 向 A 发出 ACK 报文段也只是告诉 A ,它自己知道 A 没有数据要发了,但 B 还是能够向 A 发送数据。
所以想要愉快的结束这次对话就需要四次挥手。
TCP 协议如何来保证传输的可靠性
答:TCP 提供一种面向连接的、可靠的字节流服务。其中,面向连接意味着两个使用 TCP 的应用(通常是一个客户和一个服务器)在彼此交换数据之前必须先建立一个 TCP 连接。在一个 TCP 连接中,仅有两方进行彼此通信;而字节流服务意味着两个应用程序通过 TCP 链接交换 8 bit 字节构成的字节流,TCP 不在字节流中插入记录标识符。
对于可靠性,TCP通过以下方式进行保证:
数据包校验:目的是检测数据在传输过程中的任何变化,若校验出包有错,则丢弃报文段并且不给出响应,这时TCP发送数据端超时后会重发数据;
对失序数据包重排序:既然TCP报文段作为IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段的到达也可能会失序。TCP将对失序数据进行重新排序,然后才交给应用层;
丢弃重复数据:对于重复数据,能够丢弃重复数据;
应答机制:当TCP收到发自TCP连接另一端的数据,它将发送一个确认。这个确认不是立即发送,通常将推迟几分之一秒;
超时重发:当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段;
流量控制:TCP连接的每一方都有固定大小的缓冲空间。TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据,这可以防止较快主机致使较慢主机的缓冲区溢出,这就是流量控制。TCP使用的流量控制协议是可变大小的滑动窗口协议。
TCP拥塞控制
拥塞控制就是防止过多的数据注入网络中,这样可以使网络中的路由器或链路不致于过载。拥塞控制是一个全局性的过程。涉及网络中所有的主机、所有的路由器,以及与降低网络传输性能有关的所有因素。
TCP进行拥塞控制的算法有四种,即慢开始(slow-start)、拥塞避免(congestion-avoidance)、快重传(fast retransmit)、快恢复(fast recovery)。
为了讨论问题方便,提出以下假定:
(1) 数据是单方向传送的,对方只传送确认报文。
(2) 接收方总有足够大的缓存空间,因而发送窗口的大小由网络的拥塞程度来决定
慢开始和拥塞避免
拥塞控制也叫做基于窗口的拥塞控制。为此,发送方维持一个叫作拥塞窗口cwnd(congestion window)的状态变量。拥塞窗口的大小取决于网络的用谁程度,并且动态的变化。发送方让自己的发送窗口等于拥塞窗口。
接收方窗口值rwnd和拥塞窗口值cwnd的区别:
接收窗口值:接收方根据缓存设置的值,并告知给发送方,反映接收方容量。
拥塞窗口值:发送方根据自己估算的网络拥塞程度而设置的窗口值,反映网络当前容量。
发送方控制拥塞窗口的原则是:只要网络没有出现拥塞,拥塞窗口就可以再扩大一些,以便让更多的分组发送出去,如果网络出现了拥塞,就必须将拥塞窗口减小一些,以减少分组的发送。判断网络拥塞的依据就是出现了超时
慢开始算法的思路:刚开始发送数据时,不一下向网络中注入大量数据,而是先探测一下,即由小到大逐渐增大发送窗口,也就是说,由小到大逐渐增大拥塞窗口数值。
慢开始算法具体规定:刚开始发送数据时,先把拥塞窗口cwnd根据发送方的最大报文段SMSS(Sender Maximum Segment Size)数值的大小设置为不超过2-4个SMSS的数值。在每收到一个对新的报文段的确认后,可以把拥塞窗口增加最多一个SMSS的数值。用这样的方法逐步增大发送方的拥塞窗口rwnd,可以使分组注入到网络中的速率更加合理。
下面举例说明一下,虽然实际上TCP是用字节数作为窗口大小的单位,但为了方便描述,下面使用报文段的个数来作为窗口的大小的单位,并且假设所有的报文段大小相等。
刚开始时发送方先将窗口大小cwnd设置为1(这里的1是指的是一个报文段的字节数,下同),发送第一个分组,接收方接收后确认分组0。发送方接收到对分组0的确认后,将cwnd增加到2(即收到一个确认就把拥塞窗口增加一个报文段长度)。
发送方接着发送分组2 ~ 3,接收方接收后确认分组2 ~ 3,发送方收到2个确认后将cwnd从2增加到4。
发送方接着发送分组4 ~ 7,接收方接收后确认分组4 ~ 7,发送方收到4个确认后将cwnd从4增加到8。
一个传输轮次:发送方从发送一批分组到接收到它们的确认所经历的时间,即往返时间RTT(RTT并非是恒定的数值)。
拥塞避免算法的思路是让拥塞窗口cwnd缓慢增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是像慢开始阶段那样加倍增长。因此在拥塞避免阶段就有“加法增大”AI(Additive Increase)的特点。这表明在拥塞避免阶段,拥塞窗口cwnd按线性规律增长,比慢开始算法的拥塞窗口增长速率缓慢得多。
下面用一个具体的例子来说明拥塞控制的过程,下图假设TCP发送窗口等于拥塞窗口,慢开始初始门限设置为16个报文段,即ssthresh = 16。
在拥塞避免阶段,拥塞窗口是按照线性规律增大的,这常称为加法增大AI。无论在慢开始阶段还是拥塞避免阶段,只要出现一次超时(即出现一次网络拥塞),就把慢开始门限值 ssthresh 设置为当前拥塞窗口的一半,这叫做乘法减小 MD(Multiplication Decrease)。当网络频繁出现拥塞时,ssthresh 值就下降的很快,以大大减少注入网络中的分组数。
快速重传和快恢复
快恢复算法,如果发送方连续接收到3个冗余ACK,发送方知道现在只是丢失了个别的报文段,此时调整门限值 ssthresh为当前拥塞窗口的一半,同时设置拥塞窗口 cwnd为新的门限值(发生报文段丢失时拥塞窗口的一半),而不是从1开始
如上图所示,当cwnd = 24时,收到了3个冗余ACK,于是不启动慢开始,而是执行快恢复算。这时,发送方调整门限值 ssthresh = cwnd / 2 = 12,同时设置拥塞窗口 cwnd = ssthresh = 12,并开始拥塞避免算法。
TCP对这种丢包事件的行为,相比于超时指示的丢包,不那么剧烈,所以对于连续收到3个冗余ACK,拥塞窗口不会从1开始开始。
TCP流量控制
流量控制就是让发送方的发送速率不要太快,要让接收方来得及接收。即发送方的发送速率和接收方应用程序的读取速率相匹配。
TCP利用滑动窗口机制实现对发送方的流量控制。在建立连接的三次握手过程中通信双方协商一些参数,其中就有窗口值rwnd,接收方会将自己的接收能力存放字窗口值字段中传送给发送方,发送方根据这个窗口值设置发送窗口的大小。因此发送方发送窗口不能超过接收方给出的接收窗口的数值。
TCP的窗口单位是字节。
如下图所示,现假设接收方在建立连接告知发送方自己的接收窗口值是400,数据报文段的序号是从1开始的。ACK表示确认位,ack表示确认字段的值。一个报文段的长度是100字节,图中发送窗口中一个窗口表示100字节。
为了解决这个问题,TCP为每个连接设有一个持续计时器(persistence timer)。只要TCP连接的一方收到了对方的零窗口通知,就启动持续计时器。若持续计时器设置时间到期,就发送一个探测报文段(仅携带1字节的数据),而对方就在确认这个探测报文段时给出了现在的窗口值,如果窗口值仍然是零,那么收到报文段的一方就重新设置持续计时器。如果窗口不是零,则死锁的僵局就打破了
TCP的传输效率
应用进程将数据传送到TCP的发送缓存后,剩下的发送任务就由TCP来控制了。可以用不同的机制来控制TCP报文段的发送时机。
(1) 第一种机制:TCP维持一个变量,它等于最大报文段长度MSS。只要缓存中存放的数据达到MSS字节,就组装成一个TCP报文段发送出去。
(2) 第二种机制:由发送方的应用进程指明要求发送报文段,即TCP支持推送(push)操作。
(3) 第三种机制:发送方的一个计时器时限到了,这时就把当前已有的缓存数据装入报文段(但长度不能超过MSS)发送出去。
前面说过,如果发送方仅发送一个字节数据,那么在传输过程中对数据组装到网络层至少需要41字节,当在线路带宽不富裕时,这种传送方法的效率的确不高。
在TCP的实现中广泛使用Nagle算法。Nagle算法要求,当一个TCP连接中有在传数据(即那些已发送但还未经确认的数据),小的报文段(长度小于MSS)就不能发送,将这些小数据放入发送缓存中,直到所有的在传数据都收到了ACK.。并且,在收到ACK后,TCP需要将缓存中的小数据整合到一个报文段中发送。这种方法迫使TCP遵循停等协议——只有在收到所有在传数据的ACK后才能继续发送。该算法的优点在于它实现了自时钟控制:ACK返回的越快,数据传输的也就越快。
Nagle算法还规定:当发送缓存中的数据达到发送窗口大小的一半或已达到报文段的最大长度时,就立即发送下一个报文段。在相对高延迟的广域网中,更需要减少小报文段的数目,该算法使得单位时间内发送报文段数目更少。也就是说RTT控制着发包速率。
TCP连接管理和状态转换
前面提到建立一个TCP连接需要3个报文段。而关闭一个TCP连接需要4个报文段。TCP协议还支持连接处于半开启状态,但这种情况并不常见。存在上述半开启状态的原因在于TCP的通信模型是双向的。这也意味着在两个方向中可能会出现只有一个方向正在进行数据传输的情况。
TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。这就是所谓的半关闭。
如下图所示的半关闭,首先发送的两个报文段与TCP正常关闭完全相同:客户端发送的FIN,接着是服务器端返回对FIN的ACK。由于接收到半关闭的一方仍能够发送数据,因此在之后,服务器端可以向客户端发送任意数量的报文段(图中只画出了一个报文段)。当接收半关闭的一方完成了数据发送后,它将会发送一个FIN来关闭本方的连接,同时向客户端发出一个文件尾指示。当第2个FIN被确认后,整个连接完全关闭。
2 TCP半开启
如果在未告知另一端的情况下通信的一端关闭或终止连接,那么就认为该条TCP连接处于半开状态。这种情况发生在通信一方的主机崩溃的情况下。只要不尝试通过半开连接传输数据,正常工作的一端将不会检测出另一端已经崩溃。
例如,某些个人电脑运行了远程登录客户端,并且在一天结束时关闭。如果在电源被切断时没有数据在传输,那么服务器就永远不会知道该客户端已经消失,服务器可能还一直以为该连接处于ESTABLISHED状态。当第二天用户启动电脑并开始新的一个会话时,服务器会启动一个新的服务进程。这样就会导致服务器上有很多半开的TCP连接。当然,TCP有自己的方法来应对这种情况,具体见下文。
3 同时开启 && 同时关闭
虽然两个应用程序同时主动打开连接看似不大可能,但是在特定安排的情况下是有可能实现。通信双方在接收到来自对方的SYN之前必须先发送一个SYN;两个SYN必须经过网络送达对方。该场景还要求通信双方都拥有一个IP地址和端口号,并且将其告知对方。上述十分少见,一旦发生,可称为同时打开。
例如,主机A的一个应用程序通过本地的7777端口向主机B的8888端口发送一个主动打开请求,与此同时主机B的应用进程也通过本地的8888端口向主机A的7777端口提出一个主动打开请求,此时就会发生一个同时打开的情况。上述的情况不同于主机A的一个客户端连接主机B的一个服务器,而同时又有主机B的一个客户端连接主机A的一个服务器的情况。在这种情况下,服务器(A或B主机的服务器)始终是连接的被动打开者而非主动打开者。而各自的客户端也会使用不同的端口号。
HTTPS 性能优化经验
HTTPS 访问速度优化
1.TCP Fast Open
HTTPS 和 HTTP 使用 TCP 协议进行传输,也就意味着必须通过三次握手建立 TCP 连接,但一个 RTT 的时间内只传输一个 syn 包是不是太浪费?能不能在 syn 包发出的同时捎上应用层的数据?其实是可以的,这也是 TCP Fast Open 的思路,简称 TFO。具体原理可以参考 RFC7413。
TFO 对于系统版本有一定的要求,TFO 的 IPv4 支持在 3.6(客户端)和 3.7(服务端)版本中被合并进 Linux 内核主线,从 3.13 版本开始默认打开。IPv6 服务器的 TFO 支持被合并进入 3.16 版本。Microsoft Edge 从 Windows10 Previewbuild14352 开始支持 TFO。
2. HSTS
在系列文章的第一篇中,我们提到过用户的 HTTP 请求会被 302 跳转到 HTTPS,这会有两个影响:
不安全,302 跳转不仅暴露了用户的访问站点,也很容易被中间者劫持。
降低访问速度,302 跳转不仅需要一个 RTT,浏览器执行跳转也需要执行时间。
由于 302 跳转事实上是由浏览器触发的,服务器无法完全控制,这个需求导致了 HSTS 的诞生。
HSTS(HTTP Strict Transport Security)的作用是强制客户端(如浏览器)使用 HTTPS 与服务器创建连接。服务端返回一个 HSTS 的 HTTP Header,浏览器获取到 HSTS 头部之后,在一段时间内,不管用户输入 www.baidu. com 还是 http:// www.baidu. com,都会默认将请求内部跳转成 https:// www.baidu. com。Chrome,Firefox,IE,EDGE 等常见浏览器都支持了 HSTS。
3.OCSP Stapling
OCSP 全称在线证书状态检查协议(RFC 6960),用来向 CA 站点查询证书状态,比如是否撤销。通常情况下,浏览器使用 OCSP 协议发起查询请求,CA 返回证书状态内容,然后浏览器接受证书是否可信的状态。
这个过程非常消耗时间,因为 CA 站点有可能在国外,网络不稳定,RTT 也比较大。那有没有办法不直接向 CA 站点请求 OCSP 内容呢?OCSP Stapling 就能实现这个功能。
OSCP Stapling 工作原理简单来说就是浏览器发起 Client Hello 时会携带一个 certificate status request 的扩展,服务端看到这个扩展后将 OCSP 内容直接返回给浏览器,完成证书状态检查。由于浏览器不需要直接向 CA 站点查询证书状态,这个功能对访问速度的提升非常明显。关于 OCSP Stapling 的详细介绍可参考 RFC6066 第 8 节。
Nginx 目前已经支持 OCSP Stapling File.
4.试用 SPDY 或者 HTTP2
SPDY 是 Google 推出的优化 HTTP 传输效率的协议,它基本上沿用了 HTTP 协议的语义, 但是通过使用帧控制实现了多个特性,显著提升了 HTTP 协议的传输效率。
SPDY 最大的特性就是多路复用,能将多个 HTTP 请求在同一个连接上一起发出去,不像目前的 HTTP 协议一样,只能串行地逐个发送请求。Pipeline 虽然支持多个请求一起发送,但是接收时依然得按照顺序接收,本质上无法解决并发的问题。
HTTP2 是 IETF2015 年 2 月份通过的 HTTP 下一代协议,它以 SPDY 为原型,经过两年多的讨论和完善最终确定。
本文就不过多介绍 SPDY 和 HTTP2 的收益,需要说明两点:
SPDY 和 HTTP2 目前的实现默认使用 HTTPS 协议。
SPDY 和 HTTP2 都支持现有的 HTTP 语义和 API,对 WEB 应用几乎是透明的。