序列化和反序列化

序列化就是将对象转换成二进制数据的过程,以方便传输或存储。而反序列就是将二进制转换为对象的过程。
比如 网络传输 必须是二进制,但调用方请求的出入参数都是对象。就需要转换过程,如JSON对象和二进制互转,这个过程就是序列化和反序列化。
JDK 原生序列化。

HTTP

特点

无状态,双向传输基于TCP/IP,灵活,明文传输不安全

状态码

1xx 提示信息 2xx 成功 3xx 重定向 4xx客户端请求错误 5xx服务器错误
302 重定向
401 权限不足,需要授权
403 资源拒绝访问,需要登录
502 网关错误
503 超时

常见字段

Host 指定服务器域名
Content-Length 长度
Connection:keep-alive 长连接

HTTP1.1

早起1.0 每次请求都要一次TCP三次握手;
1.为了解决该问题,1.1 提出了长连接,
通过header 配置Connection:keep-alive ,建立CS持久连接,
减少建立和断开的额外开销;
2.长连接引入,使得管道pipeline网络传输成为可能,客户端可实现连续发请求,无需等待响应的阻塞其他的请求。但是,服务器依然按照顺序回应,必须一个一个处理回应,如果前面回应慢,引起 队头阻塞。

1.1性能一般般,需要HTTP的优化。

HTTPS 解决HTTP什么问题?

解决明文传输的问题,HTTPS 在HTTP与TCP层之间加入 SSL/TLS 协议。一是把信息加密,解决窃听风险,二是校验机制,解决篡改信息风险,三是身份认证,解决冒充网站风险。

具体来说,HTTPS是 如何解决呢?
一个是混合加密,采用对称加密和非对称加密结合,保证信息密文
对称加密:只有一个密钥,运算快,密钥保密,无法安全密钥交换。
非对称加密:两个密钥,公钥任意分发,私钥保密,能交换密钥但速度慢。

建立通信前,非对称;通信过程,对称

二是摘要算法,用于签名,为数据生成唯一的指纹,校验数据完整性,防篡改。
摘要算法一般是不可逆的 ,散列函数与md5与sha系列。客户端在发送前,会通过摘要算法对明文计算,算出一个摘要指纹,把【摘要+明文】加密;在发送后,服务器解密得到【摘要+明文】,用同一个摘要算法对明文计算,算出摘要,并比对传过来的摘要是否想等。

三是数字证书
客户端先向服务器索取公钥,然后用公钥机密,传入服务器给私钥解密。
虽然摘要算法保证了数据不被篡改,但是,如何保证公钥不被篡改?
这个需要第三方权威机构CA,颁发数字证书,将服务器公钥放在数字证书。只要证书可信,公钥就是可信。
CA机构是分级的,树型,最顶是根CA,必须相信。
如何申请?
我们首先要知道,CA有自己的私钥,每个客户端都内置
已信任的CA的公钥。
1,服务器吧自己公钥注册给CA,
2,CA用自己私钥给公钥签名并颁发数字帐户上,
3,第一次客户端请求服务器,会拿到数字证书,会使用事先已内置浏览器或OS的CA公钥,校验数字证书真实性。
4,认证证书后,就获取服务器的公钥,加密报文数据,给服务私钥解密。

Q:HTTPS是如何建立连接的?期间交互了什么?
SSL/TLS 协议流程:
基本流程:C向S索取公钥,双方协商生成会话密钥,双方密钥通信。
前两步就是握手阶段。
详细流程:
握手四次通信:
1,ClientHello:客户端发送加密通信请求,这一步,客户端主要向服务端发送:TLS版本,客户端随机数(用于生产会话密钥),支持的加密算法列表

2, SeverHello,服务器回应:1,确认TLS版本,若浏览器不支持,关闭加密通信;2,服务端随机数(用于生产会话密钥),3,确认加密算法列表,4,数字证书。

3,客户端回应
首先通过客户端CA公钥校验证书,取出公钥,加密报文,向服务器发送:随机数(会被服务器公钥加密),加密通信算法改变通知(表示之后都是会话密钥通信),握手结束通知(表示客户端握手阶段结束,这里还会把数据做个摘要,发送给服务端校验)

  1. 服务器的最后响应
    这个过程产生3个随机数,通过加密算法的协商后,计算出本次通信的会话密钥。然后,向客户端发送最后端消息:
    1,加密算法改变通知(之后都是会话密钥通信);
    2,服务端握手结束通知,表示服务端握手已结束(这里同时会把数据做个摘要,给客户端校验)。

接下来就是,用会话密钥加密的HTTP请求。

HTTP1.1 /HTTP2.0/HTTP3 演变;

首先,将HTTP1.1 相比HTTP1.0 增加 TCP长连接,支持管道传输,节省了频繁建立和断开TCP的性能开销,减少多次请求等待的响应时间。
但是,有1.1的问题有三个:
一是数据太大,只压缩body,占用带宽;
二是队头阻塞,服务器顺序响应,一旦响应过慢,导致后续阻塞。
三是单向请求,不适应服务器推送场景。

那么,针对这些问题,HTTP2 进行改进:
一是头部压缩,如果头相同或相似,协议会自己消除重复,这是HPACK算法,实现原理是:客户端和服务端同时维护一张头表信息,所有字段都会存入这个表,生成一个索引号,只传输索引号即可,提高速度;
二是纯二进制格式,1.1 只是文本,头和主体用空格隔开,2.0头帧和数据帧都是二进制,也是提高传输速度。
三是数据流发送,不再像1.1顺序发送顺序响应,而是对数据包做标记,指出它属于哪个回应,同一连接发送连续数据包,每个请求或回应的所有数据包,统称为stream流。

三是数据流
每个数据流都标记着一个独一无二的编号,其中规定客户端发出的数据流编号为奇数, 服务器发出的数据流编号为偶数

客户端还可以指定数据流的优先级。优先级高的请求,服务器就先响应该请求。

四是多路复用
http2在一个连接里并发处理请求或回应,不再是按照顺序对应,不需排队,就没有队头阻塞问题。

五是服务器推送
增加新功能,不再是请求-答应模式,而是服务器主动推送消息。
比如,初始化HTML是,可以主动推送静态JS,CS到客户端,减少延迟等待。就是Server Push,可减少了多个RTT。

http2 缺陷:
多个HTTP请求复用1个TCP连接,下层的TCP协议不知道有多少个HTTP请求;
一旦丢包,触发TCP重传机制,此时,HTTP必须等待丢的包被重新传回来,这样阻塞了所有HTTP请求。

HTTP3 改成了UDP!
UDP不管顺序,不管丢包,不会出现队头阻塞,也不会出现丢包全部重传问题。

UDP不可靠,那么怎么实现类似TCP可靠呢?
QUIC协议保证。当某个流发生丢包,会阻塞这个流,其他流不受影响。然后是TLS1.3 把6次TCP+TLS握手合并为3次QUIC。
QUIC是一个UDP之上的伪TCP+TLS+HTTP2的多路复用协议。

Q:一次请求发生了什么?

一是对URL解析,来确定是发送给WEB服务器请求。
包括协议头,服务器host,uri路径。
二是查询真真实IP,使用DNS服务器解析。在发送消息前,委托系统查询DNS服务器域名的真实IP。DNS服务器具有树状的层级,根域服务器保存所有域名,顶级域名服务器更少,只保存com或cn后缀的,权威域名保存更少,同时服务器数量也更多。
解析过程:客户端发出DNS请求,询问IP,在本地DNS服务器找,如果有自己返回IP,如果没有,委托根域名,根域名委托对应的顶级域名服务器,顶级域名服务器委托对应的权威域名服务器。权威域名服务器找到IP,告诉本地DNS缓存。

三是协议栈 封装

输入图片描述

输入图片描述


通过DNS获取IP后,就可以把HTTP的传输工作交给OS的协议栈。
协议栈的内部分为几部分,分别承担不同工作,上下关系有一定规则,上面会向下委托公,下面收到委托并执行。

应用程序通过调用socket 库,来委托协议栈工作,协议栈的上方分别是收发数据的TCP和UDP协议,下方是IP协议控制网络包的收发操作,在互联网上传数据时,数据会被切成一块块的网络包,而将网络包发送给对方的操作就是有IP负责。

IP包括ICMP协议 和ARP协议。
ICMP时告知网络包传输过程中产生的错误和各种控制信息。
ARP 是根据IP地址查询相应的以太网MAC地址。

IP下面是网卡驱动程序,负责控制网卡硬件。最下是网卡硬件,真正的网络信号的收发操作

四,细看,可靠传输TCP
先讲讲TCP,序号解决包乱序,校验和解决完整性,窗口大小声明窗口大小,解决两端流量处理能力的控制。此外,拥塞控制,控制发送的速度。

在 HTTP 传输数据之前,首先需要 TCP 建立连接,TCP 连接的建立,通常称为三次握手

这个所谓的「连接」,只是双方计算机里维护一个状态机,在连接建立的过程中,双方的状态变化时序图就像这样。

  • 一开始,客户端和服务端都处于 CLOSED 状态。先是服务端主动监听某个端口,处于 LISTEN 状态。

  • 然后客户端主动发起连接 SYN,之后处于 SYN-SENT 状态。

  • 服务端收到发起的连接,返回 SYN,并且 ACK 客户端的 SYN,之后处于 SYN-RCVD 状态。

  • 客户端收到服务端发送的 SYNACK 之后,发送 ACKACK,之后处于 ESTABLISHED 状态,因为它一发一收成功了。

  • 服务端收到 ACKACK 之后,处于 ESTABLISHED 状态,因为它也一发一收了。

所以三次握手目的是保证双方都有发送和接收的能力

查看连接命令: netstat - napt 命令

TCP 分割数据

如果HTTP消息过长,超过了MSS的长度。
TCP 就要把HTTP的数据拆解一块块的数据发送,而不是一次性发送。

  • MTU:一个网络包的最大长度,以太网中一般为 1500 字节。
  • MSS:除去 IP 和 TCP 头部之后,一个网络包所能容纳的 TCP 数据的最大长度。

数据会被拆为MSS长度的单位,并加上TCP头,然后交给IP模块发送数据。

TCP会有两个端口,一个是浏览器监听 的短裤,随机,一个是服务器监听的80或443

输入图片描述

输入图片描述

双方建立连接后,TCP报文的数据部分就是存放HTTP头+数据,组装好TCP报文之后,要交给下面的网络层处理

远程定位 —- IP

TCP 模块在执行连接、收发、断开等各阶段操作时,都需要委托 IP 模块将数据封装成网络包发送给通信对象。

假设客户端有多个网卡,就会有多个 IP 地址,那 IP 头部的源地址应该选择哪个 IP 呢?

当存在多个网卡时,在填写源地址 IP 时,就需要判断到底应该填写哪个地址。这个判断相当于在多块网卡中判断应该使用哪个一块网卡来发送包。

这个时候就需要根据路由表规则,来判断哪一个网卡作为源地址 IP。

两点传输 — MAC

在 MAC 包头里需要发送方 MAC 地址接收方目标 MAC 地址,用于两点之间的传输

一般在 TCP/IP 通信里,MAC 包头的协议类型只使用:

  • 0800 :IP 协议

  • 0806 :ARP 协议

先得搞清楚应该把包发给谁,这个只要查一下路由表就知道了。在路由表中找到相匹配的条目,然后把包发给 Gateway 列中的 IP 地址就可以了。

既然知道要发给谁,按如何获取对方的 MAC 地址呢?

不知道对方 MAC 地址?不知道就喊呗。

此时就需要 ARP 协议帮我们找到路由器的 MAC 地址。

  • 先查询 ARP 缓存,如果其中已经保存了对方的 MAC 地址,就不需要发送 ARP 查询,直接使用 ARP 缓存中的地址。

  • 而当 ARP 缓存中不存在对方 MAC 地址时,则发送 ARP 广播查询。如果对方和自己处于同一个子网中,那么就可以得到对方回应的 MAC 地址。

    输入图片描述

    输入图片描述

    出口— 网卡

    IP 生成的网络包只是存放在内存中的一串二进制数字信息,没有办法直接发送给对方。因此,我们需要将数字信息转换为电信号,才能在网线上传输,也就是说,这才是真正的数据发送过程。
    负责执行这一操作的是网卡,要控制网卡还需要靠网卡驱动程序

网卡驱动从 IP 模块获取到包之后,会将其复制到网卡内的缓存区中,接着会其开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列FCS

送别者—-交换机

交换机的设计是将网络包原样转发到目的地。交换机工作在 MAC 层,也称为二层网络设备

首先,电信号到达网线接口,交换机里的模块进行接收,接下来交换机里的模块将电信号转换为数字信号。

然后通过包末尾的 FCS 校验错误,如果没问题则放到缓冲区。这部分操作基本和计算机的网卡相同,但交换机的工作方式和网卡不同。

计算机的网卡本身具有 MAC 地址,并通过核对收到的包的接收方 MAC 地址判断是不是发给自己的,如果不是发给自己的则丢弃;相对地,交换机的端口不核对接收方 MAC 地址,而是直接接收所有的包并存放到缓冲区中。因此,和网卡不同,交换机的端口不具有 MAC 地址

将包存入缓冲区后,接下来需要查询一下这个包的接收方 MAC 地址是否已经在 MAC 地址表中有记录了。

交换机的 MAC 地址表主要包含两个信息:

  • 一个是设备的 MAC 地址,

  • 另一个是该设备连接在交换机的哪个端口上。

交换机根据 MAC 地址表查找 MAC 地址,然后将信号发送到相应的端口

当 MAC 地址表找不到指定的 MAC 地址会怎么样?

这种情况下,交换机无法判断应该把包转发到哪个端口,只能将包转发到除了源端口之外的所有端口上,无论该设备连接在哪个端口上都能收到这个包。只有相应的接收者才接收包,而其他设备则会忽略这个包

出境大门 — 路由器

路由器是基于IP设计,俗称三层网路设备,各个端口都有MAC地址和IP地址;
而交换机是基于以太网设计,俗称二层网络设备,端口不具备MAC地址。

当转发包时,首先路由器端口会接收发给自己的以太网包,然后路由表查询转发目标,再由相应的端口作为发送方将以太网包发送出去。

首先,电信号到达网线接口部分,路由器中的模块会将电信号转成数字信号,然后通过包末尾的 FCS 进行错误校验。

如果没问题则检查 MAC 头部中的接收方 MAC 地址,看看是不是发给自己的包,如果是就放到接收缓冲区中,否则就丢弃这个包。

完成包接收操作之后,路由器就会去掉包开头的 MAC 头部。

MAC 头部的作用就是将包送达路由器,其中的接收方 MAC 地址就是路由器端口的 MAC 地址。因此,当包到达路由器之后,MAC 头部的任务就完成了,于是 MAC 头部就会被丢弃

接下来,路由器会根据 MAC 头部后方的 IP 头部中的内容进行包的转发操作。

接下来就会进入包的发送操作

首先,我们需要根据路由表的网关列判断对方的地址。

  • 如果网关是一个 IP 地址,则这个IP 地址就是我们要转发到的目标地址,还未抵达终点,还需继续需要路由器转发。

  • 如果网关为空,则 IP 头部中的接收方 IP 地址就是要转发到的目标地址,也是就终于找到 IP 包头里的目标地址了,说明已抵达终点

知道对方的 IP 地址之后,接下来需要通过 ARP 协议根据 IP 地址查询 MAC 地址,并将查询的结果作为接收方 MAC 地址。

路由器也有 ARP 缓存,因此首先会在 ARP 缓存中查询,如果找不到则发送 ARP 查询请求。

网络包完成后,接下来会将其转换成电信号并通过端口发送出去。这一步的工作过程和计算机也是相同的。

发送出去的网络包会通过交换机(城门)到达下一个路由器。由于接收方 MAC 地址就是下一个路由器的地址,所以交换机会根据这一地址将包传输到下一个路由器。(服务器内网)

接下来,下一个路由器会将包转发给再下一个路由器,经过层层转发之后,网络包就到达了最终的目的地。

不知你发现了没有,在网络包传输的过程中,源 IP 和目标 IP 始终是不会变的,一直变化的是 MAC 地址,因为需要 MAC 地址在以太网内进行两个设备之间的包传输。

互相扒皮— 服务器 与 客户端

应用层 HTTP
传输层 TCP
网络层 IP
链路层 以太网MAC
从高到低封装,从低到高拆解。

当 扒开 TCP 的头,里面有序列号,需要看一看这个序列包是不是我想要的,如果是就放入缓存中然后返回一个 ACK,如果不是就丢弃。TCP头部里面还有端口号, HTTP 的服务器正在监听这个端口号。

于是,服务器自然就知道是 HTTP 进程想要这个包,于是就将包发给 HTTP 进程。

———————
服务器的 HTTP 进程看到,原来这个请求是要访问一个页面,于是就把这个网页封装在 HTTP 响应报文里。

HTTP 响应报文也需要穿上 TCP、IP、MAC 头部,不过这次是源地址是服务器 IP 地址,目的地址是客户端 IP 地址。

穿好头部衣服后,从网卡出去,交由交换机转发到出城的路由器,路由器就把响应数据包发到了下一个路由器,就这样跳啊跳。

最后跳到了客户端的城门把手的路由器,路由器扒开 IP 头部发现是要找城内的人,于是把包发给了城内的交换机,再由交换机转发到客户端。

客户端收到了服务器的响应数据包后,同样也非常的高兴,客户能拆快递了!

扩展 ping原理 ICMP协议

也就是互联网控制报文协议ICMP 主要的功能包括:确认 IP 包是否成功送达目标地址、报告发送过程中 IP 包被废弃的原因和改善网络设置等。

细说 TCP

首先,介绍TCP 头部格式。

输入图片描述

输入图片描述

序列号:在建立连接时由计算机生成的随机数作为初始值,通过SYN包传输给接收端,每一次累加该数据字节的大小,解决乱序问题。

确认应答号:指下一次期望收到的数据的序列号,用来解决不丢包问题。

控制位:确认答应,异常断开,chu,结束

Q:为什么需要 TCP 协议?TCP 工作在哪一层?

输入图片描述

输入图片描述

因为 TCP 是一个工作在传输层可靠数据传输的服务,它能确保接收端接收的网络包是无损坏、无间隔、非冗余和按序的。

IP 层是「不可靠」的,它不保证网络包的交付、不保证网络包的按序交付、也不保证网络包中的数据的完整性。

Q:什么是 TCP ?

TCP 是面向连接的、可靠的、基于字节流的传输层通信协议。

  • 面向连接:一定是「一对一」才能连接,不能像 UDP 协议 可以一个主机同时向多个主机发送消息,也就是一对多是无法做到的;

  • 可靠的:无论的网络链路中出现了怎样的链路变化,TCP 都可以保证一个报文一定能够到达接收端;

  • 字节流:消息是「没有边界」的,所以无论我们消息有多大都可以进行传输。并且消息是「有序的」,当「前一个」消息没有收到的时候,即使它先收到了后面的字节已经收到,那么也不能扔给应用层去处理,同时对「重复」的报文会自动丢弃。

Q:什么是TCP连接?

简单来说就是,用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket、序列号和窗口大小称为连接。

Q:如何唯一确定一个TCP连接?

TCP 四元组可以唯一的确定一个连接,四元组包括如下:

  • 源地址

  • 源端口

  • 目的地址

  • 目的端口

Q:有一个 IP 的服务器监听了一个端口,它的 TCP 的最大连接数是多少?

服务器通常固定在某个本地端口上监听,等待客户端的连接请求。

因此,客户端 IP 和 端口是可变的,其理论值计算公式如下:
最大TCP连接数 = 客户端IP数 x 客户端端口数
对于IPv4,2的32次方IP ,2的16次方端口,最大TCP连接数是2的48次方。

这是理论值,实际上:
Linux 文件描述符限制,socket是fd文件,打开句柄 1024,这个可调。
另一个是内存限制,每个TCP连接占用一定的内存。

Q:UDP和TCP区别与场景?

UDP 不提供控制机制,利用IP提高的无连接通信
UDP 协议简单,头部8字节,分别是 16位源端口,16位目标端口,16位包长度,16位校验和

区别:
1,连接
TCP面向连接,传输数据前先建立连接
UDP无连接
2,服务对象
TCP连接一对一
UDP一对一,一对多,多对多广播
3,可靠性
TCP可靠,校验和保证无差错,确认答应号保证不丢失,序号保证不重复,顺序性,
UDP不可靠,只是尽最大努力交付
4,拥塞控制,流量控制
TCP拥塞控制和流量控制,保证数据传输多安全性;
UPD即使网络非常堵塞,也不影响UDP发送速率
5,首部开销
TCP首部长度较长,不包括【选项】,20字节;
UDP首部只有8字节

6,应用场景
TCP:FTP文件传输 HTTP/HTTPS
UDP:DNS,视频,音频,广播通信

Q:为什么 UDP 头部没有「首部长度」字段,而 TCP 头部有「首部长度」字段呢?

原因是 TCP 有可变长的「选项」字段,而 UDP 头部长度则是不会变化的,无需多一个字段去记录 UDP 的首部长度。

选项 常见:MSS最长报文大小

Q:为什么是3次握手?不是两次,四次?

相信大家比较常回答的是:“因为三次握手才能保证双方具有接收和发送的能力。”但是,回答过于片面。
首先,了解TCP连接的连接概念。

  • 用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket、序列号和窗口大小称为连接。

那么,重要的是为什么三次握手才可以初始化Socket、序列号和窗口大小并建立 TCP 连接

接下来,我从3个方面回答:

  • 三次握手才可以阻止历史重复连接的初始化(主要原因)

  • 三次握手才可以同步双方的初始序列号

  • 三次握手才可以避免资源浪费

原因一:避免历史连接
简单来说,三次握手的首要原因是为了防止旧的重复连接初始化造成混乱

如果旧SYN报文比当前SYN报文先抵达到服务端,服务端会返回SYN+ACK,客户端根据自身上下问题,判断是旧连接,那么客户端发送RST报文,表示中断这一次连接。舍弃之前的,重新开始新的SYN。

如果是两次握手,服务端就不能判断出历史连接而中断。

  • 如果是历史连接(序列号过期或超时),则第三次握手发送的报文是 RST 报文,以此中止历史连接;

  • 如果不是历史连接,则第三次发送的报文是 ACK 报文,通信双方就会成功建立连接;

原因二:同步双方初始序列号
TCP协议双方,都必须维护一个序列号,序列号是可靠传输的关键:可以去重,可以顺序接收,可以表示哪些数据包已经被对方接收。

第一次客户端发送初始序列号SYN,服务端需要ACK回应,同时发生自己的出生序列号SYN,客户端当然也要回应接收。
这样来回,次确保双方初始序列号被对方知晓。

原因三:避免资源浪费

如果只有两次握手,服务器ACK不知道客户端是否已经接收,这就导致服务端每次收到SYN就主动建立一个连接,这就有个问题,如果客户端SYN传输被网络阻塞,而引起了超时重发,之后阻塞的SYN也终于发送到服务端,意味着服务端接收了2次相同的SYN,服务器会重复处理请求,造成重复分配资源。

小结:三次握手是为了防止历史连接的 建立,帮助双方初始化序列号,减少重复资源的开销。
两次握手无法做到以上这些,四次握手显得多余。

Q:为什么客户端和服务端的初始序列号ISN不相同?

报文延迟,复制重发,丢失等造成不同连接之间互相影响,客户端和服务端初始化序列号不同,是为了兼容这些问题环境。

Q:IP层会分片,TCP为什么要MSS?

首先介绍MTU 和 MSS。
MTU: 一个网络包的最大长度,以太网中一般位1500字节。
MSS:一个减去IP 和TCP 头部的网络包数据的最大长度

如果仅TCP报文交给IP分片,IP层超过一个MTU大小的数据发送,就要进行分片,把数据分片成若干片,保证每一个分片都小于MTU。分片后,由目标主机的IP层重新组装,再交给TCP传输层。

这里存在一个隐患,如果一个IP分片丢失,就需要整个IP报文的所有分片重传,因为IP层没有超时重传机制,那么,它由传输层TCP来负责超时和重传,接收方发现缺失,不会响应ACK,而是等对方超时后,重发整个TCO报文。

这样,非常无效率。

所以,TCP建立连接时,双方协商MSS值,当TCP层发现数据超过MSS时,就会先分片,他肯定不会大于MTU。IP自然不用分片。

经过TCP层分片后,如果TCP分片丢失,超时重发只是发送一个MSS单位的分片,大大增加重传的效率?

Q:SYN攻击是什么?如何避免?

就是在三次握手过程,攻击者伪造不同IP一直发送SYN报文,服务端接收后,就进入SYN_RCVD状态,且服务端发送ACK+SYN报文无回应,久而久之就会占满SYN接收队列(未连接队列),使得服务器不能正常给用户连接。

如何避免?
一是修改Linux内核参数,控制队列大小和饱和策略。
比如,限制SYN_RCVD状态连接的最大个数,超出处理能力时,采取丢弃策略。

二是启动sync cookies
首先,正常情况下:

  • 当服务端接收到客户端的 SYN 报文时,会将其加入到内核的「 SYN 队列」;

  • 接着发送 SYN + ACK 给客户端,等待客户端回应 ACK 报文;

  • 服务端接收到 ACK 报文后,从「 SYN 队列」移除放入到「 Accept 队列」;

  • 应用通过调用 accept() socket 接口,从「 Accept 队列」取出的连接。

  • 如果accept方法调用过慢时,就会导致「 Accept 队列」被占满。

  • 如果应用程序过慢时,就会导致「 Accept 队列」被占满。

但开启sync cookies 后

  • 当 「 SYN 队列」满之后,后续服务器收到 SYN 包,不进入「 SYN 队列」;

  • 计算出一个 cookie 值,再以 SYN + ACK 中的「序列号」返回客户端,

  • 服务端接收到客户端的应答报文时,服务器会检查这个 ACK 包的合法性。如果合法,直接放入到「 Accept 队列」。

  • 最后应用通过调用 accpet() socket 接口,从「 Accept 队列」取出的连接。

TCP 四次挥手

客户端发送首部FIN=1 的TCP报文,即FIN报文,之后进入FIN等待1状态。
服务端接收后响应ACK,进入等待关闭状态。
客户端接收ACK后,进入FIN等待2状态
等服务处理后面,再发送FIN报文,然后进入等待最后ACK状态。
客户端收到后,会一个ACK,进入时间等待状态TIME_WAIT。
服务器收到后,关闭连接。
服务端经过2MSL时间后,关闭连接。

每个方向都需要FIN 和 ACK,这里需要注意,TIME_WAIT是主动关闭连接才有

Q: 为什么四次挥手?

因为双方都需要分开发送一个FIN 和分开响应一个ACK,来告知和确认。

Q:为什么TIME_WAIT等待是2MSL?

MSL是最大报文生存时间,它是任何报文在网络上生存的最长时间。
顺便一提,TTL是经过路由的跳数。MSL要大于等于TTL消耗位0的时间。

TIME_WAIT持续2倍MSL,在Linux中是60s,说白来,这其实是要等报文一来一回的时间。
比如,如果对方没有接收到最后ACK,超时重传FIN报文,然后我接收到FIN报文,ACK与重传FIN 正好一来一回,2MSL足够时间让我接收重传的FIN报文,之后,我重发ACK,2MSL重新计时。

Q:为什么需要TIME_WAIT?

原因一 : 防止旧连接的数据包
比如,双方关闭连接后,服务端的相同端口号的TCP连接被重用。
如果TIME_WAIT没有或过短,上一次由服务器发送,但被网络阻塞的报文,会现在发送过来给一个新的客户端,导致数据错乱。
等待2MSL时间,保证服务器所有数据包自然死亡。

原因二:保证连接正确关闭:
TIME-WAIT 作用是等待足够的时间以确保最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭。

如果TIME_WAIT没有或过短,

  • 客户端四次挥手的最后一个 ACK 报文如果在网络中被丢失了,此时如果客户端 TIME-WAIT 过短或没有,则就直接进入了 CLOSE 状态了,那么服务端则会一直处在 LASE-ACK 状态。

  • 当客户端发起建立连接的 SYN 请求报文后,服务端会发送 RST 报文给客户端,连接建立的过程就会被终止。

Q:TIME_WAIT 过多有什么危害?

  • 第一是内存资源占用;

  • 第二是对端口资源的占用,一个 TCP 连接至少消耗一个本地端口;

Q: 如何优化TIME_WAIT?

一是可以复用处于 TIME_WAIT 的 socket 为新的连接所用
使用这个选项,还有一个前提,需要打开对 TCP 时间戳的支持,即

这个时间戳的字段是在 TCP 头部的「选项」里,用于记录 TCP 发送方的当前时间戳和从对端接收到的最新时间戳。

由于引入了时间戳,我们在前面提到的 2MSL 问题就不复存在了,因为重复的数据包会因为时间戳过期被自然丢弃。

温馨提醒:net.ipv4.tcp_tw_reuse要慎用,因为使用了它就必然要打开时间戳的支持 net.ipv4.tcp_timestamps当客户端与服务端主机时间不同步时,客户端的发送的消息会被直接拒绝掉。小林在工作中就遇到过。。。排查了非常的久

二是当系统中处于 TIME_WAIT 的连接一旦超过一个值时,系统就会将所有的 TIME_WAIT 连接状态重置。,问题更多

三是我们可以通过设置 socket 选项,来设置调用 close 关闭连接行为。

Q:如果已经建立了连接,但是客户端突然出现故障了怎么办?

TCP 有一个机制是保活机制。这个机制的原理是这样的:

定义一个时间段,在这个时间段内,如果没有任何连接相关的活动,TCP 保活机制会开始作用,每隔一个时间间隔,发送一个探测报文,该探测报文包含的数据非常少,如果连续几个探测报文都没有得到响应,则认为当前的 TCP 连接已经死亡,系统内核将错误信息通知给上层应用程序。

socket编程

输入图片描述

输入图片描述

  • 服务端和客户端初始化 socket,得到文件描述符;

  • 服务端调用 bind,将绑定在 IP 地址和端口;

  • 服务端调用 listen,进行监听;

  • 服务端调用 accept,等待客户端连接;

  • 客户端调用 connect,向服务器端的地址和端口发起连接请求;

  • 服务端 accept 返回用于传输的 socket 的文件描述符;

  • 客户端调用 write 写入数据;服务端调用 read 读取数据;

  • 客户端断开连接时,会调用 close,那么服务端 read 读取数据的时候,就会读取到了 EOF,待处理完数据后,服务端调用 close,表示连接关闭。

这里需要注意的是,服务端调用 accept 时,连接成功了会返回一个已完成连接的 socket,后续用来传输数据。

所以,监听的 socket 和真正用来传送数据的 socket,是「两个」 socket,一个叫作监听 socket,一个叫作已完成连接 socket

成功连接建立之后,双方开始通过 read 和 write 函数来读写数据,就像往一个文件流里面写东西一样。

Q: listen 时候参数 backlog 的意义?

Linux内核中会维护两个队列:

  • 未完成连接队列(SYN 队列):接收到一个 SYN 建立连接请求,处于 SYN_RCVD 状态;

  • 已完成连接队列(Accpet 队列):已完成 TCP 三次握手过程,处于 ESTABLISHED 状态;

输入图片描述

输入图片描述

在 Linux 内核 2.2 之后,backlog 变成 accept 队列,也就是已完成连接建立的队列长度,所以现在通常认为 backlog 是 accept 队列。

Q:什么是 TCP 半连接队列和全连接队列?

在 TCP 三次握手的时候,Linux 内核会维护两个队列,分别是:

半连接队列,也称 SYN 队列;全连接队列,也称 accepet 队列;服务端收到客户端发起的 SYN 请求后,内核会把该连接存储到半连接队列,并向客户端响应 SYN+ACK,接着客户端会返回 ACK,服务端收到第三次握手的 ACK 后,内核会把连接从半连接队列移除,然后创建新的完全的连接,并将其添加到 accept 队列,等待进程调用 accept 函数时把连接取出来

img

Q:accept 发送在三次握手的哪一步?

输入图片描述

输入图片描述

Q:客户端调用 close 了,连接是断开的流程是什么?

输入图片描述

输入图片描述

————————————————

Q: - 没有开启TCP keepalive保活, 一直没有数据交互;进程崩溃 和主机崩溃的区别?

如果有保活机制

  • 如果对端程序是正常工作的。当 TCP 保活的探测报文发送给对端, 对端会正常响应,这样 TCP 保活时间会被重置,等待下一个 TCP 保活时间的到来。

  • 如果对端主机崩溃,或对端由于其他原因导致报文不可达。当 TCP 保活的探测报文发送给对端后,石沉大海,没有响应,连续几次,达到保活探测次数后,TCP 会报告该 TCP 连接已经死亡

没有的话:

如果客户端主机崩溃了,服务端是无法感知到的,在加上服务端没有开启 TCP keepalive,又没有数据交互的情况下,服务端的 TCP 连接将会一直处于 ESTABLISHED 连接状态,直到服务端重启进程

kill -9 来模拟进程崩溃的情况,发现在 kill 掉进程后,服务端会发送 FIN 报文,与客户端进行四次挥手
如果其中一方的进程发生了崩溃,这个过程操作系统是可以感知的到的,于是就会发送 FIN 报文给对方,然后与对方进行 TCP 四次挥手。

  • 第一种,客户端主机宕机,又迅速重启,会发生什么?

  • 第二种,客户端主机宕机,一直没有重启,会发生什么?

客户端主机宕机,又迅速重启

在客户端主机宕机后,服务端向客户端发送的报文会得不到任何的响应,在一定时长后,服务端就会触发超时重传机制,重传未得到响应的报文。

客户端主机宕机,一直没有重启

这种情况,服务端超时重传报文的次数达到一定阈值后,内核就会判定出该 TCP 有问题,然后通过 Socket 接口告诉应用程序该 TCP 连接出问题了

滑动窗口机制

TCP 利用滑动窗口实现流量控制。流量控制是为了控制发送方发送速率,保证接收方来得及接收。 TCP会话的双方都各自维护一个发送窗口和一个接收窗口。接收窗口大小取决于应用、系统、硬件的限制。发送窗口则取决于对端通告的接收窗口。接收方发送的确认报文中的window字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将接收方的确认报文window字段设置为 0,则发送方不能发送数据。

img

img

TCP头包含window字段,16bit位,它代表的是窗口的字节容量,最大为65535。这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。接收窗口的大小是约等于发送窗口的大小。

详细讲一下拥塞控制?

防止过多的数据注入到网络中。 几种拥塞控制方法:慢开始( slow-start )、拥塞避免( congestion avoidance )、快重传( fast retransmit )和快恢复( fast recovery )。

img

img

慢开始

把拥塞窗口 cwnd 设置为一个最大报文段MSS的数值。而在每收到一个对新的报文段的确认后,把拥塞窗口增加至多一个MSS的数值。每经过一个传输轮次,拥塞窗口 cwnd 就加倍。 为了防止拥塞窗口cwnd增长过大引起网络拥塞,还需要设置一个慢开始门限ssthresh状态变量。

当 cwnd < ssthresh 时,使用慢开始算法

当 cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法

当 cwnd = ssthresh 时,既可使用慢开始算法,也可使用拥塞控制避免算法

拥塞避免

让拥塞窗口cwnd缓慢地增大,每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍。这样拥塞窗口cwnd按线性规律缓慢增长。

无论在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没有收到确认),就要把慢开始门限ssthresh设置为出现拥塞时的发送 方窗口值的一半(但不能小于2)。然后把拥塞窗口cwnd重新设置为1,执行慢开始算法。这样做的目的就是要迅速减少主机发送到网络中的分组数,使得发生 拥塞的路由器有足够时间把队列中积压的分组处理完毕。

快重传

有时个别报文段会在网络中丢失,但实际上网络并未发生拥塞。如果发送方迟迟收不到确认,就会产生超时,就会误认为网络发生了拥塞。这就导致发送方错误地启动慢开始,把拥塞窗口cwnd又设置为1,因而降低了传输效率。

快重传算法可以避免这个问题。快重传算法首先要求接收方每收到一个失序的报文段后就立即发出重复确认,使发送方及早知道有报文段没有到达对方。

发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待重传计时器到期。由于发送方尽早重传未被确认的报文段,因此采用快重传后可以使整个网络吞吐量提高约20%。

快恢复

当发送方连续收到三个重复确认,就会把慢开始门限ssthresh减半,接着把cwnd值设置为慢开始门限ssthresh减半后的数值,然后开始执行拥塞避免算法,使拥塞窗口缓慢地线性增大。

在采用快恢复算法时,慢开始算法只是在TCP连接建立时和网络出现超时时才使用。 采用这样的拥塞控制方法使得TCP的性能有明显的改进。

常见重传机制:

超时重传

快速重传

SACK

D-SACK

TCP 重传、滑动窗口、流量控制、拥塞控制发愁图解

https://mp.weixin.qq.com/s/Tc09ovdNacOtnMOMeRc_uA

精炼版

https://mp.weixin.qq.com/s/plkDQ4YCawcLOr5In_L8TQ