面试题答案
一键面试整体架构
- 发送端:
- 数据分片:将待发送的大数据块分割成合适大小的数据包,以便于在网络中传输。
- 序列号生成:为每个数据包分配唯一的序列号,用于接收端重组数据以及检测丢包。
- 发送队列:维护一个待发送数据包的队列,按照一定策略(如优先级)从队列中取出数据包进行发送。
- 定时器管理:为每个已发送但未确认的数据包启动定时器,用于检测超时重传。
- 接收端:
- 接收缓冲区:用于暂存接收到的数据包。
- 序列号校验:对接收到的数据包进行序列号校验,丢弃重复的数据包,将有序的数据包传递给重组模块。
- 数据重组:根据序列号将接收到的数据包重新组合成原始数据。
- 确认发送:向发送端发送确认消息,告知已成功接收数据包。
核心算法
- 超时重传算法:
- 发送端为每个数据包设置一个初始超时时间(如 RTO,Round - Trip Time 的估计值)。当定时器超时且未收到该数据包的确认消息时,重传该数据包,并适当增加超时时间(如采用指数退避算法,每次重传时将超时时间翻倍)。
- 拥塞控制算法:
- 慢启动:开始时,发送端以一个较小的窗口大小(如一个数据包)发送数据。每收到一个确认消息,窗口大小增加一个数据包。
- 拥塞避免:当窗口大小达到一定阈值(ssthresh)后,进入拥塞避免阶段。此时每收到全部确认消息,窗口大小增加一个数据包,而不是成倍增加。
- 拥塞检测与处理:如果出现超时重传,说明可能发生了拥塞,此时将阈值减半,窗口大小重置为 1,重新进入慢启动阶段。
- 数据包排序与重组算法:
- 接收端根据数据包的序列号对其进行排序。使用一个缓存区暂存乱序到达的数据包,当接收到一个数据包时,检查其序列号是否与期望的序列号一致。如果一致,将其添加到重组数据中,并检查缓存区中是否有后续期望的数据包,若有则依次添加。
利用 Node.js 的 UDP 模块特性
- 创建 UDP 套接字:
const dgram = require('dgram'); const socket = dgram.createSocket('udp4');
- Node.js 的
dgram
模块提供了简单易用的 UDP 套接字创建方法,无论是 UDP4 还是 UDP6 都能轻松创建。
- Node.js 的
- 发送数据:
const message = Buffer.from('Hello, UDP!'); socket.send(message, 0, message.length, 41234, '127.0.0.1', (err) => { if (err) { console.error(err); } });
- 通过
send
方法可以方便地向指定地址和端口发送数据,并且可以在回调函数中处理发送过程中的错误。
- 通过
- 接收数据:
socket.on('message', (msg, rinfo) => { console.log(`Received: ${msg.toString()} from ${rinfo.address}:${rinfo.port}`); });
- 使用
message
事件监听接收到的数据,rinfo
包含了发送端的地址和端口信息,便于接收端处理和回复。
- 使用
- 设置套接字选项:
socket.setBroadcast(true);
- 可以通过
setBroadcast
等方法设置 UDP 套接字的一些特性,如设置为广播模式,以适应不同的网络通信需求。
- 可以通过