admin 管理员组

文章数量: 1184232

(1)说说 TCP 的三次握手?

TCP的三次握手,就像两个人打电话前确认“你能听到我吗?我也能听到你”的过程,目的是让客户端和服务器在正式传数据前,互相确认“双方的发送和接收功能都正常”,并约定好数据传输的规则。

1. 第一次握手
客户端先发起请求:发送一个SYN包(可以理解为“请求连接信号”),告诉服务器:“我想跟你建立连接,这是我的初始序号(ISN),你记一下”。
此时客户端处于“等待服务器回应”的状态。

2. 第二次握手
服务器收到客户端的请求后,回复一个SYN+ACK包(“同意连接+确认收到”):

  • SYN告诉客户端:“我同意连接,这是我的初始序号(ISN),你也记一下”;
  • ACK告诉客户端:“你刚才发的请求我收到了,下次你就从这个序号之后发数据”。
    此时服务端处于“等待客户端确认”的状态。

3. 第三次握手
客户端收到服务端的回应后,再发一个ACK包:“你发的序号我收到了,下次你就从这个序号之后发数据”。
此时,双方都确认了“对方能收到自己的消息”,连接正式建立,接下来就可以开始传输数据了。

为什么必须是三次?少一次不行吗?

  • 如果只握两次手:服务器不确定客户端是否收到了自己的SYN+ACK(比如这个包在路上丢了),可能会白白为“无效连接”分配资源。
  • 三次握手刚好让双方都完成“双向确认”:客户端知道服务器能收能发,服务器也知道客户端能收能发,不多不少,效率最高。

(2)说说 TCP 的四次挥手?

TCP的四次挥手,其实就是TCP连接“优雅分手”的过程。确保双方都准备好断开连接,避免数据丢失。

四次挥手的具体过程:
假设A是主动关闭方,B是被动关闭方。

1. 第一次挥手(A→B:“我没数据要发了”)
A觉得自己的任务完成了(比如客户端已经拿到所有需要的数据),会给B发一个FIN(Finish)包,意思是“我这边要关闭发送通道了,不会再给你发新数据了”。
此时A进入“FIN_WAIT_1”状态,等待B的回应。

2. 第二次挥手(B→A:“我知道你没数据了”)
B收到FIN后,会先给A回一个ACK包,意思是“你的意思我收到了,我知道你不发了”。
此时B进入“CLOSE_WAIT”状态(这个状态很关键:B可能还有没发完的数据,需要继续处理,比如服务器可能还有最后的响应要发给客户端);而A收到ACK后,会进入“FIN_WAIT_2”状态,等待B说“我也发完了”。

3. 第三次挥手(B→A:“我也没数据要发了”)
B处理完所有剩余数据后,确认自己也没东西要发给A了,就会给A发一个FIN包,意思是“我这边也处理完了,发送通道也关闭了,不会再给你发新数据了”。
此时B进入“LAST_ACK”状态,等待A的最终确认。

4. 第四次挥手(A→B:“我知道你也没数据了”)
A收到B的FIN后,会给B回一个ACK包,意思是“你的意思我也收到了,那我们就彻底结束吧”。
此时A不会马上关闭连接,而是进入“TIME_WAIT”状态(前面聊过的,持续2倍MSL时间),确保B能收到这个ACK;而B收到ACK后,就会彻底关闭连接。

为什么是“四次”而不是“三次”?
核心原因是:TCP的连接是“双向的”(就像一条双向车道),关闭连接时需要分别关闭两个方向的通道。

  • 第一次和第二次挥手,是关闭A→B的方向;
  • 第三次和第四次挥手,是关闭B→A的方向。

中间B可能需要时间处理数据(B在“CLOSE_WAIT”状态时,就是在处理自己的剩余数据),不能合并成三次。

简单说,四次挥手就是“双方互相确认两次”的过程,既保证了数据不丢失,又给了双方处理收尾工作的时间,体现了TCP“可靠”的特点。

(3)为什么 TCP 挥手需要有 TIME_WAIT 状态?

要理解TCP挥手时的TIME_WAIT状态,我们可以从“TCP连接关闭的可靠性”和“网络环境的复杂性”两个角度来说,结合实际开发场景会更易懂。

先简单回顾TCP挥手过程
TCP连接关闭需要“四次挥手”:

  1. 主动关闭方(比如客户端)发送FIN包,告诉对方“我要关了”;
  2. 被动关闭方(比如服务器)收到后,回复ACK确认“我知道了”;
  3. 被动关闭方处理完剩余数据后,也发送FIN包“我也准备关了”;
  4. 主动关闭方收到后,回复ACK确认“好的,结束”。

TIME_WAIT状态就出现在主动关闭方发送最后一个ACK之后,会持续一段时间(通常是2倍的MSL,MSL是“数据包在网络中能存活的最长时间”,比如1分钟,所以TIME_WAIT可能持续2分钟)。

为什么需要TIME_WAIT?
核心原因:网络不是“绝对可靠”的,可能丢包;而且旧数据可能在网络里“迷路”后又突然出现,TIME_WAIT就是为了应对这两种问题。

1. 确保最后一个ACK能被对方收到,避免连接关闭不彻底
主动关闭发送的最后一个ACK(第四次挥手)可能会在网络中丢失。
如果没有TIME_WAIT,主动关闭方发送ACK后直接关闭连接,一旦这个ACK丢了,被动关闭方会因为没收到确认,超时后重新发送FIN包。这时候主动关闭方已经“下线”,没人处理这个重传的FIN,被动关闭方就会一直等,连接永远关不干净。
而有了TIME_WAIT,主动关闭方会在这段时间内“待命”:如果收到被动关闭方重传的FIN,就重新发送ACK,直到对方确认关闭。

2. 防止“旧数据包”干扰新连接
TCP连接是通过“四元组”(源IP、源端口、目的IP、目的端口)唯一标识的。比如客户端用192.168.1.1:8080连接服务器10.0.0.1:80,这个四元组就是一个唯一连接。
假设主动关闭方(比如客户端)刚关闭连接,马上用相同的四元组建立新连接。但网络中可能还残留着上一次连接没发完的“旧数据包”(比如因为网络延迟,绕了远路)。这些旧包如果进入新连接,会被误认为是新数据,导致数据混乱(比如业务逻辑错误、数据解析失败)。

TIME_WAIT持续2倍MSL的意义就在这:

  • MSL是数据包的最大存存活时间,1倍MSL确保网络中残留的旧包能“自然死亡”;
  • 再留1倍MSL,确保对方发送的最后一个FIN(如果重传)也能被处理。
    等TIME_WAIT结束,旧包已经消失,用相同四元组建立新连接就安全了。

对Java开发的实际影响
比如服务器用ServerSocket绑定了8080端口,关闭后马上重启,可能会报“地址已在使用”(Address already in use)。
这就是因为主动关闭的服务器进程还处于TIME_WAIT状态,系统会保留这个端口一段时间,防止新连接被旧包干扰。此时可以通过设置SO_REUSEADDR参数(serverSocket.setReuseAddress(true))允许端口复用,避免启动失败。

(4)TCP 超时重传机制是为了解决什么问题?

TCP的超时重传机制,你可以理解成“网络版的快递跟踪与补发”,核心是解决“数据包在传输过程中丢了没人管”的问题,保证数据能可靠到达。

咱们先想个场景:你用Java程序通过Socket给服务器发数据,比如一个订单信息的数据包。但网络不是绝对可靠的——可能因为线路拥堵、设备故障,·这个包在半路丢了,服务器压根没收到·。这时候如果没有任何机制,发送方永远不知道“对方没收到”,数据传输就卡在这了,你的程序可能就一直等结果,最后超时报错。

超时重传就是来解决这个问题的:
发送方发完一个数据包后,会启动一个计时器(就像给快递设了个“预计送达时间”)。如果在计时器到期前,没收到接收方返回的“确认收到”消息(ACK),就默认这个包丢了,立刻重新发送一份。

对Java开发者来说,这个机制是TCP“可靠性”的核心保障之一。比如你用HttpClient调用接口时,哪怕中间有数据包丢失,超时重传会默默帮你补传,直到成功或重试次数耗尽——你不需要在代码里手动处理“丢包重发”,但底层正是靠这个机制,让你的请求能在复杂的网络环境中尽量成功到达。

简单说,超时重传就是TCP的“保底措施”:不管因为什么原因丢了包,只要等不到确认,就重新发,直到对方收到为止,避免数据传输因为丢包而彻底失败。

(5)TCP 滑动窗口的作用是什么?

TCP的滑动窗口可以理解成“数据传输的智能传送带”,它的核心作用是让数据发送和接收更高效、更稳定,避免“发得太快对方接不住”或“发得太慢浪费网络”的问题。

滑动窗口的3个核心作用:

1. 实现“批量发送”,提高传输效率
不用等一个数据包的确认消息回来再发下一个,而是可以连续发送“窗口大小”范围内的多个包。比如窗口大小是5,发送方可以一口气发5个包,等对方确认后,窗口往前“滑”,再发接下来的5个。这就像流水线作业,比“逐个确认”快得多。

2. 匹配接收方的“处理能力”,防止数据溢出
接收方的缓冲区(临时存数据的地方)大小有限。滑动窗口的大小由接收方决定,发送方绝不会超过这个窗口发数据,避免对方“接不过来”导致数据丢失。比如接收方缓冲区快满了,就会把窗口调小(比如从5减到2),发送方就会放慢速度。

3. 动态调整速度,适配网络状态
窗口大小不是固定的,会根据实际情况变化:

  • 如果接收方处理得快、网络通畅,窗口会变大(比如从5升到8),发送方可以更快发送;
  • 如果接收方卡顿(比如缓冲区满了)或网络丢包,窗口会变小(比如从5降到3),发送方放慢速度。

对Java开发者的意义:
你写的Java程序(比如用Socket通信、调用HTTP接口)底层都依赖TCP滑动窗口。比如:

  • 当你用OutputStream发数据时,不用手动控制“一次发多少”,TCP会通过滑动窗口自动调节;
  • 即使对方服务器处理较慢,滑动窗口也会让你的程序“智能减速”,不会因为发得太快导致数据丢失,省去了你在代码里手动处理“流量控制”的麻烦。

(6)TCP/IP 四层模型是什么?

TCP/IP四层模型可以理解成“网络世界的简化版工作流程”——它在OSI七层模型的基础上做了合并,用更务实的方式划分了数据传输的分工,就像把快递流程里“相似的步骤合并成一个岗位”,让实际操作更高效。
1. 应用层(“用户和商家的沟通层”)

  • 作用:直接处理用户的具体需求,比如你用浏览器逛淘宝(HTTP协议)、发消息联系卖家(IM协议),这些“看得见的交互”都由应用层负责。

2. 传输层(“负责把货送完整的快递员”)

  • 作用:确保数据“完整、有序”地从一个应用传到另一个应用。比如把大文件拆成小数据包,给每个包编号,对方收到后按编号拼接;如果丢了包,会要求重发(TCP协议的核心功能)。

3. 网络层(“规划送货路线的导航系统”)

  • 作用:给数据“指路”,通过IP地址定位目标设备(比如你的手机要连服务器,网络层会计算“从你的路由器到服务器机房”的最优路径)。

4. 网络接口层(“实际运输的工具和路面”)

  • 作用:处理最底层的物理传输,包括网线、Wi-Fi的信号传递,以及相邻设备间的连接(比如你的电脑和路由器之间、路由器和基站之间)。

(7)OSI 七层模型是什么?

从“寄快递”看七层模型(从用户端到接收端,自顶向下)

1. 应用层(用户直接接触的“寄件软件”)
作用:直接对接用户的需求,比如你用浏览器访问网页、发邮件,这些操作背后的数据传递,都是应用层在“发起请求”。

2. 表示层(给数据“打包美化”)
作用:处理数据的格式,比如把文字转成二进制、压缩文件、加密(比如HTTPS的加密就在这层附近),确保接收方看得懂。

3. 会话层(“约定配送规则”)
作用:建立、管理和结束两个设备之间的“对话”,比如确定“这次数据传输从什么时候开始,到什么时候结束”,避免混乱。

4. 传输层(“负责送货上门的快递员”)
作用:确保数据完整到达,比如把大文件拆成小包裹(分片),对方收到后再拼起来;如果中途丢了包裹,会要求重发(比如TCP协议的重传机制)。

5. 网络层(“规划运输路线的调度中心”)
作用:找最优路径,比如你的数据从北京传到上海,网络层会决定走“北京→济南→南京→上海”还是其他路线,核心是“选路”(通过IP地址定位目标设备)。

6. 数据链路层(“路面交通规则”)
作用:在相邻设备间“安全传递”,比如两台路由器之间、电脑和路由器之间,确保数据在“小范围路段”不冲突、不错传(通过MAC地址识别相邻设备)。

7. 物理层(“实际的运输工具和线路”)
作用:传递最原始的电信号、光信号,比如网线里的电信号、光纤里的光信号,是数据传输的“物理载体”。

(8)从网络角度来看,用户从输入网址到网页显示,期间发生了什么?

第一步:输入网址
浏览器要做的是“把名字翻译成门牌号”,这个过程叫DNS解析:

  • 浏览器先查自己的“小本本”(本地缓存),如果之前访问过,可能记下了对应的IP,直接用;
  • 没记住?就问你家路由器(本地DNS服务器),路由器也记不住的话,会层层向上问“上级DNS服务器”(比如运营商的DNS),直到找到这个域名对应的IP地址。

第二步:建立连接
拿到IP地址后,浏览器要和目标服务器“搭个桥”,才能传递信息。网络里用TCP协议来经过三次握手完成连接。
如果网址是https开头(带小锁标志),这一步还会多一个“暗号协商”环节(TLS握手):双方先交换“身份证”(证书)确认身份,再商量一个只有彼此知道的“暗号”(加密密钥),确保后面说的话只有对方能听懂(防止被偷听)。
第三步:发送请求
连接打通后,浏览器会给服务器发一个“订单”,也就是HTTP请求。这个请求里包含:

  • 你要的具体内容;
  • 你的请求头,比如浏览器类型、能接受的文件格式;
  • 可能还有“附加信息”,比如登录后的Cookie

第四步:服务器处理并返回响应
服务器(比如Java程序员熟悉的Tomcat、Nginx)收到请求后,会按流程处理:

  • 可能去查数据库(比如查商品信息),可能调用业务逻辑(比如算价格);
  • 处理完后,生成响应内容,通常是HTML、CSS、JS代码,可能还有图片、视频等资源;
  • 最后把这些内容打包成HTTP响应,加上“说明标签”(响应头,比如内容类型、长度),发回给浏览器。

第五步:浏览器渲染
浏览器收到响应后,开始“拆餐盒”:

  • 先解析HTML,生成“骨架”(DOM树);
  • 解析CSS,给骨架“穿衣服”(CSSOM树);
  • 执行JS,让页面“动起来”(比如点击按钮、加载更多内容);
  • 最后把这些整合起来,渲染出你看到的完整网页。

(9)什么是物理地址,什么是逻辑地址?

逻辑地址是程序在执行时使用的地址,它是相对于程序起始地址的偏移量;而物理地址是内存芯片中实际存在的、唯一的地址。逻辑地址需要经过操作系统和内存管理单元(MMU)的转换,才能映射到相应的物理地址,从而实现对内存单元的访问。

(10)到底什么是 TCP 连接?

TCP连接,你可以理解成“两台设备之间建立的一条‘虚拟专线’”——它不是物理上的电线,而是通过一系列规则,让双方能“认得出彼此、守着规矩收发数据”的一种状态。
在真正传数据前,先通过“三次握手”确认双方都准备好了;传数据时,按约定的规则保证可靠(比如丢了重发、乱了重排);传完后,通过“四次挥手”体面结束。这个从“准备好”到“结束”的整个过程,就叫“TCP连接”。

(11)HTTP 1.0 和 2.0 有什么区别?

HTTP/1.0 版本主要增加以下几点:

  • 增加了HEAD、POST等新方法。
  • 增加了响应状态码。
  • 引入了头部,即请求头和响应头。
  • 在请求中加入了HTTP版本号。
  • 引入了 Content-Type ,使得传输的数据不再限于文本。

HTTP/1.1 版本主要增加以下几点:

  • 新增了连接管理即 keepalive ,允许持久连接。
  • 支持 pipeline,无需等待前面的请求响应,即可发送第二次请求。
  • 允许响应数据分块(chunked),即响应的时候不标明Content-Length,客户端就无法断开连接,直到收到服务端的 EOF ,利于传输大文件。
  • 新增缓存的控制和管理。
  • 加入了 Host 头,用在你一台机子部署了多个主机,然后多个域名解析又是同一个 IP,此时加入了 Host 头就可以判断你到底是要访问哪个主机。

HTTP/2 版本主要增加以下几点:

  • 是二进制协议,不再是纯文本。
  • 支持一个 TCP 连接发起多请求,移除了 pipeline。
  • 利用 HPACK 压缩头部,减少数据传输量。
  • 允许服务端主动推送数据

(12)HTTP 2.0 和 3.0 有什么区别?

HTTP 2.0和3.0都是为了让网络传输更快、更高效,但它们解决问题的思路和底层技术有很大不同,就像从“优化快递车”升级到“换运输路线”。
一、HTTP 2.0:解决“单车道堵车”
HTTP 1.x有个大问题:一次连接只能发一个请求,像“单车道”,前面的请求没处理完,后面的只能排队(“队头阻塞”)。
HTTP 2.0的核心是“多路复用”:

  • 把一个连接变成“多车道”,多个请求可以同时在一个连接里发,不用排队。
  • 还会把数据拆成小“帧”,标上编号,服务器收到后再按编号拼起来,效率大大提高。

另外,它还加了“服务器推送”:比如你请求一个网页,服务器知道你可能还需要CSS、JS文件,会主动一起发给你,不用等你再发请求要。

二、HTTP 3.0:连“马路”都换了
HTTP 2.0虽然解决了“单车道”问题,但它和1.x一样,底层用的是TCP协议。TCP有个麻烦:一旦传输中丢了一个“帧”,就会停下来等重传,这时候其他“车道”的帧也会被卡住(还是“队头阻塞”,只是从“请求级”变成了“帧级”)。
HTTP 3.0直接换掉了底层协议,不用TCP了,改用QUIC协议(基于UDP):

  • QUIC天生支持“多路复用”,而且某个“车道”丢了帧,只会影响自己,其他“车道”正常传输,彻底解决了TCP的“队头阻塞”。
  • 连接建立更快:TCP建立连接要“三次握手”,QUIC可以在第一次通信时就完成加密和连接,节省时间(尤其对手机APP这类频繁短连接场景很友好)。
  • 更好的移动性:比如手机从WiFi切到4G,TCP连接会断,需要重新建立;QUIC能平滑切换,连接不中断。

(13)HTTP 和 HTTPS 有什么区别?

HTTP和HTTPS的区别,在于前者内容谁都能看,后者只有收件人能解密,核心差异在“安全性”。

HTTP:“裸奔”的传输:数据从客户端到服务器的路上,没有任何加密保护,容易被拦截、篡改。
它的网址以http://开头,传输效率稍高(因为少了加密步骤),但安全性几乎为零。
HTTPS:“加密”的传输:它的网址以https://开头,比HTTP多了一层加密保护,安全性大大提升,但传输时需要额外的加密和解密步骤,效率略低(现在硬件和协议优化后,差异已经很小)。

其他关键区别:细节和场景

端口不同

  • HTTP默认用80端口(比如http://example:80,通常省略不写)。
  • HTTPS默认用443端口(比如https://example:443,同样省略)。

证书要求

  • HTTP不需要证书,随便一个服务器都能搭。
  • HTTPS必须安装CA(权威证书机构)颁发的- SSL证书(就像“加密资格证”),证明服务器身份是可信的。如果证书无效或过期,浏览器会提示“不安全”。

适用场景

  • HTTP适合对安全性要求低的场景:比如静态网页展示(纯文字新闻)、内部非敏感系统。
  • HTTPS必须用于敏感场景:比如登录(输密码)、支付(银行卡信息)、电商(订单数据),现在几乎所有正规网站都强制用HTTPS(浏览器会给HTTP网站标“不安全”)。

(14)HTTP 与 RPC 之间的区别?

从功能特性上:

  • http是一个属于应用层的超文本传输协议,是万维网数据通信的基础,主要服务在网页端和服务端的数据传输上。
  • RPC是一个远程过程调用协议,它的定位是实现不同计算机应用之间的数据通信, 屏蔽通信底层的复杂性,让开发者就像调用本地服务一样完成远程服务的调用。

因此,这两个协议在定位层面就完全不同。

从实现原理上:

  • http协议是一个已经实现并且成熟的应用层协议,它定义了通信的报文格式Request Body和Request Header, 以及Response Body和Response Header。
    也就是说,符合这样一个协议特征的通信协议,才是http协议。
  • RPC只是一种协议的规范,它并没有具体实现,只有按照RPC通信协议规范实现的通信框架,

也就是RPC框架,才是协议的具体实现,比如Dubbo、gRPC等。

因此,我们可以在实现RPC框架的时候,自定义报文通信的协议规范、自定义序列化方式、自定义网络通信协议的类型等等
因此,从这个层面来说,http是成熟的应用协议,而RPC只是定义了不同服务之间的通信规范。

从应用层面上:

  • http协议和实现了RPC协议的框架都能够实现跨网络节点的服务之间通信。
    并且他们底层都是使用TCP协议作为通信基础。
  • 但是,由于RPC只是一种标准协议,只要符合RPC协议的框架都属于RPC框架。
    因此,RPC的网络通信层也可以使用HTTP协议来实现,比如gRPC、OpenFeign底层都采用了http协议。

(15)TCP 和 UDP 有什么区别?

场景TCP的做法UDP的做法
发消息前先“打招呼”(三次握手),确认对方准备好了才发直接发,不管对方在不在、有没有准备好
发的过程中每发一批数据,等对方说“收到了”才继续(确认机制);如果超时没收到回复,就重发发完就不管,丢了也不重发
数据量大时自动拆分小包,对方收到后再拼起来(拆包/粘包处理)不管大小,直接发(如果超过网络限制,可能被底层拆分,但UDP本身不管拼接)
对方忙不过来时主动放慢速度(流量控制、拥塞控制),避免对方“累死”不管对方忙不忙,照发不误,可能导致对方接收的数据“溢出”丢失
发完后会“道别”(四次挥手),确保双方都处理完发完就断,不打招呼

(16)TCP 的粘包和拆包能说说吗?

TCP的粘包和拆包,就像快递打包时遇到的“包裹合并”和“包裹拆分”问题——数据在传输过程中,可能被TCP“重新包装”,导致接收方收到的数据包和发送方发送的不一致。

先搞懂:为什么会有粘包和拆包?
TCP是“流式传输”,就像一根水管,数据是连续不断地流过去的,没有天然的“数据包边界”。TCP为了提高效率,会根据网络情况动态调整发送策略:

  • 比如发送方连续发了几个小数据,TCP可能会把它们合并成一个大的数据包一起发(省流量、提效率),这就是粘包
  • 比如发送方发了一个很大的数据,超过了TCP一次能传的最大长度(MSS),TCP会把它拆成多个小数据包分开发,这就是拆包

对Java开发的影响:需要“拆包”逻辑:
如果我们直接用InputStream.read()读取TCP数据,可能会遇到粘包和拆包问题。
这时候就需要我们在应用层自己定义“消息边界”,比如:

  • 给每个消息加固定长度的“头部”,说明消息总长度(比如先读4个字节表示长度,再按长度读内容)。
  • 用特殊符号作为分隔符(比如每条消息结尾加“\r\n”)。

(17)Cookie、Session、Token 之间有什么区别?

Cookie、Session、Token 都是用来解决“用户登录后,服务器怎么记住他”的问题,但实现方式和适用场景完全不同。
1. Cookie:“贴在你身上的身份贴”
Cookie 是服务器给用户浏览器的“小纸条”,里面记着简单的身份信息(比如用户 ID、登录状态)。

  • 工作流程:你第一次登录成功后,服务器写一张小纸条(Cookie),让浏览器存起来。之后你每次访问这个网站,浏览器都会自动把这张纸条带给服务器,服务器一看纸条就知道“是你来了”。
  • 特点:
    • 存在用户的浏览器里(客户端),容量小(一般几 KB)。
    • 每次请求都会自动发送给服务器,可能泄露信息(所以不能存密码等敏感内容)。
    • 比如记住登录状态(“7天内自动登录”),就是靠 Cookie 实现的。

2. Session:“健身房前台的会员登记本”
Session 是服务器自己记的“会员档案”,里面存着用户的详细信息(比如用户名、权限)。

  • 工作流程:你登录后,服务器在自己的内存/数据库里建一个档案(Session),生成一个唯一的“档案编号”(SessionId),然后把这个编号通过 Cookie 发给你。之后你每次来,浏览器带着编号,服务器查编号对应的档案,就知道你是谁、有什么权限。
  • 特点:
    • 信息存在服务器(安全,能存敏感内容),但会占用服务器资源(用户多了,档案本就厚了)。
    • 依赖 Cookie 传递 SessionId(如果用户禁用 Cookie,就得用 URL 传编号,麻烦且不安全)。
    • 适合单体应用(比如一个 Tomcat 服务器),但在分布式系统中麻烦(多个服务器之间要同步档案,否则换个服务器就不认你了)

3. Token:“带防伪水印的临时通行证”
Token 是服务器给用户的“加密通行证”,里面直接包含用户身份信息(比如用户 ID、过期时间),且有防伪签名。

  • 工作流程:你登录后,服务器用密钥把你的信息加密成一张通行证(Token)发给你。之后你每次访问,直接带这张通行证,服务器解密后验签名(确认没被篡改),就能知道你是谁,不用查自己的档案本。
  • 特点:
    • 存在用户端(可以是浏览器、APP),服务器不用存信息(省资源)。
    • 不依赖 Cookie(可以放在请求头里),适合前后端分离、APP 等场景。
    • 适合分布式系统(多个服务器只要有相同的密钥,都能解密验证),但一旦发出就很难主动作废(除非服务器记录“黑名单”)。

核心区别总结

角度CookieSessionToken
存哪里客户端(浏览器)服务器(内存/数据库)客户端(浏览器/APP)
安全程度较低(易被篡改)较高(服务器掌控)中(加密防篡改,但信息可见)
服务器压力有(存信息)无(不存信息)
分布式适配无(只是传信息)难(需同步)易(密钥一致即可)
典型场景记住登录状态单体应用的用户会话前后端分离、APP、微服务

简单说:Cookie 是“客户端带的小纸条”,Session 是“服务器记的档案”,Token 是“加密的通行证”。实际开发中,单体应用常用 Session·分布式/前后端分离常用 Token,Cookie 则常用来辅助传递 SessionId 或存简单状态。

(18)线上 CPU 飙高如何排查?

线上 CPU 飙高排查可遵循“定位进程 → 定位线程 → 定位代码”的步骤,首先使用 top 命令查看占用 CPU 的高消耗进程。接着,在找到进程 ID (PID) 后,使用 top -H -p [pid] 命令查看进程内占用 CPU 最高的线程 ID,并将其转换为十六进制。最后,使用 jstack -l [pid] 命令导出堆栈信息,通过十六进制线程 ID 在堆栈日志中找到对应的 Java 代码行,从而定位到具体问题所在,如死循环或高耗时业务。

详细排查步骤:

1. 使用 top 命令定位高消耗进程:

  • 在命令行中输入 top 命令,查看当前系统中 CPU 使用率高的进程列表。
  • 找到占用 CPU 最高的进程,记录其 PID。

2. 使用 top -H -p [pid] 定位高消耗线程:

  • 在确认进程后,使用 top -H -p [pid] 命令(将 [pid] 替换为上一步找到的进程 ID)来查看该进程中各线程的 CPU 占用情况。
  • 确定占用 CPU 最高的线程,记录其线程 ID。

3. 将线程 ID 转换为十六进制:

  • 由于 jstack 命令通过十六进制线程 ID 来定位堆栈信息,因此需要将找到的线程 ID 转换为十六进制。
  • 在 Linux 环境下,可以使用命令 printf “%x\n” <线程ID> 来完成转换。

4. 使用 jstack 命令导出线程堆栈信息:

  • 使用 jstack -l [pid] 命令导出目标进程的线程堆栈信息。
  • 将输出重定向到一个文件中,如 jstack -l [pid] > jstack_log。

5. 在堆栈日志中定位具体代码:

  • 在导出的 jstack_log 文件中,使用 grep 命令搜索上一步转换得到的十六进制线程 ID。
  • 通过搜索结果,找到该线程的堆栈信息,其中会包含代码中的类名、方法名,甚至具体的代码行数,从而定位导致 CPU 飙高的具体业务逻辑代码。

可能导致 CPU 飙高的原因:

  • 死循环或无限循环:
    程序代码中存在的死循环导致 CPU 持续高强度运行。
  • 高耗时计算或复杂算法:
    代码中执行过于复杂或数据量巨大的计算,消耗大量 CPU 资源。
  • 频繁的 Full GC:
    大量内存对象创建导致频繁的垃圾回收,特别是 Full GC 过程会消耗大量的 CPU。

(19)TCP 是用来解决什么问题?

TCP的存在,本质上是为了解决“网络传输不可靠”的问题,让数据能“稳稳当当、按顺序”到达目的地。
TCP就是来解决这些“麻烦”的,具体做了三件核心事:
1. 保证“不丢”:丢了就重发

  • TCP会给每个数据包编上号,如果超过一定时间没收到回复,就默认这个包丢了,立刻重发,直到对方确认收到。

2. 保证“有序”:乱了就重排

  • 就算数据包在路上跑乱了顺序,TCP也会根据编号把它们重新排好队,再交给上层应用(比如你的浏览器、文件管理器),确保应用拿到的是完整、顺序正确的数据。

保证“不堵”:忙了就慢发

  • TCP会实时监测对方的接收能力:如果发现对方“忙不过来”(比如缓存快满了),就自动放慢发送速度;等对方空闲了,再加快速度,避免数据“堆积堵车”导致丢失。

本文标签: 计算机网络 面试题 热门 java