计网复习
基础篇
TCP/IP 网络模型有哪几层
应用层
-
而且应用层是工作在操作系统中的用户态,传输层及以下则工作在内核态。
应用层只需要专注于为用户提供应用功能,比如 HTTP、FTP、Telnet、DNS、SMTP等。
传输层
-
传输层为应用层服务,有两个传输协议TCP(传输控制协议)和UDP(用户数据包协议)、
-
UDP 也可以实现可靠传输,把 TCP 的特性在应用层上实现就可以,但这也不容易。流量控制、超时重传、拥塞控制
-
传输层的数据包大小超过 MSS(TCP 最大报文段长度) ,就要将数据包分块。
网络层
- 帮助实现应用到应用的通信,而实际的传输功能就交给下一层,也就是网络层。
- 网络层最常使用的是 IP 协议,IP 协议会将传输层的报文作为数据部分,再加上 IP 包头组装成 IP 报文,如果 IP 报文大小超过 MTU(以太网中一般为 1500 字节)会再次分片。
-
IP 地址分成两种意义: 一个是网络号,负责标识该 IP 地址是属于哪个「子网」的; 一个是主机号,负责标识同一「子网」下的不同主机; 怎么分的呢?这需要配合子网掩码才能算出 IP 地址 的网络号和主机号。
-
寻址的过程中,先匹配到相同的网络号(表示要找到同一个子网),才会去找对应的主机。 除了寻址能力, IP 协议还有另一个重要的能力就是路由。
网络接口层
-
生成了 IP 头部之后,接下来要交给网络接口层(Link Layer)在 IP 头部的前面加上 MAC 头部,并封装成数据帧(Data frame)发送到网络上。
-
MAC 头部是以太网使用的头部,它包含了接收方和发送方的 MAC 地址等信息,我们可以通过 ARP 协议获取对方的 MAC 地址。 所以说,网络接口层主要为网络层提供「链路级别」传输的服务,负责在以太网、WiFi 这样的底层网络上发送原始数据包,工作在网卡这个层次,使用 MAC 地址来标识网络上的设备。
总结
键入网址到网页显示,期间发生了什么?
浏览器解析URL,生成HTTP请求消息。
浏览器把域名发给DNS,得到对应IP地址。
当然中间有缓存,浏览器缓存,操作系统缓存,hosts文件,之后再去找本地DNS
把HTTP的传输工作交给操作系统的协议栈。
可靠传输TCP
-
TCP 三握手
保证有接受和发送的能力,因为在三次握手中,两方都进行了一收一发。第三次握手中可以发送信息了。
MTU:一个网络包的最大长度
MSS:除去IP和TCP头部之后,一个网络包所能容纳的TCP数据的最大长度
远程定位IP
在 IP 协议里面需要有源地址 IP 和 目标地址 IP: 源地址IP,即是客户端输出的 IP 地址; 目标地址,即通过 DNS 域名解析得到的 Web 服务器 IP。 因为 HTTP 是经过 TCP 传输的,所以在 IP 包头的协议号,要填写为 06(十六进制),表示协议为 TCP。
两点传输MAC
生成了 IP 头部之后,接下来网络包还需要在 IP 头部的前面加上 MAC 头部。
接受方的MAC地址,就在自己的ROM中。
发送方的MAC地址,则需要使用ARP协议。先问操作系统缓存IP-MAC的映射是否存在,不在,则去广播询问整个以太网/子网。
出口-网卡
交换机
将网络包不做任何修改的原样发送到目的地。成为二层网络设备,本身不具有一般MAC地址和IP地址。只是进行转发,保留一个MAC地址到端口的映射表,如MAC在表中,则转发,否则广播并且计入表中。
路由器
网络包经过交换机之后,现在到达了路由器,并在此被转发到下一个路由器或目标设备。
这一步转发的工作原理和交换机类似,也是通过查表判断包转发的目标。 不过在具体的操作过程上,路由器和交换机是有区别的。
因为路由器是基于 IP 设计的,俗称三层网络设备,路由器的各个端口都具有 MAC 地址和 IP 地址; 而交换机是基于以太网设计的,俗称二层网络设备,交换机的端口不具有 MAC 地址。
在网络包传输的过程中,源 IP 和目标 IP 始终是不会变的,一直变化的是 MAC 地址,因为需要 MAC 地址在以太网内进行两个设备之间的包传输。
服务器与客户端
数据包抵达服务器后,服务器会先扒开数据包的 MAC 头部,查看是否和服务器自己的 MAC 地址符合,符合就将包收起来。
接着继续扒开数据包的 IP 头,发现 IP 地址符合,根据 IP 头中协议项,知道自己上层是 TCP 协议。
于是,扒开 TCP 的头,里面有序列号,需要看一看这个序列包是不是我想要的,如果是就放入缓存中然后返回一个 ACK,如果不是就丢弃。TCP头部里面还有端口号, HTTP 的服务器正在监听这个端口号。
于是,服务器自然就知道是 HTTP 进程想要这个包,于是就将包发给 HTTP 进程。 服务器的 HTTP 进程看到,原来这个请求是要访问一个页面,于是就把这个网页封装在 HTTP 响应报文里。
HTTP 响应报文也需要穿上 TCP、IP、MAC 头部,不过这次是源地址是服务器 IP 地址,目的地址是客户端 IP 地址。
TCP关闭时四次挥手
数据传输完毕后,双方都可释放连接。最开始的时候,客户端和服务器都是处于ESTABLISHED状态,然后客户端主动关闭,服务器被动关闭。
第一次挥手 客户端发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态
第二次挥手 服务器端接收到连接释放报文后,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT 关闭等待状态
第三次挥手 客户端接收到服务器端的确认请求后,客户端就会进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文,服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
第四次挥手 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态,但此时TCP连接还未终止,必须要经过2MSL后(最长报文寿命),当客户端撤销相应的TCB后,客户端才会进入CLOSED关闭状态,服务器端接收到确认报文后,会立即进入CLOSED关闭状态,到这里TCP连接就断开了,四次挥手完成
为什么客户端要等待2MSL?
主要原因是为了保证客户端发送那个的第一个ACK报文能到到服务器,因为这个ACK报文可能丢失,并且2MSL是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃,这样新的连接中不会出现旧连接的请求报文。
Linux系统是如何收发网络包的?
OSI模型:
-
应用层,负责给应用程序提供统一的接口;
-
表示层,负责把数据转换成兼容另一个系统能识别的格式;
-
会话层,负责建立、管理和终止表示层实体之间的通信会话;
-
传输层,负责端到端的数据传输;
-
网络层,负责数据的路由、转发、分片;
-
数据链路层,负责数据的封帧和差错检测,以及 MAC 寻址;
-
物理层,负责在物理网络中传输数据帧;
TCP/IP 网络模型:
- 应用层,负责向用户提供一组应用程序,比如 HTTP、DNS、FTP 等;
- 传输层,负责端到端的通信,比如 TCP、UDP 等;
- 网络层,负责网络包的封装、分片、路由、转发,比如 IP、ICMP 等;
- 网络接口层,负责网络包在物理网络中的传输,比如网络包的封帧、 MAC 寻址、差错检测,以及通过网卡传输网络帧等;
Linux网络协议栈
Linux使用的是TCP/IP的模型。
Linux接受网络包流程
网卡是一个硬件,负责收发网络包,当网卡接受到一个网络包之后,用DMA写入内存,然后告知操作系统网络包已达。
告知OS的方法:
-
中断。但是频繁中断会导致性能开销。
-
使用轮询poll。
CPU收到硬件中断请求之后,调用已经注册的硬件中断函数。
硬件中断函数先暂时屏蔽中断,然后调用软中断函数,之后恢复刚才屏蔽的中断。
Linux接收网络包流程:
- 网络接口层 检查和去掉帧头和帧尾
- 网络层,取出IP包,判断是发送还是接收。如果是接收,会看看TCP还是UDP,然后去掉IP头和IP尾。
- 传输层取出TCP或者UDP的头,根据 [源IP,源端口,目标IP,目标端口]作为表示,找出socket,放在socket接收缓冲区。
- 应用层程序调用socket接口,把内核的socket接收缓冲区的数据copy到应用层的缓冲区,并唤醒用户进程。
- 完成接收。
Linux发送网络包流程
发送网络包的流程和接收流程相反。
首先,应用程序会调用 Socket 发送数据包的接口,由于这个是系统调用,所以会从用户态陷入到内核态中的 Socket 层,内核会申请一个内核态的 sk_buff 内存,将用户待发送的数据拷贝到 sk_buff 内存,并将其加入到发送缓冲区。
接下来,网络协议栈从 Socket 发送缓冲区中取出 sk_buff,并按照 TCP/IP 协议栈从上到下逐层处理 。如果使用的是 TCP 传输协议发送数据,那么先拷贝一个新的 sk_buff 副本 ,这是因为 sk_buff 后续在调用网络层,最后到达网卡发送完成的时候,这个 sk_buff 会被释放掉。而 TCP 协议是支持丢失重传的,在收到对方的 ACK 之前,这个 sk_buff 不能被删除。所以内核的做法就是每次调用网卡发送的时候,实际上传递出去的是 sk_buff 的一个拷贝,等收到 ACK 再真正删除。
接着,对 sk_buff 填充 TCP 头。这里提一下,sk_buff 可以表示各个层的数据包,在应用层数据包叫 data,在 TCP 层我们称为 segment,在 IP 层我们叫 packet,在数据链路层称为 frame。 你可能会好奇,为什么全部数据包只用一个结构体来描述呢?协议栈采用的是分层结构,上层向下层传递数据时需要增加包头,下层向上层数据时又需要去掉包头,如果每一层都用一个结构体,那在层之间传递数据的时候,就要发生多次拷贝,这将大大降低 CPU 效率。
于是,为了在层级之间传递数据时,不发生拷贝,只用 sk_buff 一个结构体来描述所有的网络包,那它是如何做到的呢?是通过调整 sk_buff 中 data 的指针,比如: 当接收报文时,从网卡驱动开始,通过协议栈层层往上传送数据报,通过增加 skb->data 的值,来逐步剥离协议首部。 当要发送报文时,创建 sk_buff 结构体,数据缓存区的头部预留足够的空间,用来填充各层首部,在经过各下层协议时,通过减少 skb->data 的值来增加协议首部。
至此,传输层的工作也就都完成了。
然后交给网络层,在网络层里会做这些工作:选取路由(确认下一跳的 IP)、填充 IP 头、netfilter 过滤、对超过 MTU 大小的数据包进行分片。处理完这些工作后会交给网络接口层处理。
网络接口层会通过 ARP 协议获得下一跳的 MAC 地址,然后对 sk_buff 填充帧头和帧尾,接着将 sk_buff 放到网卡的发送队列中。
这一些工作准备好后,会触发软中断 告诉网卡驱动程序,这里有新的网络包需要发送,驱动程序会从发送队列中读取 sk_buff,将这个 sk_buff 挂到 RingBuffer 中,接着将 sk_buff 数据映射到网卡可访问的内存 DMA 区域,最后触发真实的发送。
当数据发送完成以后,其实工作并没有结束,因为内存还没有清理。当发送完成的时候,网卡设备会触发一个硬中断来释放内存,主要是释放 sk_buff 内存和清理 RingBuffer 内存。
最后,当收到这个 TCP 报文的 ACK 应答时,传输层就会释放原始的 sk_buff 。
当发送网络数据时,涉及几次数据copy?
- 应用层把数据发给内核的sk_buff内存,加入缓冲区。第一次。
- 使用TCP时,传输层到网络层会copy,只有收到ACK之后,才会释放。第二次。
- IP层发现sk_buff大于MTU进行切片时。申请额外sk_buff。第三次
HTTP篇
HTTP 基本概念
HTTP即超文本传输协议,HyperText Transfer Protocol
-
协议:它使用计算机能够理解的语言确立了一种计算机之间交流通信的规范(两个以上的参与者),以及相关的各种控制和错误处理方式(行为约定和规范)。
-
传输: 是一个在计算机世界里专门用来在两点之间传输数据的约定和规范。
-
超文本: 文本就是简单的字符,超文本就是超越了字符,包括图片、视频、压缩包等等二进制文件。其中超链接可以从一个超文本中跳转到另一个超文本。
HTTP 是一个在计算机世界里专门在「两点」之间「传输」文字、图片、音频、视频等「超文本」数据的「约定和规范」。
HTTP常见字段
- Host字段,指定主机,也就制定了IP:Port对。
- Content-Length字段,服务器返回数据时,表明数据长度。
- Connection字段,常用于客户端要求服务器使用 HTTP长连接 机制。其特点是只要一段没有明确提出断开连接,就一直连接。
- Content-Type字段,用于服务器回应时,告诉客户端,本次数据的格式。
- Accept,客户端请求时,可以用该字段声明自己可以接受的格式。
- Content-Encoding字段,服务器返回时表明数据的压缩方法。
- Accept-Encoding字段,说明客户端可以接受的压缩方法。
GET 与 POST
GET 与 POST 的区别
- GET指获取服务器的指定资源。其请求参数一般在URL中,URL只支持ASCII,HTTP不对URL长度做限制。
- POST指对服务器的指定资源做处理。其请求具体内容在body中,不出现在URL。
- 浏览器不会对POST缓存,但是会对GET缓存。这是基于RFC语义的。
- GET是安全且幂等的。
- POST是不安全且不幂等的。
- 不过RFC规定归规定,实际上程序员不一定完全符合。
- HTTP未对body以及URL查询参数做限制,任何操作都可以有这两个东西。
HTTP缓存技术
HTTP缓存的实现
- 避免发送HTTP请求的方法就是缓存技术。
- 实现方式有两种,强制缓存与协商缓存。
- 缓存的识别与使用一般在头部。
强制缓存
强缓存指的是只要浏览器判断缓存没有过期,则直接使用浏览器的本地缓存,决定是否使用缓存的主动性在于浏览器这边。
强缓存是利用下面这两个 HTTP 响应头部(Response Header)字段实现的,它们都用来表示资源在客户端缓存的有效期:
Cache-Control, 是一个相对时间;
Expires,是一个绝对时间;
如果 HTTP 响应头部同时有 Cache-Control 和 Expires 字段的话,Cache-Control 的优先级高于 Expires 。
协商缓存
协商缓存可以有两个方法实现:
第一种,请求头部中的 If-Modified-Since 字段与响应头部中的 Last-Modified 字段实现,这两个字段的意思是:
响应头部中的 Last-Modified:标示这个响应资源的最后修改时间;
请求头部中的 If-Modified-Since:当资源过期了,发现响应头中具有 Last-Modified 声明,则再次发起请求的时候带上 Last-Modified 的时间,服务器收到请求后发现有 If-Modified-Since 则与被请求资源的最后修改时间进行对比(Last-Modified),如果最后修改时间较新(大),说明资源又被改过,则返回最新资源,HTTP 200 OK;如果最后修改时间较旧(小),说明资源无新修改,响应 HTTP 304 走缓存。
第二种:请求头部中的 If-None-Match 字段与响应头部中的 ETag 字段,这两个字段的意思是:
响应头部中 Etag:唯一标识响应资源;
请求头部中的 If-None-Match:当资源过期时,浏览器发现响应头里有 Etag,则再次向服务器发起请求时,会将请求头 If-None-Match 值设置为 Etag 的值。服务器收到请求后进行比对,如果资源没有变化返回 304,如果资源变化了返回 200。
- 协商缓存需要配合强制缓存中 cache-control 使用。
- 当同时有 ETag 和 Last-Modified 的时候,优先 ETag,因为 ETag 可以保证一定能检测出文件的修改。
HTTP特性
HTTP优点:
- 简单:报文为header+body,头部为key-value
- 灵活和易于拓展:各种请求方法都不没固定死,允许开发人员自定义和扩充。
- 应用广泛和跨平台。
HTTP缺点:
-
无状态:好处是轻便好用,但当进行有关联性的操作时很麻烦。
一种方法是使用Cookie
-
明文传输:网上裸奔,不安全。可以通过HTTPS解决。
HTTP/1.1性能
HTTP是基于TCP/IP的,使用请求-应答模式。
- 长连接:避免每次发送请求都建立一次TCP链接
- 管道网络传输:基于长连接,可以多个请求而无需等待前一个请求的完成。
- 队头阻塞:当顺序发送的请求序列中的一个请求因为某种原因被阻塞时,在后面排队的所有请求也一同被阻塞了,会招致客户端一直请求不到数据,这也就是「队头阻塞」,好比上班的路上塞车。
HTTP与HTTPS
HTTP与HTTPS区别
-
HTTP 是超文本传输协议,信息是明文传输,存在安全风险的问题。HTTPS 则解决 HTTP 不安全的缺陷,在 TCP 和 HTTP 网络层之间加入了 SSL/TLS 安全协议,使得报文能够加密传输。
-
HTTP 连接建立相对简单, TCP 三次握手之后便可进行 HTTP 的报文传输。而 HTTPS 在 TCP 三次握手之后,还需进行 SSL/TLS 的握手过程,才可进入加密报文传输。
-
两者的默认端口不一样,HTTP 默认端口号是 80,HTTPS 默认端口号是 443。
-
HTTPS 协议需要向 CA(证书权威机构)申请数字证书,来保证服务器的身份是可信的。
HTTPS解决的HTTP问题
- 信息加密:混合加密(非对称加密 + 对称加密)的方式实现信息的机密性,解决了窃听的风险。对称加密可能会破解,但是可以用非对称加密加密数字签名。
- 校验机制:摘要算法的方式来实现完整性,它能够为数据生成独一无二的「指纹」,指纹用于校验数据的完整性,解决了篡改的风险。
- 身份证书:将服务器公钥放入到数字证书中,解决了冒充的风险
HTTPS 是如何建立连接的?
SSL/TLS 协议基本流程:
- 客户端向服务器索要并验证服务器的公钥。
- 双方协商生产「会话秘钥」。
- 双方采用「会话秘钥」进行加密通信。
前两步也就是 SSL/TLS 的建立过程,也就是 TLS 握手阶段。
TLS的握手,涉及四次通信。不同密钥交换算法,握手流程也不一样。
TLS协议流程:
- ClientHello,发送:
- TLS版本,
- client的随机数 client random,
- client支持的密码套件列表(比如 : RSA加密算法)
- ServerHello:
- 确认版本,如不支持,则关闭
- server生成随机数 server random
- 确认密码套件列表
- 服务器的数字证书
- 客户端回应:
- 再生成一个随机数 pre-master key
- 加密通信算法改变通知,表示随后的信息会加密
- 客户端通知握手结束
这个时候服务器和客户端有了三个随机数,接下来就用双方协商的加密锁算法,生成本次通信密钥
- 服务器最后回应
- 通知握手结束
HTTPS的应用程序如何保证完整性。
数据经过切片、压缩和加密。转交给TCP层传输。
一个问题:HTTPS是安全可靠的吗?
如果有中间人怎么办。 答案是确实安全可靠。
HTTP/1.1、HTTP/2、HTTP/3演变
HTTP/2(相比HTTP/1.1):
-
头部压缩
-
完全二进制格式
-
多个流并发传输:用 stream id 来表示每个流,用奇偶表示客户端与服务端。
-
服务器主动推送资源
-
仍然存在队头阻塞
HTTP/3:
-
为了解决队头阻塞,把 TCP 换成了 UDP,用基于 UDP 实现的 QUIC(Quick UDP Internet Connections)协议,可以实现类似的可靠性传输。
QUIC:
-
无队头阻塞:类似多路复用,可在一个连接上并发传输多个stream,每个stream都可以认为是一个HTTP请求。当丢包时,只会一个流受到阻塞。
-
更快连接建立:因为内核实现的传输层和openssl实现的表示层难以合并在一起,所以TLS在TCP之后。
QUIC可以用一个RTT完成连接建立和密钥协商、
-
连接迁移:没有使用四元组的方式绑定连接,而是用连接ID标记。迁移时无需重新连接。
QUIC 是一个在 UDP 之上的伪 TCP + TLS + HTTP/2 的多路复用的协议。
-
HTTP/1.1优化
- 减少重定向请求次数:让代理服务器得知即可,而无需让客户端得知。
- 合并请求:把多个小请求合并成一个大请求。
- 延迟发送请求:等用到了在解析URL。
HTTPS RSA 握手解析
RSA算法不支持前向保密。
HTTPS ECDHE 握手解析
ECDHE支持前向保密,每次生成一个临时的密钥,通过椭圆曲线加速计算。
HTTPS优化
-
会话复用。
-
Session ID
客户端和服务器首次 TLS 握手连接后,双方会在内存缓存会话密钥,并用唯一的 Session ID 来标识,Session ID 和会话密钥相当于 key-value 的关系。
当客户端再次连接时,hello 消息里会带上 Session ID,服务器收到后就会从内存找,如果找到就直接用该会话密钥恢复会话状态,跳过其余的过程,只用一个消息往返就可以建立安全通信。当然为了安全性,内存中的会话密钥会定期失效。
问题:
不一定命中,因为服务器是负载均衡的。
每一个客户端维护密钥,内存压力大。
-
Session Ticket
把缓存的工作交给客户端。再次连接的时候让客户端发送Ticket即可。
问题在于可能会出现重放攻击。需要为密钥设定一个合理的过期时间。
HTTP/2优点
- 兼容HTTP/1.1 没有引入新的协议名,只在应用层做了改变。
-
HTTP/3
有了HTTP,为什么还有RPC?
为什么有了HTTP,还有WebSocket?
-
HTTP是客户端向服务器发起请求,而不是服务器向客户端发东西,因此有些时候不方便,比如玩游戏。
-
HTTP把一个全双工的TCP改成了半双工,因为设计之初就没考虑玩游戏这种情况。
-
WebSocket和Socket没一点关系
-
怎么建立WebSocket?为了兼容,最初都用HTTP,在HTTP头中,添加一些字段,表示协议升级为WebSocket
-
WebSocket是基于TCP的。是全双工的。
TCP篇
TCP基本认识
TCP头格式
TCP与UDP区别:
-
连接:TCP连接,传输之前要连接;UDP不需要
-
服务对象:TCP一对一;UDP可以一对多,多对多
-
可靠性:TCP可靠,UDP不可靠
-
拥塞控制、流量控制:TCP有,UDP没有
-
首部开销:TCP长,UDP短。TCP有可选变长首部,UDP没有
-
传输方式:TCP是流式没有边界;UDP是一个包一个包,有边界;
-
分片不同:
TCP 连接建立
TCP 和 UDP 可以使用同一个端口吗
可以的。
操作系统用 : [协议号(传输层),源IP,源端口,目标IP,目标端口] 来标记一个
在一个机器上:不同协议可以同时共用一个端口,但是同一个端口不能被同一个协议同时绑定两次。
-
TCP三次握手主要是阻止历史连结,而不是两次或者四次
-
如何在Linux系统查看TCP状态? 使用netstat -napt
-
为什么每次初始化TCP连接,初始的序列号都不一样。为了避免历史报文。
-
IP层MTU会分片,为什么TCP还需要MSS。
那么当如果一个 IP 分片丢失,整个 IP 报文的所有分片都得重传。所以让IP层分片是没有效率的。
-
第一次握手丢失了,会发生什么? 会进行等待超时,重发(重发的SYN号一样),每次超时都会加倍等待时间,直到超时次数过多(达到操作系统设置值)就会断开连接。
-
第二次握手丢失了,会发生什么? 第二次握手丢失的时候,服务器(发送SYN-ACK)和客户端(发送SYN)都会重发。
- 第三次握手丢失了,会发生什么? 第三次握手是客户端给服务器发一个SYN-ACK的ACK,如果丢失,那么服务器会重发 SYN-ACK而不是客户端重发,因为ACK丢失是不会重发的。
TCP 连接断开
主动关闭连接的一方采用TIME_WAIT状态
客户端的FIN_WAIT_2和服务端的CLOSED_WAIT等待的时间是服务器的结尾收拾。
第一次挥手丢失了,会发生什么?
如果客户端不断丢失重发,达到TCP重发次数之后,再等待一段时间,最后客户端会单方面断开连接。
-
第二次挥手丢失了,会发生什么?
ACK不重发,客户端会进行重发,然后如果不断丢失,客户端可能会单方面断开连接。
-
第三次挥手丢失了,会发生什么?
服务器不断重发,最后仍然超时,如果一直收不到客户端的ACK就断开连接。
客户端不断等待,超时,则断开。
-
第四次挥手丢失了,会发生什么?
ACK丢失,客户端不重传,则服务器重传同时重置客户端的2MSL定时器,直到服务器超过次数,再等待2MSL,关闭。
-
为什么TIME_WAIT等待时间是2MSL? 2MSL是包在网络上存在的最大生存时间,超过这个时间就会被丢弃,2MSL就是来回的时间。TTL是路由器跳的次数。
-
为什么需要TIME_WAIT状态?
防止历史连接中的数据,因为传输延迟,被后面相同四元组的连接错误接受。
等待足够时间,确保最后的ACK能够让服务器接收。
-
TIME_WAIT过多的危害?
占用系统资源。占用端口资源
-
TIME_WAIT状态过多的原因?
HTTP没有使用长连接
HTTP长连接超时:大量客户端都一直没有发送数据,导致服务端主动关闭,处于TIMWE_WAIT
HTTP长连接的请求数量达到上限:数量达到上限,导致不断地关闭连接。
-
服务器出现大量CLOSE_WAIT状态的原因?
CLOSE_WAIT是被动关闭方才用的状态。如果被动关闭方没有调用close函数,就没办法发送FIN报文进行第三次握手,所以没办法使 CLOSE_WAIT 转化为LAST_ACK状态。很可能是代码出了问题。
-
如果已经建立了连接,但是客户端宕机了怎么办?
注意是客户端宕机,而不是进程结束。客户端宕机无法感知,但是进程结束会由内核发送FIN。
如果两方一直都没有发送数据,TCP有一个保活机制,如果长时间不发数据,保活机制开始工作。每隔一段时间就发很少量的一段消息,如果一直没有回应,就会关闭连接。
如果是服务器发送数据,但是一直没有回应,也就是没有ACK的话,那么TCP会通过一样的流程。
-
如果建立了连接,但是服务端的进程崩溃会发生什么?
进程崩溃,会由内核进行四次挥手。但如果直接崩溃,则和上面一个问题一样。
Socket编程
-
listen的时候backlog的意义?
也就是从listen到accept这之间,把来的请求阻塞住。
-
accept发生在三次握手的哪一步?
由图,客户端connect成功返回发生在第二次握手,服务端accept成功返回发生在第三次握手成功之后。
-
客户端调用 close 了,连接断开的流程是什么?
-
没有accept可以建立TCP连接吗?
可以,accept 不参与TCP三次握手,只是负责从TCP全连接队列中取出一个已经连接的socket,用户层通过accept调用拿到已经建立连接的 socket,就可以对socket读写了。
-
没有listen,可以建立TCP连接吗?
可以。
客户端是可以自己连自己的形成连接(TCP自连接),也可以两个客户端同时向对方发出请求建立连接(TCP同时打开),这两个情况都有个共同点,就是没有服务端参与,也就是没有 listen,就能 TCP 建立连接。
-
TCP重传、滑动窗口、流量控制、拥塞控制
重传机制
- 超时重传:问题是等待太浪费时间了。
- 快速重传:如果没收到而后面的包先来了,则发送相同的ACK。发送端便知道有一个包丢失了。但是不好判断重传多少。
-
SACK方法:选择性重发。需要在头部添加一个SACK字段
滑动窗口
图中的 ACK 600 确认应答报文丢失,也没关系,因为可以通过下一个确认应答进行确认,只要发送方收到了 ACK 700 确认应答,就意味着 700 之前的所有数据「接收方」都收到了。这个模式就叫累计确认或者累计应答。
窗口大小一般由接收端决定,防止接收端处理不过来导致不断重发。
发送方窗口:
接收方窗口:
流量控制
窗口大小根据接收方需求动态调整。
TCP设定一个定时器,如果超时,则发送窗口探测报文。
糊涂窗口综合征
使用 Nagle 算法,该算法的思路是延时处理,只有满足下面两个条件中的任意一个条件,才可以发送数据: 条件一:要等到窗口大小 >= MSS 并且 数据大小 >= MSS; 条件二:收到之前发送数据的 ack 回包;
拥塞控制
慢启动
假设拥塞窗口cwnd和发送窗口swnd相等
一开始cwnd=1,收到ACK加倍增长。
达到慢启动门限ssthresh转为使用拥塞避免算法。
拥塞避免算法
当达到ssthresh之后,每收到一个ACK,cwnd增加1
一直增长,网络慢慢拥塞,出现丢包时,需要重发。
重发,出发拥塞发生算法。
拥塞发生算法
超时重传:
ssthresh 设置为 cwnd / 2,cwnd重置为 初始值 1
快速重传:
认为情况不严重。
cwnd=cwnd / 2, ssthresh = cwnd。
进入快速回复算法。
快速恢复算法
cwnd = ssthresh + 3
重传丢失数据包。
每收到重复的ACK,cwnd+1
如何理解TCP时面向字节流协议?
UDP协议传输时,操作系统不会对消息进行拆分。组装号UDP头部之后直接发送出去。数据部分就是完整的用户数据,接收方收到一个UDP报文就能读到一个完整的用户消息。而在内核中一个UDP报文,就是一个队列元素,不管顺序等。
TCP协议则会被OS拆分成多个报文。接收方无法得知消息的边界。必须自定义信息格式以保证消息的边界。也就是会粘包。
TCP KeepAlive 和 HTTP Keep-Alive 是一个东西吗?
TCP的KeepAlive是TCP层,内核完成的,是保活机制。
HTTP的Keep-Alive是长连接的含义。
TCP协议有什么缺陷?
- 升级TCP很困难:因为它是写在操作系统内核中的。
- TCP 建立连接的延迟很高:特别是HTTPS还添加了TLS层的握手
- TCP 存在队头阻塞问题:因为TCP是面向字节流的,所以只要一个字节丢失,就不会向上传递。
- 网络迁移需要重新建立 TCP 连接:因为TCP是 [源IP,源端口,目标IP,目标端口],当换网络的时候,IP会改变,需要重新建立。
如何基于UDP实现可靠传输
-
QUIC这么做了。
- 意义在于:
- 解决TCP升级困难,因为是实现在应用层
- 减少了建立连接的延迟
- 解决队头阻塞,可以并发
- 解决网络迁移需要重新建立,可以用唯一标识符代替四元组。
- 意义在于:
TCP 和 UDP 可以使用同一个端口吗?
可以。
内核以 [协议号,源IP,源端口,目标IP,目标端口]标识,但是不能TCP同时有两个进程绑定同一个端口。
服务端没有listen,客户端发起连接请求,会发生什么?
如果listen了,并且指定backlog,那么肯定客户端请求会存在队列中。
但是如果没有listen,内核找不到对应的socket,会由内核发出关闭连接的操作。
没有listen,可以建立TCP连接吗?
可以。
一个进程自己connect自己。
bind -> connect -> send -> recv
TCP 四次挥手,可以变成三次吗?
可以。
四次挥手的原因是:
客户端发送FIN,内核会回复ACK。这个时候服务端需要处理数据或者发送一些数据。之后才第三次挥手。
如果服务端不需要再继续发送数据,那么可以直接第二次挥手和第三次挥手结合。但是需要阻止内核直接回复ACK。
这是一个参数,TCP_QUICKACK,关闭这个参数,即可延迟确认。
那么这个时候就可以让服务器ACK和FIN一起发送,减少一次挥手。
TCP 序列号和确认号是如何变化的?
IP篇
IP基本知识
IP是网络层,目的是把数据发给目标主机
MAC是链路层,目的是把数据发给相邻主机
广播地址:主机部分全一
本机地址:主机部分全0
网络号为127.0.1:回环地址
无分类地址 CIDR
a.b.c.d/x 用x表示网络号与主机号之间的划分。
x可以用来划分子网。使用子网掩码来进行子网划分
更高效的利用IP地址
ping 的工作原理 - ICMP
ICMP协议:IP基于ICMP工作。
ICMP 主要的功能包括:确认 IP 包是否成功送达目标地址、报告发送过程中 IP 包被废弃的原因和改善网络设置等。
ICMP类型分为两类,一类是查询报文类型,一类是差错报文类型。
traceroute-差错报文的使用
- traceroute 的第一个作用就是故意设置特殊的 TTL,来追踪去往目的地时沿途经过的路由器。
- traceroute 还有一个作用是故意设置不分片,从而确定路径的 MTU。
localhost、127.0.0.1、0.0.0.0与本机IP的区别
127.0.0.1 就是回环地址,表示本机
localhost 是一个域名,写在 hosts.txt 中,就是 127.0.0.1
0.0.0.0
是不能被ping
通的。在服务器中,0.0.0.0
并不是一个真实的的IP地址,它表示本机中所有的IPV4地址。监听0.0.0.0
的端口,就是监听本机中所有IP的端口。
本机IP地址,就是当前这个主机的IP地址。