途游游戏(后端一面)
1. 传输层的报头是什么?
TCP 报头至少包含 20 字节的固定字段,若有选项字段可扩展至 60 字节。具体结构如下图:
例如,TCP 三次握手时,第一个报文的 SYN 标志位为 1,序号为随机值;第二个报文的 SYN 和 ACK 标志位均为 1,确认号为对方序号 + 1,通过这些字段完成连接建立。
UDP 是无连接协议,报头仅 8 字节,结构简洁,不包含可靠性相关字段,具体如下图:
UDP 的简洁性使其适用于实时场景(如视频通话、DNS 查询),但因无重传和排序机制,可能出现丢包或乱序。
2. TCP协议特点是什么?
TCP是可靠的传输协议,其设计围绕 “可靠传输” 和 “有序交互” 展开,确保数据从发送方到接收方的过程中 “不丢、不重、不错序”。
-
特点一:面向连接 。通信前必须 “握手” 建立关系,TCP 不像 UDP 那样 “发完就走”,而是在传输数据前必须通过三次握手建立连接,结束后通过四次挥手断开连接。
-
特点二:可靠传输 。TCP 通过多重机制保证数据可靠到达,核心手段包括:序号与确认机制:每个数据包都带有唯一序号,接收方收到后会返回确认报文(ACK)。若发送方超时未收到 ACK,会自动重传该数据包(解决丢包问题)。重传机制:针对两种场景触发重传:一是发送方等待超时(如网络延迟导致 ACK 未及时返回);二是接收方收到乱序或重复数据包时,通过 ACK 告知发送方 “需要重传某序号的包”(解决错序、重复问题)。
-
特点三:有序传输。TCP 会给每个数据包按发送顺序编号(如 1、2、3…),接收方收到后会按序号重新排序,再交给应用层(如浏览器、聊天软件)。假设发送方按顺序发送数据包 1、2、3,若接收方先收到 3,再收到 1、2,TCP 会将其整理为 1、2、3 的顺序后再交付,避免应用层处理混乱的数据。
-
特点四:流量控制。接收方的缓存空间有限(如手机内存不足),若发送方发送速度过快,接收方可能来不及处理,导致数据溢出丢失。TCP 通过滑动窗口机制解决这一问题:接收方在 ACK 报文中告知发送方自己的 “接收窗口大小”(即当前能接收的最大数据量)。发送方根据接收窗口动态调整发送速率,确保发送的数据量不超过接收方的处理能力。
-
特点五:拥塞控制。TCP 通过慢启动、拥塞避免、快重传、快恢复等算法,动态调整发送速率,防止因数据量过大导致网络拥塞(路由器缓存溢出、丢包激增)。其核心逻辑是:网络通畅时逐步加快发送速度,检测到拥塞(如丢包)时立即减速,平衡 “传输效率” 和 “网络稳定性”,同时保证多个 TCP 连接公平共享带宽。
TCP 的上述特点使其成为需要高可靠性场景(如网页浏览、文件传输、邮件发送)的首选协议,但也因此比 UDP 更复杂,额外的握手、确认、重传机制会带来一定的延迟和开销。相比之下,UDP 更适合实时性要求高(如视频通话、游戏)但可容忍少量丢包的场景。
3. TCP 拥塞控制的过程是怎样的?
整个过程可分为四个核心阶段:慢启动、拥塞避免、拥塞检测、快速恢复。
-
慢启动:连接建立时,
cwnd
初始化为1 个 MSS(最大段大小)。每收到一个 ACK,cwnd
增加 1 个 MSS(指数增长:1→2→4→8...)。比如初始cwnd=1
,发送 1 个段,收到 ACK 后cwnd=2
;再发送 2 个段,收到 2 个 ACK 后cwnd=4
,以此类推。当cwnd ≥ ssthresh
时,切换到拥塞避免阶段。
-
拥塞避免:每收到一轮 ACK(窗口内所有段的确认),
cwnd
增加 1 个 MSS/cwnd(线性增长:如cwnd=10
时,每轮 ACK 后cwnd=10+1=11
)。若发生拥塞(超时或 3 个重复 ACK),进入拥塞检测。
-
拥塞检测:当检测到拥塞时,TCP 通过以下两种方式应对:
-
超时重传:长时间未收到 ACK,通常意味着网络严重。
ssthresh
设为当前cwnd
的一半(即ssthresh = cwnd/2
)。cwnd
重置为1 个 MSS,重新进入慢启动。
-
-
-
快速重传:收到 3 个相同 ACK,表明某个段可能丢失,但网络仍可工作。
ssthresh
设为当前cwnd
的一半(即ssthresh = cwnd/2
)。cwnd
设为ssthresh + 3*MSS
(快速恢复),进入快速恢复阶段。
-
-
快速恢复:在轻微拥塞(3 个重复 ACK)后快速恢复传输,避免长时间等待。每收到一个额外的重复 ACK,
cwnd
增加 1 个 MSS(补偿丢失的段)。收到新 ACK 后,cwnd
设为ssthresh
,进入拥塞避免阶段。
4. TCP 为什么需要进行拥塞控制 ?
主要是为了避免网络因数据 “泛滥” 而崩溃的核心机制。简单说,若无拥塞控制,大量 TCP 连接会疯狂发送数据,导致路由器缓存塞满、丢包激增,最终所有数据都无法高效传输 , 这就像高速公路上的汽车无视交通规则,结果大家都堵在原地。
网络的承载能力是有限的,就像高速公路的车道数量固定。当 TCP 发送方(如手机、电脑)向网络中注入的数据量超过中间设备(路由器、交换机)的处理能力时,就会引发拥塞,具体表现为:
-
路由器缓存溢出:路由器的临时缓存空间有限,若数据到达速度超过转发速度,多余的数据包会被丢弃(丢包)。
-
连锁反应:丢包会导致 TCP 发送方触发重传机制,进一步增加网络中的数据量,形成 “丢包→重传→更拥塞” 的恶性循环
-
公平性缺失:没有拥塞控制时,发送速度快的设备会抢占更多带宽,导致其他设备 “饿死”,网络资源分配失衡。
举个例子:当 100 台电脑同时通过 TCP 向同一服务器发送大文件,若不限制速度,路由器很快会被数据包淹没,原本 1 秒能传完的小数据(如聊天消息)可能需要几十秒,甚至超时失败。
5. https加密流程是怎样的?
传统的 TLS 握手基本都是使用 RSA 算法来实现密钥交换的,在将 TLS 证书部署服务端时,证书文件其实就是服务端的公钥,会在 TLS 握手阶段传递给客户端,而服务端的私钥则一直留在服务端,一定要确保私钥不能被窃取。
在 RSA 密钥协商算法中,客户端会生成随机密钥,并使用服务端的公钥加密后再传给服务端。根据非对称加密算法,公钥加密的消息仅能通过私钥解密,这样服务端解密后,双方就得到了相同的密钥,再用它加密应用消息。
我用 Wireshark 工具抓了用 RSA 密钥交换的 TLS 握手过程,你可以从下面看到,一共经历了四次握手:
TLS 第一次握手
首先,由客户端向服务器发起加密通信请求,也就是 ClientHello 请求。在这一步,客户端主要向服务器发送以下信息:
-
(1)客户端支持的 TLS 协议版本,如 TLS 1.2 版本。
-
(2)客户端生产的随机数(Client Random),后面用于生成「会话秘钥」条件之一。
-
(3)客户端支持的密码套件列表,如 RSA 加密算法。
TLS 第二次握手
服务器收到客户端请求后,向客户端发出响应,也就是 SeverHello。服务器回应的内容有如下内容:
-
(1)确认 TLS 协议版本,如果浏览器不支持,则关闭加密通信。
-
(2)服务器生产的随机数(Server Random),也是后面用于生产「会话秘钥」条件之一。
-
(3)确认的密码套件列表,如 RSA 加密算法。(4)服务器的数字证书。
TLS 第三次握手
客户端收到服务器的回应之后,首先通过浏览器或者操作系统中的 CA 公钥,确认服务器的数字证书的真实性。
如果证书没有问题,客户端会从数字证书中取出服务器的公钥,然后使用它加密报文,向服务器发送如下信息:
-
(1)一个随机数(pre-master key)。该随机数会被服务器公钥加密。
-
(2)加密通信算法改变通知,表示随后的信息都将用「会话秘钥」加密通信。
-
(3)客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个摘要,用来供服务端校验。
上面第一项的随机数是整个握手阶段的第三个随机数,会发给服务端,所以这个随机数客户端和服务端都是一样的。
服务器和客户端有了这三个随机数(Client Random、Server Random、pre-master key),接着就用双方协商的加密算法,各自生成本次通信的「会话秘钥」。
TLS 第四次握手
服务器收到客户端的第三个随机数(pre-master key)之后,通过协商的加密算法,计算出本次通信的「会话秘钥」。
然后,向客户端发送最后的信息:
-
(1)加密通信算法改变通知,表示随后的信息都将用「会话秘钥」加密通信。
-
(2)服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个摘要,用来供客户端校验。
至此,整个 TLS 的握手阶段全部结束。接下来,客户端与服务器进入加密通信,就完全是使用普通的 HTTP 协议,只不过用「会话秘钥」加密内容。
6. https中证书是怎么拿到的,里面包含什么?
证书的获取需经过权威机构认证,获取 HTTPS 证书的过程本质是 “向权威机构证明服务器身份并申请加密凭证”,具体步骤如下:
-
生成密钥对与证书请求:服务器管理员先通过工具(如 OpenSSL)生成一对非对称密钥(私钥 + 公钥),私钥需严格保管,公钥将包含在证书中。基于公钥生成证书签名请求,包含网站域名、申请者信息(如公司名称)等,提交给证书颁发机构(CA)。
-
CA 验证申请者身份:CA 会根据证书类型(如域名验证型 DV、组织验证型 OV、扩展验证型 EV)进行不同程度的审核,比如DV 证书仅验证域名所有权(如通过邮箱或 DNS 解析记录),10 分钟内可完成,而OV/EV 证书需验证申请者的真实组织信息(如营业执照),EV 还需更严格的法律资质审核,耗时 1-5 天。
-
部署证书到服务器:管理员将证书与服务器私钥配对,配置到 Web 服务器(如 Nginx、Apache)中,重启服务后,网站即可启用 HTTPS。
HTTPS 证书是一个结构化的加密文件,核心内容可分为身份标识、加密信息和验证依据三类,具体包括:
信息类别 | 具体内容 | 作用 |
---|---|---|
身份信息 |
网站域名(如www.example.com,支持单域名、多域名或通配符域名)、申请者名称、组织信息(OV/EV 证书) |
证明 “该证书属于哪个网站 / 机构”,防止钓鱼网站冒充 |
加密信息 |
服务器的公钥(用于客户端加密数据)、证书支持的加密算法(如 RSA、ECC) |
客户端用公钥加密数据,只有对应私钥的服务器能解密,保障传输安全 |
验证依据 |
签发机构(CA)名称、CA 的数字签名、证书有效期(起始 / 结束时间) |
CA 签名确保证书未被篡改;有效期限制证书的使用时长,需定期更新 |
此外,证书还可能包含证书链信息(上级 CA 的中间证书),用于客户端追溯到根 CA,完成整个信任链的验证。
7. 证书为什么放在第三方?
把证书交给第三方(即证书颁发机构 CA),本质是为了建立一套全网通用的 “信任机制, 如果没有中立权威的第三方背书,服务器自己签发的证书就成了 “自说自话”,无法让客户端相信其身份真实性。
假设服务器自己生成证书并宣称 “我是官网”,客户端(如浏览器)会面临两个无法解决的问题:
-
身份无法验证:客户端无法判断这个证书是不是 “钓鱼网站” 伪造的,因为任何人都能生成自签证书并声称自己是合法网站。
-
信任链断裂:客户端的操作系统或浏览器预装了全球知名 CA 的根证书,但不会认可 “自签证书”,导致浏览器直接提示 “不安全”。
自签证书就像 “自己给自己发身份证”,在网络世界里毫无说服力。
CA 作为第三方,存在的意义是用自己的 “权威信用” 为服务器身份背书,具体体现在:
-
验证服务器身份的真实性:CA 会严格审核申请者的信息(如域名所有权、企业资质),确保证书只颁发给 “真正的所有者”。例如,申请
www.bank.com
的证书时,CA 会通过 DNS 解析、邮箱验证等方式确认申请者确实拥有该域名,防止骗子冒用银行域名签发证书。 -
用 CA 的信用担保证书有效性:CA 签发证书时,会用自己的根私钥对证书进行签名。由于客户端(浏览器、操作系统)预装了 CA 的根证书(包含根公钥),可以通过验证签名确认 “该证书确实由可信 CA 签发,且未被篡改”。这里的逻辑是:客户端信任 CA → CA 信任服务器 → 客户端因此信任服务器。
-
维护全网统一的信任标准:全球 CA 需遵守国际标准,确保审核流程、证书格式、加密算法统一。这种标准化让不同厂商的客户端(如 Chrome、Firefox、Windows)都能识别和验证证书,避免 “各立标准” 导致的信任混乱。
证书交给第三方 CA,不是技术限制,而是网络信任体系的必然选择。CA 通过 “验证身份 - 权威签发 - 责任约束” 的流程,解决了 “服务器如何让客户端相信自己” 的核心问题,让 HTTPS 的加密功能真正落地为 “安全且可信” 的传输。没有这个中立第三方,互联网的身份验证就会陷入 “公说公有理” 的混乱,安全无从谈起。
8. Java中list由哪些子类?
List
是一个接口,继承自Collection
接口,代表有序、可重复的元素集合。常见的实现类有 ArrayList、LinkedList、Vector 等。
9. ArrayList数据结构是什么样的?具体是怎么实现的
ArrayList核心设计思想是动态数组,通过自动扩容机制,让数组可以像 “可变长度” 一样使用。
ArrayList 的底层是一个Object 类型的数组,用于存储元素:
transient Object[] elementData; // 存储元素的数组
private int size; // 实际存储的元素数量
-
transient 关键字:表示该字段不参与序列化,避免数组中空闲位置被序列化。
-
size 变量:记录 ArrayList 中实际存储的元素数量,与数组长度(
elementData.length
)不同。
当添加元素时,若数组容量不足,ArrayList 会触发扩容机制:
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity); // 默认初始容量为10
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++; // 记录结构修改次数
if (minCapacity - elementData.length > 0)
grow(minCapacity); // 触发扩容
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // 新容量 = 旧容量 * 1.5
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity; // 若新容量仍不足,直接使用minCapacity
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity); // 处理超大容量的特殊情况
elementData = Arrays.copyOf(elementData, newCapacity); // 创建新数组并复制元素
}
扩容步骤:
-
计算最小所需容量:若数组为空,首次扩容为默认容量 10;否则为当前
size+1
。 -
确定新容量:新容量为旧容量的1.5 倍(
oldCapacity + (oldCapacity >> 1)
)。 -
检查新容量是否足够:若仍不足,则直接使用最小所需容量。
-
处理最大容量限制:若新容量超过
Integer.MAX_VALUE - 8
,调用hugeCapacity()
处理。 -
创建新数组并复制元素:使用
Arrays.copyOf()
复制原数组元素到新数组。
10. 有个电脑,向另一个电脑通过程序发送数据,这个数据在硬件层面怎么流动的,经过那些步骤?
当一台电脑通过程序向另一台电脑发送数据时,看似简单的操作背后,是从软件到硬件层层协作的复杂流程。从应用程序发出数据,到最终通过物理介质抵达目标设备,每一步都依赖硬件的精准配合。
1、首先数据出发:从应用程序到网络协议栈:
-
应用层:数据的 “原始形态”:程序(如聊天软件、浏览器)生成的数据(如文字、图片片段)首先在应用层被处理。此时数据是 “应用能理解的格式”,比如 HTTP 协议的请求报文、TCP 协议的数据流。应用层会给数据加上应用层头部(如 HTTP 的请求方法、URL),标记数据的用途。
-
传输层:给数据 “贴标签”,确保准确送达:数据被传递到传输层(如 TCP 或 UDP 协议),这里会进行两件关键操作:加上传输层头部,包含源端口和目标端口(比如客户端用 5000 端口发,目标服务器用 80 端口收),用于区分同一设备上的不同程序。若用 TCP,还会处理可靠传输(如三次握手建立连接、确认重传机制),确保数据完整;若用 UDP,则仅简单封装,追求速度。
-
网络层:规划 “路线图”,确定目标地址:数据进入网络层(主要是 IP 协议),被封装成IP 数据包:头部包含源 IP 地址(发送方电脑的 IP)和目标 IP 地址(接收方电脑的 IP)。网络层会通过路由算法(如查找路由表)确定数据的下一跳设备(通常是网关或路由器)。
-
数据链路层:给数据包 “套外壳”,适配物理层:数据链路层(如以太网协议)将 IP 数据包封装成帧:头部添加源 MAC 地址(发送方网卡的物理地址)和目标 MAC 地址(下一跳设备的 MAC 地址,如路由器的 MAC)。MAC 地址是网卡的唯一标识,像硬件的 “身份证”。尾部添加校验码(如 CRC),用于检测数据在传输中是否损坏。
2、硬件接力:从网卡到物理介质的 “传递赛”
-
网卡:数据的 “出口” 与 “翻译官”:数据链路层处理完成后,帧被传递到网卡(网络接口卡)。网卡的核心作用是:将数字信号(0 和 1)转换为物理信号,通过网线、无线电磁波等物理介质发送信号。发送前,网卡会检查目标 MAC 地址是否与自身匹配(若不匹配则忽略,除非是广播帧)。
-
物理层:信号的 “高速公路”:物理介质(网线、Wi-Fi、光纤等)负责传输物理信号。
-
中间设备:数据的 “中转站”:数据从源设备到目标设备,通常需经过多个中间硬件:交换机:工作在数据链路层,根据 MAC 地址在局域网内转发帧(如同一办公室内的设备通信)。路由器:工作在网络层,根据 IP 地址在不同网络间转发数据包(如从家庭网络到互联网)。路由器会修改帧的源 MAC 地址(替换为自身出口网卡的 MAC),但保持 IP 地址不变。防火墙 / 网关:可能对数据进行过滤(如拦截不安全的端口),确保网络安全。
3、数据抵达:目标设备的 “接收与解析”:
-
目标网卡:信号的 “进口” 与 “解码”:目标设备的网卡接收到物理信号后,先将其转换为数字信号,再检查帧的目标 MAC 地址是否与自身匹配。若匹配,去除帧头部和尾部,将剩下的 IP 数据包传递给网络层。
-
协议栈反向解析:层层 “拆包”:网络层:去除 IP 头部,检查目标 IP 是否为本机,然后将数据传递给传输层。传输层:去除 TCP/UDP 头部,根据目标端口找到对应的应用程序(如 80 端口对应浏览器)。应用层:最终接收数据,按照应用协议(如 HTTP、FTP)解析,呈现给用户(如显示网页内容、弹出消息)。
从软件生成数据,到硬件将信号传递到目标设备,整个过程就像 “包装礼物”:应用层是 “礼物本身”,传输层、网络层、数据链路层依次加上 “包装纸和标签”(头部信息),由网卡将 “礼物” 转换成物理信号,通过网线 / 无线等介质运输,途中经交换机、路由器等 “中转站” 接力,最终由目标设备层层 “拆包”,取出原始数据。每一层硬件和协议的协作,确保了数据从 “发出” 到 “收到” 的准确与高效。
11. NAT原理是什么?
NAT(网络地址转换) 的核心作用,就是解决 IPv4 地址耗尽的问题:通过将局域网内的私有 IP 地址转换为唯一的公网 IP 地址,让多台设备共享一个公网出口,同时隐藏内部网络结构,提升安全性。
为什么需要NAT技术?IPv4 地址总数约 43 亿个,早已分配殆尽。而家庭、企业中往往有多个设备(手机、电脑、智能家电)需要联网,若每个设备都分配一个公网 IP,地址会迅速耗尽。NAT 的出现正是为了 “精打细算”:用少量公网 IP(通常 1 个)对应大量私有 IP(如 192.168.x.x、10.x.x.x),实现多设备共享上网。
NAT 的核心是在路由器(或网关)上进行IP 地址和端口的转换,具体流程可分为 “出站” 和 “入站” 两步:
-
出站:私有 IP→公网 IP:当局域网设备(如 192.168.1.100)访问公网服务器(如 203.0.113.5)时,数据包先到达路由器。路由器将数据包的源 IP(192.168.1.100)替换为自身公网 IP(如 123.123.123.123),同时分配一个临时端口(如 50001),并在 NAT 表中记录对应关系:
192.168.1.100:端口A → 123.123.123.123:50001
。转换后的数据包发送到公网服务器,服务器只知道 “来自 123.123.123.123:50001” -
入站:公网 IP→私有 IP:公网服务器返回数据时,目标 IP 是路由器公网 IP(123.123.123.123),目标端口是 50001。路由器查询 NAT 表,找到 “50001 对应 192.168.1.100: 端口 A”,将目标 IP 和端口替换为私有 IP 和原端口,再转发给局域网设备。
通过这种 “替换 - 记录 - 反向查找” 的机制,NAT 实现了多设备对单一公网 IP 的共享。