一 TCP和UDP区别?
二 TCP头部和UDP头部区别?
TCP 头部:TCP 头部较复杂,包含了许多字段来支持面向连接的可靠性、流量控制和拥塞控制等功能。
字段 | 大小 | 描述 |
---|---|---|
源端口 | 16 位 | 发送端的端口号 |
目的端口 | 16 位 | 接收端的端口号 |
序列号 | 32 位 | 用于数据流的排序和重组 |
确认号 | 32 位 | 确认已收到的数据的序列号 |
数据偏移 | 4 位 | TCP 头部长度 (以 32 位字为单位) |
保留 | 6 位 | 预留位,通常为 0 |
控制位 | 6 位 | 例如 SYN、ACK、FIN 等标志 |
窗口大小 | 16 位 | 用于流量控制 |
校验和 | 16 位 | 用于检测数据在传输过程中是否损坏 |
紧急指针 | 16 位 | 如果 URG 标志位被设置,该字段有效 |
选项 | 可变 | 包含额外的控制信息和选项 |
填充 | 可变 | 确保头部长度是 32 位字的倍数 |
数据 | 可变 | 实际的数据 |
UDP 头部:UDP 头部较简单,主要提供基本的端到端数据传输功能。UDP 头部的结构如下:
字段 | 大小 | 描述 |
---|---|---|
源端口 | 16 位 | 发送端的端口号 |
目的端口 | 16 位 | 接收端的端口号 |
长度 | 16 位 | UDP 数据包的总长度 (头部+数据) |
校验和 | 16 位 | 用于检测数据在传输过程中是否损坏 |
数据 | 可变 | 实际的数据 |
三 TCP如何保证数据传输安全?
TCP主要提供了检验和、序列号/确认应答、超时重传、滑动窗口、拥塞控制和 流量控制等方法实现了可靠性传输。
检验和:通过检验和的方式,接收端可以检测出来数据是否有差错和异常,假如有差错就会直接丢弃TCP段,重新发送。
序列号/确认应答:序列号的作用不仅仅是应答的作用,有了序列号能够将接收到的数据根据序列号排序,并且去掉重复序列号的数据。
TCP传输的过程中,每次接收方收到数据后,都会对传输方进行确认应答。也就是发送ACK报文,这个ACK报文当中带有对应的确认序列号,告诉发送方,接收到了哪些数据,下一次的数据从哪里发。
滑动窗口:滑动窗口既提高了报文传输的效率,也避免了发送方发送过多的数据而导致接收方无法正常处理的异常。
超时重传:超时重传是指发送出去的数据包到接收到确认包之间的时间,如果超过了这个时间会被认为是丢包了,需要重传。最大超时时间是动态计算的。
拥塞控制:在数据传输过程中,可能由于网络状态的问题,造成网络拥堵,此时引入拥塞控制机制,在保证TCP可靠性的同时,提高性能。
流量控制:如果主机A 一直向主机B发送数据,不考虑主机B的接受能力,则可能导致主机B的接受缓冲区满了而无法再接受数据,从而会导致大量的数据丢包,引发重传机制。而在重传的过程中,若主机B的接收缓冲区情况仍未好转,则会将大量的时间浪费在重传数据上,降低传送数据的效率。所以引入流量控制机制,主机B通过告诉主机A自己接收缓冲区的大小,来使主机A控制发送的数据量。流量控制与TCP协议报头中的窗口大小有关。
四 TCP的序列号除了保证数据有序还有什么作用?
1. 数据重组
TCP 的序列号用于数据重组。在接收方,TCP 会根据序列号将接收到的分段数据重新组合成原始数据流。这对于处理可能乱序到达的分段数据非常重要,因为数据包可能会经过不同的路径到达接收方。
2. 数据重传
序列号在数据重传中起到关键作用。如果一个数据包在传输过程中丢失或出现错误,接收方不会收到这个数据包,并会要求发送方重新发送该数据包。接收方通过确认号(ACK)通知发送方未接收到的数据包的序列号,从而触发重传。
3. 流量控制
TCP 使用序列号来实现流量控制。通过序列号,发送方可以知道已被接收方确认的数据的范围,从而限制新数据的发送量。TCP 的滑动窗口机制利用序列号来控制数据的发送速率,确保接收方能够处理接收到的数据而不会被压垮。
4. 确认和拥塞控制
确认(ACK):接收方使用确认号来指示到达的数据的序列号。发送方收到确认后,可以确定数据已经成功接收,并可以移动发送窗口,从而发送新的数据。
拥塞控制:序列号在拥塞控制中也起到作用。TCP 协议通过监控序列号和确认号来动态调整数据的发送速率,以避免网络拥塞。通过控制发送窗口的大小,TCP 能够在网络拥塞的情况下减少发送的数据量。
5. 防止重复数据
序列号帮助防止数据的重复接收。在 TCP 连接中,每个数据包都有一个唯一的序列号。接收方可以通过检查序列号来识别并丢弃重复的数据包,确保每个数据包只被处理一次。
五 如何设计一个限流器?
令牌桶算法通过一个桶来存储令牌,每秒生成一定数量的令牌。每个请求需要消耗一个令牌。如果桶为空,则请求被拒绝。令牌桶允许突发流量,但限制了平均速率。
实现步骤:
- 初始化令牌桶,设置令牌生成速率和桶的容量。
- 每秒生成一定数量的令牌,并加入桶中(如果桶未满)。
- 每次请求时,从桶中取走一个令牌。
- 如果桶为空,拒绝请求;如果有令牌,允许请求。
import java.util.concurrent.TimeUnit;
public class TokenBucket {
private final long capacity; // 桶的容量
private final long rate; // 令牌生成速率(每秒生成的令牌数)
private long tokens; // 当前令牌数
private long lastRefillTime; // 上次令牌生成时间
public TokenBucket(long capacity, long rate) {
this.capacity = capacity;
this.rate = rate;
this.tokens = capacity;
this.lastRefillTime = System.nanoTime();
}
public synchronized boolean tryAcquire() {
refill();
if (tokens > 0) {
tokens--;
return true;
}
return false;
}
private void refill() {
long now = System.nanoTime();
long elapsed = now - lastRefillTime;
long newTokens = TimeUnit.NANOSECONDS.toSeconds(elapsed) * rate;
tokens = Math.min(capacity, tokens + newTokens);
lastRefillTime = now;
}
}