Java八股-计网篇
Java八股-计网篇
基础篇
OSI七层模型和TCP/IP网络模型

每一层对应协议

输入网址到网页,发生了什么
-
浏览器对输入的URL进行解析
-
浏览器根据输入域名进行域名解析,获取到对应的IP地址
浏览器首先查询自身缓存中是否有这个域名对应的IP地址,如果有直接返回;没有则查询本地缓存中是否有该域名记录,如果有则直接返回;没有则查询hosts文件,有则返回;没有则查询本地DNS服务器(由网络接入服务器商提供,比如中国移动),有则返回;没有则本地DNS服务器向根域名服务器请求,根域名服务器可以直出接下来要向谁查询并告诉本地DNS,最终本地DNS查询到域名对应的IP地址,然后返回给浏览器
-
浏览器获取到请求的域名对应的IP后首先进行TCP连接,通过三次握手建立连接,之后发送HTTP请求,HTTP请求有请求行、请求头、空行、请求数据组成;如果使用的HTTP协议是HTTP1.1及以后将开启HTTP长连接,不需要每次发送HTTP请求都要从新建立连接;如果使用了HTTPS协议,则需要进行非对称加密来传递服务器公钥并根据一定算法生成此次连接要使用的密钥,随后使用生成的回话密钥进行对称加密。
-
服务器接收到客户端发送的请求后对其做出相应,将对应的请求资源发送给浏览器
-
浏览器根据接收到的响应报文渲染页面
-
连接结束:浏览器进行四次挥手释放TCP连接
HTTP
HTTP状态码

301
表示永久移动,请求的资源已被永久移动到新位置。服务器返回此响应时,会返回新的资源地址
302
表示临时性移动,服务器从另外的地址响应资源,但是客户端还应该使用这个地址
304
表示资源未修改,重定向已存在的缓冲文件,也称缓存重定向,也就是告诉客户端可以继续使用缓存资源,用于缓存控制
HTTP常见字段
-
Host
客户端发送请求时,用来指定服务器的域名
-
Content-Length
服务器返回数据时,会有该字段,表示本次响应数据的长度。由于是基于TCP进行通信,所以存在粘包问题,HTTP协议通过设置回车符、换行符作为HTTP Header的边界,通过Content-Length字段作为Http Body的边界
-
Connection
客户端要求服务器使用HTTP 长连接机制
Connection: Keep-Alive
-
Content-Type
服务器响应时返回给客户端,表示本次数据是什么格式
Content-Type: text/html; Charset=utf-8
-
Accept
客户端告诉服务器自己能接受哪些数据格式
Accept: */*
-
Content-Encoding
说明数据的压缩方式,表示服务器返回的数据使用了什么压缩格式
Content-Encoding: gzip
-
Accept-Encoding
客户端告诉服务器自己可以接受哪写压缩方式
Accept-Encoding: gzip, deflate
GET和POST
- GET方法将请求信息放在URL中,而POST将请求信息放在请求体中;由于浏览器对URL长度有限制,所以使用GTE的请求的URL长度有限制;GET将请求信息直接暴露在URL中不安全
- 由于GET从定义上是从服务器上获取资源,所以每次请求都不会影响到服务器而且每次的结果都是相同的即是幂等的。而POST定义是新增或提交数据,会修改服务器上的资源,是不安全的,同时每次提交数据都会创建多个资源所以不是幂等的。
- GET请求能够被缓存
HTTP缓存技术
对于一些具有重复性的HTTP请求,比如每次请求得到的数据都是一样的,就可以将数据缓存在本地。HTTP缓存有两种实现方式:强制缓存和协商缓存
强制缓存
强缓存指的是只要浏览器判断缓存没有过期,则直接使用浏览器的本地缓存,决定是否使用缓存的主动性在浏览器。强缓存利用以下两个HTTP响应头字段实现,表示资源在客户端缓存的有效期:
- Cache-Control:相对时间
- Expires:绝对时间
如果同时包含,Cache-Control优先级高于Expires。
- 当浏览器第一次请求服务器资源时,服务器在返回该资源时会在响应报文头部加上Cache-Control,Cache-Control中设置了过期时间
- 浏览器再次请求访问服务器中的该资源时,会通过请求资源的时间和过期时间判断资源是否过期,如果没有则使用该缓存,否则重新请求
- 服务器再次收到请求后,更新响应头中的Cache-Control
协商缓存
当一些请求的响应码为304
时,表示服务器告诉浏览器可以使用本地缓存资源,通常这种通过服务端告知客户端是否可以使用缓存的方式被称为协商缓存。
使用ETag
字段实现协商缓存:
- 当浏览器第一次请求访问服务器资源时,服务器在返回这个资源的同时在响应头中加上ETag唯一标识(由当前请求的资源生成)
- 当浏览器再次请求该资源时,首先检查强制缓存是否过期,没有过期则直接使用本地缓存,否则在请求头上加
If-None-Match
字段,值为ETag
唯一标识 - 服务器再次接收到请求后,会根据请求中的
If-None-Match
值与当前请求资源生成的唯一标识进行比较,相等则返回304
,不相等则返回资源并在响应头上加上新的ETag
值 - 如果收到
304
请求响应状态码,直接本地加载资源,否则更新资源

HTTP报文结构
请求报文
-
请求行
请求方法、请求URL和HTTP协议版本
GET /index.html HTTP/1.1
-
请求头
包含请求的附加信息,如客户端想要接收的内容类型、浏览器类型等
-
空行
请求头部和消息正文之间有一个空行,表示请求头部结束
-
请求体
请求的具体内容,如 POST 请求中的表单数据;GET 请求中没有消息正文
响应报文
-
状态行
HTTP协议版本、状态码、状态消息
HTTP/1.0 200 OK
-
响应头
包含响应的附加信息,如服务器类型、内容类型、内容长度等
-
空行
表示响应头部结束
-
响应体
响应的具体内容,如 HTML 页面。不是所有的响应都有消息正文,如 204 No Content 状态码的响应
HTTP和HTTPS
- HTTP是超文本超文本传输协议,信息是明文传输,存在安全风险问题。HTTPS解决HTTP不安全的缺陷,在TCP和HTTP网络层之间加入了SSL/TLS安全协议,使得报文能够加密传输
- HTTP连接建立简单,在TCP三次握手之后便可以进行HTTP报文传输;而HTTPS在TCP三次握手后还需要进行SSL/TLS的握手过程才可以进行加密报文传输
- HTTPS协议需要向CA(证书权威机构)申请数字证书,来保证服务器的身份是可信的
HTTP三大风险
-
窃听风险
混合加密实现信息的机密性,解决窃听风险
-
篡改风险
摘要算法实现完整性,根据数据生成唯一的指纹,该指纹用于校验数据的完整性,解决了篡改风险
-
冒充风险
将服务器公钥放在数字证书中,解决了冒充的风险
混合加密
使用非对称加密和对称加密实现混合加密。
- 在通信建立前采用非对称加密方式交换会话密钥,后续使用对称加密
- 在通信过程中全部使用非对称加密得到的会话密钥进行对称加密,对明文数据进行加密
摘要算法+数字签名
为了保证传输的内容不被篡改,针对传输的内容计算出一个指纹,然后一起传输给对方。对方收到报文后首先对内容也计算出一个指纹,然后将其与发送方的指纹进行对比,如果相同则说明没有被篡改,不同则被篡改了。在计算机里会用摘要算法(哈希函数)来计算出内容的哈希值,也就是内容的「指纹」,这个哈希值是唯一的,且无法通过哈希值推导出内容
但是这样并不能保证内容和哈希值不会都被替换,所以使用非对称加密对内容的哈希值进行加密形成数字签名,将内容和数字签名一同发送给服务器,服务器对内容计算哈希值后将其与公钥解密的数字签名比较
数字证书
虽然上述可以实现对内容的可靠加密,但是不能保证服务器公钥的合法性(使用伪造的公钥和私钥)。
于是存在一个CA机构,将服务器的个人信息+公钥等用自己的私钥打包成一个数字证书,服务器不仅会使用私钥对内容进行签名还会将数字证书发送给接收方。客户端接收到数字证书后首先使用CA的公钥对数字证书进行解密验证合法性,判断合法后客户端就获得了服务器的公钥,之后就可以进行非对称加密来传输生成会话密钥的准备数据。
HTTPS 建立连接
- 客户端向服务器索要并验证服务器公钥
- 双方协商生成会话密钥
- 双方采用会话密钥进行加密通信
-
客户端向服务器发起加密通信请求(客户端支持TLS协议版本、客户端生成的随机数A、客户端支持的密码套件)
-
服务器收到客户端请求后对客户端发起响应(确认TLS协议版本、服务器生成的随机数B、确认密码套件、服务器的数字证书)
数字证书为由CA用他自己的私钥对服务器公钥进行非对称加密得到的
-
客户端收到服务器回应后,使用浏览器或者系统中的CA公钥对服务器发送过来的数字证书进行解密确认证书的合法性和真实性。如果证书没有问题,此时客户端就获取到了服务器的公钥,随后使用它加密报文,并且向服务器发送消息(随机数C、加密通信算法改变通知,表示随后信息都将会用会话密钥加密通信、客户端握手结束通知,同时将之前内容做摘要供服务器校验)
服务器和客户端都有了三个随机数,接着使用协商好的加密算法各自生成本次通信的会话密钥
-
服务器计算出会话密钥并向客户端发送最后信息(加密算法改变通知、服务器握手结束通知,同时将之前内容做摘要供客户端校验)
中间人伪造数字证书并作为中间人转发消息

伪造的证书会被浏览器认定是非法的,此时用户不接受该证书就不会发生安全问题,接受了则存在安全风险。
HTTP1.0,1.1,2.0,3.0
HTTP1.0
HTTP1.0是无状态的,每个请求之间相互独立,服务器不保存任何请求的状态信息;默认状态下每个HTTP请求/响应之后连接都会被关闭,属于短连接,但是可以设置Connection: keep-alive
强制开启长连接
HTTP1.1
HTTP1.1默认开启长连接,可以在一个TCP连接上发送多个HTTP请求和响应;同时支持客户端在前一个请求的相应到达前发送下一个请求,提高传输效率
HTTP2.0
-
二进制协议:使用二进制而不是文本格式来传输数据,解析更加高效
-
头部压缩:如果同时发送多个请求,他们的请求头是一样的或者相似的,协议会减少冗余字段。
HPACK
算法:在客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号 -
并发传输(多路复用):HTTP1.x存在队头阻塞问题,提出Stream概念,多个Stream复用在一条TCP连接上,每个Stream由一个唯一的ID,不同Stream之间的数据可以乱序发送,因此可以并发的发送不同的Stream,实现并行交错发送请求和响应
-
服务器推送:服务器可以主动向客户端推送资源,而不需要客户端明确请求
HTTP3.0
将HTTP下层的TCP协议改成了UDP,设计QUIC协议实现类似TCP的可靠性传输
QUIC特点:
- 无队头阻塞:类似HTTP2.0的Stream与多路复用概念,但是不同流之间相互独立,当某个流发生丢包只会阻塞这个流,其他流不会受影响,因此不存在队头阻塞问题
- 更快的连接建立: // TODO
- 连接迁移:根据连接ID可以快速建立连接 // TODO
Cookie和Session
Cookie 是保存在客户端的一小块文本串的数据。客户端向服务器发起请求时,服务端会向客户端发送一个 Cookie,客户端就把 Cookie 保存起来。在客户端下次向同一服务器再发起请求时,Cookie 被携带发送到服务器。服务端可以根据这个 Cookie 判断用户的身份和状态
Session 指的就是服务器和客户端一次会话的过程。它是另一种记录客户状态的机制。不同的是 cookie 保存在客户端浏览器中,而 session 保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上,这就是 session。客户端浏览器再次访问时只需要从该 session 中查找用户的状态
- Cookie在客户端,Session在服务器端
- Cookie大小不超过4k,Session存储数据高于Cookie
- Session安全性高于Cookie
- Cookie存活时间高于Session
TCP
三次握手
-
第一次握手:客户端发送TCP报文段到服务器,该TCP报文中SYN被设置为1,表示这是一个连接请求,同时客户端会随机生成一个序列号x发送给服务器,客户端进入SYN_SENT状态
-
第二次握手:服务器收到客户端的连接请求后如果同意建立连接则会发送应答TCP报文给客户端,该报文中SYN和ACK都被设置为1,同时服务器也会生成一个随机的序列号y,并且将客户端的序列号加一(x+1)作为确认号发送给客户端,此时服务器进入SYN_RCVD状态
-
第三次握手:客户端接收到服务器的应答后需要给服务器发送一个确认报文,该报文ACK被设置为1,同时确认号设置为服务器序列号加一(y+1),这条消息的序列号设置为x+1,此时客户端进入ESTABLISHED状态,服务器收到该报文后也将进入ESTABLISHED状态
注:第三次握手是可以携带数据的,前两次握手不行
为什么是三次,两次行不行
为什么是三次:
-
避免历史连接
客户端首先发送了一个序列号为x的连接请求(第一次握手),结果发生了网络阻塞并且客户端宕机了,重启后再次发送连接请求,序列号为y。结果旧的连接请求x在网络恢复正常后先到达服务器,此时分别考虑三次握手和两次握手:
-
三次握手
服务器收到历史连接请求x后就会发送确认报文给客户端,并设置设置确认号为x+1,此时客户端对比自己期望的确认号y+1和回传的确认号x+1,发现不是,则直接发送RST报文终止连接,此时服务器直接从SYN_RCVD状态进入CLOSE状态,等待真正的请求报文y到达后再次建立连接。该过程中即使历史连接先一步到达,也不会占用服务器资源导致资源浪费。
-
两次握手
服务器收到历史连接请求x后就会发送确认报文给客户端,并设置设置确认号为x+1,由于是两次握手,服务器发送发确认报文直接进入ESTABLISHED状态并向客户端发送数据,客户端此时通过与期望确认号对比发现不是想要的便发送RST报文终止连接,服务器收到后断开连接。但是服务器已经建立了一次连接而且发送了数据,白白浪费了资源
-
-
同步双方序列号
序列号可以过滤掉重复消息并且保证消息的有序性,同时确保消息有没有被接收到,方便消息重发等。如果只有两次握手只能保证客户端的初始序列号被服务器接收到并不能保证服务器的初始序列号被成功接收。
-
避免资源浪费
如果只有两次握手,服务器无法知道自己ACK报文是否被成功接收,所以每接收到一个SYN报文就建立连接,导致建立过多的冗余连接造成了不必要的资源浪费。
为什么每次建立TCP连接时,初始序列号都要求不一样
-
防止历史报文被下一个相同四元组(源ip,源端口,目的ip,目的端口)的连接接收
假设每次建立连接初始序列号都是从0开始的,由一个客户端发送的数据报因为网络阻塞迟迟没有到达服务器,然而此时连接异常中断,再次建立连接后,该历史数据包刚好到达服务器并且序列号刚好在服务器接收窗口范围内,此时就导致了历史数据报被新的连接接收。
-
防止黑客伪造的相同序列号的 TCP 报文被对方接收
握手报文丢失
第一次握手丢失
第一次握手丢失服务器将接收不到SYN报文,此时客户端等待一段时间后没有收到预期的服务器恢复的SYN+ACK报文,就会重新发送SYN报文至服务器端,如果仍然没有回应则重复发送,直到发送次数达到最大重传次数,然后返回连接建立失败。
第二次握手丢失
第二次握手丢失即客户端无法接收到服务器端回复的SYN+ACK报文,此时客户端会继续发送SYN报文直到接收到第二次握手报文或者超过最大重发次数,而此时服务器端会一直阻塞在**accet()**处,等待客户端发送ACK报文,同时服务器端也会重发SYN+ACK报文,直至达到最大重发次数
第三次握手丢失
第三次握手丢失即服务器端无法接收到客户端发送的ACK报文,服务器端同样会采用超时重传机制,如果重发次数超过限制则**accept()**调用返回-1,服务器端建立连接失败;而此时客户端认为已经建立成功,开始向服务器端发送数据,但是服务器端的accept()系统调用已返回-1,不在监听状态,因此服务器接收到客户端发送的数据会发送RST报文给客户端,断开客户端单方面建立的连接状态。
四次挥手
- 第一次挥手:客户端准备关闭连接,发送FIN报文给服务器端,此时客户端进入FIN_WAIT_1状态
- 第二次挥手:服务器接收到FIN报文后向客户端发送ACK报文并进入CLOSE_WAIT状态;客户端接收到ACK报文后进入FIN_WAIT_2状态
- 第三次挥手:等待服务器处理完数据后会给客户端发送FIN报文,之后服务器进入LAST_ACK状态
- 第四次挥手:客户端接收到服务器发送的ACK报文后回复一个ACK报文,然后进入TIME_WAIT状态;服务器端收到ACK报文后进入CLOSE状态,客户端结果2MSL后自动进入CLOSE状态
主动关闭连接的才有TIME_WAIT状态
为什么挥手需要四次
- 关闭连接时,客户端向服务器发送
FIN
时只代表客户端不在发送数据了,但是仍然能接收数据 - 服务器收到
FIN
报文后,先回复一个ACK
报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送FIN
报文给客户端来表示同意现在关闭连接
挥手报文丢失
第一次挥手报文丢失
客户端发送FIN报文后如果没有在一定时间内接收到ACK报文,则会触发重传机制,在超过最大重传次数后客户端直接进入CLOSE状态
第二次挥手报文丢失
客户端发送FIN报文后服务器会回复ACK报文,此时服务器进入CLOSE_WAIT状态。由于ACK报文是不会重传的,所以当第二次挥手发送的ACK报文丢失,客户端会触发重传机制重发FIN报文,直到收到第二次挥手,或者达到最大重传次数后直接进入CLOSE状态
第三次挥手报文丢失
当第三次挥手FIN报文丢失后,服务器端因为长时间没有接收到第四次挥手ACK报文,就会触发超时重传机制直至到达最大重传次数后服务器直接断开连接。客户端由于是通过close函数关闭连接的,所以FIN_WAIT_2状态有时间限制,如果长时间未接收到服务器打第三次挥手报文则会直接断开连接。
第四次挥手报文丢失
当客户端就收到服务器发送的第三次挥手报文FIN后会发送ACK报文即第四次挥手,此时客户端进入TIME_WAIT状态,2MSL后进入关闭状态。服务器端如果没有接收到ACK报文则会一直处于LAST_ACK状态,同时会重发FIN报文,当超过最大重传次数后还没有接收到客户端的ACK报文则会直接断开连接;客户端在收到第三次挥手后就会进入TIME_WAIT状态,如果在2MSL内再次接收到FIN报文就会重置定时器,当等待2MSL后直接断开连接。
为什么TIME_WAIT等待的时间是2MSL
MSL是报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间的报文将被丢弃。2MSL即保证报文一来一回的时间,从第四次挥手发送ACK报文开始计时,如果服务器端没有接收到该报文则会重新发送FIN报文,在极端情况下ACK报文刚好到达服务器端,此时服务器端刚好重发FIN报文,等到重发FIN报文到达客户端时刚好是2MSL,所以需要2MSL保证FIN报文重发。
为什么需要TIME_WAIT状态
-
防止历史连接中的数据被后面相同四元组的连接错误的接收
设置为2MSL能够保证两个方向的数据包都将被丢弃,使得历史连接的数据包在网络中自然消失,再次出现的数据包一定是新建立的连接产生的
-
保证被动关闭连接的一方能被正确的关闭
保证第四次挥手ACK报文能够让被动关闭方接收从而正常关闭。如果TIME_WAIT时间过短或者没有就会导致重发后客户端已经进入CLOSE状态,此时就会回复RST报文,对服务器不友好
TIME_WAIT过多的危害
如果服务器有处于TIME_WAIT状态的TCP,说明是由服务器方主动发起的断开请求。过多的TIME_WAIT状态主要危害如下:
- 内存资源占用
- 端口资源占用,一个TCP连接至少消耗一个本地端口
如何解决TIME_WAIT状态过多
- 服务器设置SO_REUSEADDR套接字来通知内核,如果端口被占用,但是TCP连接处于TIME_WAIT状态时可以重用端口
- 使用长连接减少TCP的连接和断开,在长连接业务中往往不需要考虑TIME_WAIT状态
TCP报文头部格式

TCP如何保证可靠性的
- 连接管理:三次握手、四次挥手
- 校验和
- 序列号/确认应答
- 流量控制
- 最大消息长度
- 超时重传
- 拥塞控制
TCP重传机制
超时重传
当数据包或者确认应答丢失时会触发超时重传

RTT
:往返时延,即数据发送时刻到接收到确认的时刻的差值(报文一来一回的时间)
RTO
:超时重传时间,超时重传时间应该略大于往返时延
快速重传
不以时间为驱动,而是以数据驱动重传,基于接受端的反馈信息来引发重传。当连续接收到三个相同的ACK报文后直接进行重传。快速重传只解决了超时时间问题,不能解决是重传之前的一个还是所有报文。
带选择确认的重传(SACK)
在快速重传的基础上,接收方返回最近收到报文段的序列号范围,这样发送方就知道接收方哪写数据包是没收到的,这样就很清楚应该重传哪些数据包
重复SACK
在SACK基础上做了扩展,告诉发送方有哪些数据包自己重复接收了
滑动窗口
TCP发送一个数据,如果需要收到确认应答才会发送下一个数据。这样就会导致效率比较低。TCP引入窗口概念,有了窗口就可以指定窗口大小,窗口大小就是指无需等待确认应答而可以继续发送数据的最大值。接收方每次收到数据包在发送确认报文时同时告诉对方自己缓冲区还有多少空余空间,发送方根据这个剩余窗口大小值决定下次发送多少数据。
Nagle算法和延迟确认
当我们 TCP 报⽂的承载的数据⾮常⼩的时候,例如⼏个字节,那么整个⽹络的效率是很低的,因为每个 TCP 报⽂中都会有 20 个字节的 TCP 头部,也会有 20 个字节的 IP 头部,⽽数据只有⼏个字节,所以在整个报⽂中有效数据占有的比例就会⾮常低。
-
Nagle算法:
任意时刻,最多只能有一个未被确认的小段。所谓 “小段”,指的是小于 MSS 尺寸的数据块,所谓 “未被确认”,是指一个数据块发送出去后,没有收到对方发送的 ACK 确认该数据已收到。必须满足以下两个条件:1)没有已发送未确认报文时,立刻发送数据;2)存在未确认报文时,直到【没有已发送未确认报文】或【数据长度达到MSS大小】时再发送数据
-
延迟确认
ACK报文如果没有携带数据则网络效率很低,TCP 延迟确认的策略:
- 当有响应数据要发送时,ACK 会随着响应数据⼀起⽴刻发送给对⽅
- 当没有响应数据要发送时,ACK 将会延迟⼀段时间,以等待是否有响应数据可以⼀起发送
- 如果在延迟等待发送 ACK 期间,对⽅的第⼆个数据报⽂⼜到达了,这时就会⽴刻发送 ACK
拥塞控制
当网络出现拥堵时,如果继续发送数据包可能会导致数据包时延、丢失等,这是TCP就会重传数据,但是一重传就会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,这个情况就会进入恶性循环被不断地放大,于是拥塞控制的目的就是避免发送方的数据填满整个网络。
慢启动
当发送方每收到一个ACK,拥塞窗口cwnd的大小就会加一,呈指数性增长。当窗口大小小于慢启动阈值时使用慢启动算法,当大于等于慢启动阈值时使用拥塞避免算法
拥塞避免算法
进入拥塞避免算法后,每接收到一个ACK,cwnd增加1/cwnd,cwnd呈现线性增长,如果触发了重传机制就会进入拥塞发生算法
拥塞发生
当网络发生拥塞,也就是发生数据包重传,重传机制主要有两种:
-
超时重传
当发生超时重传,此时慢启动阈值变为cwnd/2,cwnd设置为初始值
-
快速重传
当发生快速重传,cwnd变成原来的一半,慢启动阈值变为当前cwnd
快速恢复
快速恢复和快速重传一般同时使用,快速恢复算法是认为,你还能收到 3 个重复 ACK 说明网络也不那么糟糕,所以没有必要像 RTO
超时那么强烈。从快速重传进入快速恢复算法后cwnd等于慢启动阈值+3,表示三个ACK数据包被接收了,然后重传丢失的数据包,如果再收到重复的ACK那么cwnd加一,如果收到新的ACK,把cwnd设置为慢启动阈值,表示恢复过程已经结束,可以恢复之前的状态了,即再次进入拥塞避免状态。
TCP半连接队列和全连接队列
在三次握手过程中,Linux内核会维护两个队列:半连接队列(SYN队列)和全连接队列(accept队列)
服务器端收到客户端发起的SYN请求后,内核会把该连接存储到半连接队列,并向客户端响应SYN+ACK,接着客户端会返回 ACK,服务端收到第三次握手的 ACK 后,内核会把连接从半连接队列移除,然后创建新的完全的连接,并将其添加到 accept 队列,等待进程调用 accept 函数时把连接取出来。

当服务端并发处理大量请求时,如果 TCP 全连接队列过小,就容易溢出。发生 TCP 全连接队溢出的时候,后续的请求就会被丢弃,这样就会出现服务端请求数量上不去的现象。
TCP粘包和半包
为什么会出现粘包和半包
- 要发送的数据小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据一次发送出去,将会发生粘包
- 接受数据端的应用层没有及时读取接受缓冲区中的数据,将发生粘包
- 要发送的数据大于TCP发送缓冲区剩余空间大小将会发生半包
- 待发送数据大于MSS(最大报文长度),TCP在传输前将进行拆包
如何解决
- 固定长度消息
- 特殊字符作为边界
- 自定义消息结构
四次挥手中收到乱序的FIN包会如何处理
如果FIN报文比数据报先抵达客户端,此时FIN报文其实是一个乱序的报文,此时客户端的TCP连接不会从FIN_WAIT_2状态转移到TIME_WAIT状态。在FIN_WAIT_2状态时,如果收到乱序的FIN报文,那么就会被加入到乱序队列,并不会进入到TIME_WAIT状态。等再次收到前面被网络延迟的数据包时,会判断乱序队列有没有数据,然后会检测乱序队列中是否有可用的数据,如果能在乱序队列中找到与当前报文的序列号保持的顺序的报文,就会看该报文是否有 FIN 标志,如果发现有 FIN 标志,这时才会进入 TIME_WAIT 状态。
TCP一端断电和进程崩溃
前提:没有开启TCP keepalive
主机崩溃
客户端主机崩溃服务器是无法感知的,再加上服务器没有开启TCP keepalive,又没有数据交互的情况下,服务器的TCP连接将会一直处于ESTABLISHED连接状态,直到服务器重启进程。即在没有TCP保活机制且双方不传输数据的情况下,一方的TCP连接处在ESTABLISHED状态,并不代表另一方的连接还一定正常。
进程崩溃
TCP的连接信息是由内核维护的,所以当服务端的进程崩溃后,内核需要回收该进程的所有TCP连接资源,于是内核会发送第一次挥手FIN报文,后续的挥手过程也都在内核完成并不需要进程的参与,所以及时服务器的进程退出了,还是能和客户端完成TCP四次挥手的过程。
拔掉网线后原本TCP连接还在吗
客户端拔掉网线后,并不会影响TCP连接状态。所以,拔掉网线后TCP连接是否还会存在关键要看拔掉网线后又没有数据传输。
有数据传输
- 客户端拔掉网线后如果服务器端发送了报文,那么在服务器重传次数没有达到最大值之前,客户端插回了网线,那么双方原本的TCP连接还能够正常存在
- 客户端拔掉网线后如果服务器端发送了报文,那么在服务器重传次数到达了最大值,服务器会断开TCP连接,等到客户端插回网线并发送数据,此时会收到服务器发送的RST报文而断开TCP连接
无数据传输
- 如果没有开启TCP keepalive机制,那么客户端拔掉网线后,即使客户端一直不插回网线TCP连接也不会断开
- 如果开启了TCP keepalive机制,客户端拔掉网线后,TCP keepalive机制会探测到对方TCP连接没有存活就会断开TCP连接
IP
IP协议相关技术
DNS域名解析、ARP和RARP协议、DHCP动态获取IP地址、NAT网络地址转换、ICMP网络控制报文协议、IGMP网络组管理协议
DNS域名协议
浏览器首先看一下自己的缓存里有没有,如果没有就向操作系统的缓存要,还没有就检查本机域名解析文件 hosts
,如果还是没有,就会 DNS 服务器进行查询,查询的过程如下:
- 客户端首先会发出一个 DNS 请求,问 www.server.com 的 IP 是啥,并发给本地 DNS 服务器(也就是客户端的 TCP/IP 设置中填写的 DNS 服务器地址)。
- 本地域名服务器收到客户端的请求后,如果缓存里的表格能找到 www.server.com,则它直接返回 IP 地址。如果没有,本地 DNS 会去问它的根域名服务器:“老大, 能告诉我 www.server.com 的 IP 地址吗?” 根域名服务器是最高层次的,它不直接用于域名解析,但能指明一条道路。
- 根 DNS 收到来自本地 DNS 的请求后,发现后置是 .com,说:“www.server.com 这个域名归 .com 区域管理”,我给你 .com 顶级域名服务器地址给你,你去问问它吧。”
- 本地 DNS 收到顶级域名服务器的地址后,发起请求问“老二, 你能告诉我 www.server.com 的 IP 地址吗?”
- 顶级域名服务器说:“我给你负责 www.server.com 区域的权威 DNS 服务器的地址,你去问它应该能问到”。
- 本地 DNS 于是转向问权威 DNS 服务器:“老三,www.server.com对应的IP是啥呀?” server.com 的权威 DNS 服务器,它是域名解析结果的原出处。为啥叫权威呢?就是我的域名我做主。
- 权威 DNS 服务器查询后将对应的 IP 地址 X.X.X.X 告诉本地 DNS。
- 本地 DNS 再将 IP 地址返回客户端,客户端和目标建立连接

ARP
ARP(Address Resolution Protocol,地址解析协议)是网络通信中的一种协议,主要目的是将网络层的 IP 地址解析为链路层的 MAC 地址。
- ARP 请求
当主机 A 要发送数据给主机 B 时,首先会在自己的 ARP 缓存中查找主机 B 的 MAC 地址。如果没有找到,主机 A 会向网络中广播一个 ARP 请求数据包,请求网络中的所有主机告诉它们的 MAC 地址;这个请求包含了请求设备和目标设备的 IP 和 MAC 地址。
- ARP 应答
网络中的所有主机都会收到这个 ARP 请求,但只有主机 B 会回复 ARP 应答,告诉主机 A 自己的 MAC 地址。并且主机 B 会将主机 A 的 IP 和 MAC 地址映射关系缓存到自己的 ARP 缓存中,以便下次通信时直接使用。
- 更新 ARP 缓存
主机 A 收到主机 B 的 ARP 应答后,也会将主机 B 的 IP 和 MAC 地址映射关系缓存到自己的 ARP 缓存中。
ICMP
ICMP
主要的功能包括:确认 IP 包是否成功送达目标地址、报告发送过程中 IP 包被废弃的原因和改善网络设置等。
ping原理
ping基于ICMP实现:
-
当执行 Ping 命令,如
ping www.baidu.com
,Ping 首先解析域名获取 IP 地址,然后向目标 IP 发送一个 ICMP Echo Request 消息。 -
当目标 IP 收到 ICMP Echo Request 消息后,它会生成一个 ICMP Echo Reply 消息并返回,即 Ping 响应消息。
-
发起 Ping 命令的设备接收到 ICMP Echo Reply 消息后,计算并显示从发送 Echo Request 到接收到 Echo Reply 的时间(通常称为往返时间 RTT,Round-Trip Time),以及可能的丢包情况。