Appearance
3.如何理解 UDP 和 TCP?
1.UDP 协议
UDP(User Datagram Protocol)是一种简单的面向数据报的通信协议。是一个无连接、不保证可靠性的传输层协议。(你让我发什么就发什么!发出去就不管了)应用层交下来的报文,不进行合并与拆分,只是在其上面加上首部后就交给下层网络层。
特点
无连接性: 不需要在通信开始前建立连接。它以数据报为单位进行通信,每个数据报都是独立的,不依赖于其他数据报。通信的双方之间不会维护连接状态。
不提供可靠性: 如果在传输过程中发生数据包丢失或损坏,UDP 不负责重发丢失的数据包,也不会检测或处理重复的数据包。在网络不稳定或拥塞的情况下可能会导致数据包丢失。
无流量控制: 发送方可以以任何速度发送数据,而不会根据网络拥塞情况进行调整。在网络拥塞时产生更多的数据包丢失。
无序处理: UDP 不提供数据包的有序处理机制。如果数据包的到达顺序发生乱序,不会对其进行重新排序,而是将它们按照原样传递给应用程序。
低开销: UDP 的报头相对较小,在通信时的开销较低,适用于需要低时延和快速通信的应用,如实时音频和视频传输。
DHCP
协议、DNS
协议、QUIC
协议等 都是基于UDP
的 (场景:处理速度快,可以丢包的情况)
server.js
js
var dgram = require("dgram");
var socket = dgram.createSocket("udp4");
socket.on("message", function (msg, rinfo) {
console.log(msg.toString());
console.log(rinfo);
socket.send(msg, 0, msg.length, rinfo.port, rinfo.address);
});
socket.bind(41234, "localhost");
var dgram = require("dgram");
var socket = dgram.createSocket("udp4");
socket.on("message", function (msg, rinfo) {
console.log(msg.toString());
console.log(rinfo);
socket.send(msg, 0, msg.length, rinfo.port, rinfo.address);
});
socket.bind(41234, "localhost");
client.js
js
var dgram = require("dgram");
var socket = dgram.createSocket("udp4");
socket.on("message", function (msg, rinfo) {
console.log(msg.toString());
console.log(rinfo);
});
socket.send(
Buffer.from("helloworld"),
0,
5,
41234,
"localhost",
function (err, bytes) {
console.log("发送了个%d字节", bytes);
}
);
socket.on("error", function (err) {
console.error(err);
});
var dgram = require("dgram");
var socket = dgram.createSocket("udp4");
socket.on("message", function (msg, rinfo) {
console.log(msg.toString());
console.log(rinfo);
});
socket.send(
Buffer.from("helloworld"),
0,
5,
41234,
"localhost",
function (err, bytes) {
console.log("发送了个%d字节", bytes);
}
);
socket.on("error", function (err) {
console.error(err);
});
udp.dstport
==41234
2.TCP
TCP
传输控制协议Transimision Control Protocal
可靠、面向连接的协议,传输效率低 (在不可靠的IP
层上建立可靠的传输层)。 TCP 提供全双工服务,即数据可在同一时间双向传播。
- 源端口号、目标端口号,指代的是发送方随机端口,目标端对应的端口
- 序列号:32 位序列号是用于对数据包进行标记,方便重组
- 确认序列号:期望发送方下一个发送的数据的编号
- 4 位首部长度:单位是字节,4 位最大能表示 15,所以头部长度最大为 60
URG
:紧急信号、ACK
:确认信号、PSH
:应该从 TCP 缓冲区读走数据、RST
:断开重新连接、SYN
:建立连接、FIN
:表示要断开- 窗口大小: 当网络通畅时将这个窗口值变大加快传输速度,当网络不稳定时减少这个值。在 TCP 中起到流量控制作用。
- 校验和:用来做差错控制,看传输的报文段是否损坏
- 紧急指针:用来发送紧急数据使用
TCP 对数据进行分段打包传输,对每个数据包编号控制顺序。TCP 报文首部有 20 个字节,额外开销大。
特点
可靠性: TCP 通过使用确认、重发控制和校验和等机制,确保了数据的可靠传输。如果一个数据包丢失或损坏,TCP 会负责重发该数据包,以确保它最终到达目的地。
顺序控制: TCP 确保数据包按照正确的顺序到达目的地,并将它们以正确的顺序交付给应用程序。这是因为 TCP 使用序列号来标识每个数据包,接收方可以根据序列号对数据包进行排序,从而提供有序的数据传输。
流量控制:TCP 通过窗口控制机制来管理通信流量,以避免过度拥塞。发送方会根据接收方的能力来控制发送速率,以确保网络不会超负荷。
3.TCP 与 UDP 对比
特点 | TCP | UDP |
---|---|---|
可靠性 | 可靠 | 不可靠 |
连接性 | 面向连接 | 无连接 |
报文 | 面向字节流 | 面向报文 |
效率 | 传输效率低 | 传输效率高 |
双工性 | 全双工 | 一对一、一对多、多对一、多对多 |
流量控制 | 滑动窗口 | 无 |
拥塞控制 | 慢开始、拥塞避免、快重传、快恢复 | 无 |
传输效率 | 慢 | 快 |
4.TCP 中的核心概念
1.滑动窗口
TCP 的滑动窗口是一种用于控制发送方和接收方之间数据流量的机制。发送方维护一个发送窗口,而接收方维护一个接收窗口。窗口实际上是缓冲区,用于存储待发送或待接收的数据。
- 发送窗口:发送方的缓冲区,用于存储待发送的数据。发送窗口的大小取决于多个因素,包括网络延迟、接收方的接收窗口大小等。
- 接收窗口:接收方的缓冲区,用于存储已接收但尚未交付给应用程序的数据。接收窗口的大小由接收方确定。
- 每次接收端收到数据后都会再次确认(
rwnd
)大小,如果值为 0,停止发送数据。 (并发送窗口探测包,持续监测窗口大小)
通过调整发送窗口的大小,TCP 可以实现流量控制。会产生队头阻塞的问题。
1.求数组中连续 k 项最大的和
js
let arr = [1, 3, 10, -2, 9, 8, -4];
function subSum(arr, k = 3) {
let max = 0;
for (let i = 0; i < k; i++) {
max += arr[i];
}
let win = max;
for (let i = k; i < arr.length; i++) {
win += arr[i] - arr[i - k]; // 出一个进一个, 滑动窗口
max = Math.max(win, max);
}
return max;
}
console.log(subSum(arr));
let arr = [1, 3, 10, -2, 9, 8, -4];
function subSum(arr, k = 3) {
let max = 0;
for (let i = 0; i < k; i++) {
max += arr[i];
}
let win = max;
for (let i = k; i < arr.length; i++) {
win += arr[i] - arr[i - k]; // 出一个进一个, 滑动窗口
max = Math.max(win, max);
}
return max;
}
console.log(subSum(arr));
2.无重复字符的最长字符串长度
js
let str = "abcbde";
function findStr(str) {
// 滑动窗口边界
let left = 0;
let right = 0;
// 用于存储当前滑动窗口内的字符
let set = new Set();
// 最大不重复字符串的长度
let maxLen = 0;
while (right < str.length) {
while (set.has(str[right])) {
// 遇到重复则滑动左侧
set.delete(str[left++]);
}
// 字符添加到集合中
set.add(str[right]);
maxLen = Math.max(maxLen, set.size);
// 像右滑动
right++;
}
return maxLen;
}
console.log(findStr(str));
let str = "abcbde";
function findStr(str) {
// 滑动窗口边界
let left = 0;
let right = 0;
// 用于存储当前滑动窗口内的字符
let set = new Set();
// 最大不重复字符串的长度
let maxLen = 0;
while (right < str.length) {
while (set.has(str[right])) {
// 遇到重复则滑动左侧
set.delete(str[left++]);
}
// 字符添加到集合中
set.add(str[right]);
maxLen = Math.max(maxLen, set.size);
// 像右滑动
right++;
}
return maxLen;
}
console.log(findStr(str));
2.TCP
拥塞处理
- TCP 维护一个拥塞窗口
cwnd
(congestion window)变量,设置ssthresh
慢启动阈值 ,在传输过程中没有拥塞就将cwnd
值增大。 cwnd < ssthresh
使用慢开始算法cwnd > ssthresh
使用拥塞避免算法- ROT(Retransmission TimeOut) 时更新
ssthresh
值为当前窗口的一半,更新cwnd
= 1 - 快重传,可能在发送的过程中出现丢包情况。此时不要立即回退到慢开始阶段,而是对已经收到的报文重复确认,如果确认次数达到 3 次,则立即进行重传 快恢复算法 (减少超时重传机制的出现),同时重置
cwnd
的的值。
3.粘包
发送方发送的数据包很小,而发送频率较高时,希望合并多个数据包以减少网络开销和提高效率(一次性发送,以减少数据包的数量)。
Nagle
算法的基本定义是任意时刻,最多只能有一个未被确认的小段 (TCP 内部控制)Cork算法
当达到MSS
(Maximum Segment Size )值时统一进行发送(此值就是帧的大小 -ip
头 -tcp
头 = 1460 个字节)理论值