【学习笔记】TCP 与 UDP

TCP(Transmission Control Protocol)与UDP(User Datagram Protocol)是 网络通讯 中最基础也最常用的两种 传输层 协议。

文章目录

      • [1. 简介](#1. 简介)
      • [2. OSI 与 TCP/IP 模型中的定位](#2. OSI 与 TCP/IP 模型中的定位)
      • [3. 协议原理与关键机制](#3. 协议原理与关键机制)
        • [3.1 UDP](#3.1 UDP)
        • [3.2 TCP](#3.2 TCP)
      • [5. 实践:Socket 接口示例](#5. 实践:Socket 接口示例)
      • [6. 调优](#6. 调优)

1. 简介

TCP 是1974年由早期的NCP(Network Control Protocol)逐渐演化出来的概念。它在1980年被拆分成了现在常见的TCP与IP协议,形成经典的 TCP/IP 协议簇。后来,UDP 在 RFC 768(互联网工程任务协会,Internet Engineering Task Force, IETF,发布的一个标准文件)中发布,用于减少延迟、适应简单的请求或应答环境,如DNS(域名系统)。

90年代后,TCP 持续优化拥塞控制算法(如Tahoe、Reno、CUBIC),在万兆与云计算场景中处于核心地位。而UDP则在实时音视频、VoIP(基于IP的语音传输,一种语音通话技术)、在线游戏等领域被广泛使用。

2. OSI 与 TCP/IP 模型中的定位

TCP/IP模型将传统的OSI理论模型简化为4层结构,传输层分为TCP/UDP两种机制。

3. 协议原理与关键机制

3.1 UDP

【注】

  • 源/目的端口: 标识进程级别的通信端点
  • 长度: 头部与数据总长度,最小8字节
  • 校验和: 包含伪首部,检验UDP头与数据的完整性

【机制】

  • 无握手: 发送即完成,无三次握手
  • 无重传: 丢包不重发、无拥塞控制,由上层应用决定策略。
  • 场景: 实时音视频、DNS查询等。实时性要求高,应用自有可靠机制或可忽略丢包。
3.2 TCP

【注】

  • 序列号 / 确认号: 实现可靠传输的核心,按字节计数流
  • Flags: URG,ACK,PSH,RST,SYN,FIN等,控制连接的建立,终止与数据推送。
  • 窗口大小: 流量控制的关键,接收方通告自己缓存区的剩余空间
  • 选项: 常见有最大报文段MSS、时间戳、窗口缩放等,用于性能优化。

【机制】

  • 三次握手(3-way Handshake):

    1. 客户端发送SYN,选择输出序列号x

    2. 服务端回应 SYN+ACK,确认号 x+1,并发送自己序列号 y

    3. 客户端再发 ACK 确认号 y+1,一切就绪

  • 四次挥手(4-way Teardown):

    1. 发起方发送 FIN。

    2. 对方 ACK。

    3. 对方再发 FIN。

    4. 原方 ACK,等待 TIME-WAIT 结束后真正释放。

  • 重传
    超时重传(RTO): 基于往返时间(RTT)估算,动态调整超时重传定时器。
    快速重传: 连续收到 3 个相同的ACK时,立即重传疑似丢失的报文段,而无需等待超时。

  • 拥塞控制算法

    1. 慢启动: 指数增长拥塞窗口(cwnd),直到达到阈值

    2. 拥塞避免: 加法增大,线性增长cwnd

    3. 快速重传与快速恢复: 检测到丢包后,一方面减小阈值,一方面快速恢复到阈值区。

    4. CUBIC(linux默认),BBR(Google提出)等

5. 实践:Socket 接口示例

下面以编程语言示例基础的TCP和UDP的服务端与客户端。

C语言
c 复制代码
// UDP 客户端示例:初始化并发送
int sock = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in serv;
memset(&serv, 0, sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_port = htons(9000);
serv.sin_addr.s_addr = inet_addr("127.0.0.1");
char *msg = "Hello UDP";
sendto(sock, msg, strlen(msg), 0, (struct sockaddr*)&serv, sizeof(serv));

// TCP 服务器示例:接受连接并回复
int sock = socket(AF_INET, SOCK_STREAM, 0);
bind(sock, (struct sockaddr*)&serv, sizeof(serv));
listen(sock, 5);
int conn = accept(sock, NULL, NULL);
char buf[1024];
int len = recv(conn, buf, sizeof(buf), 0);
send(conn, "Hello TCP", 9, 0);
close(conn);
Go语言
go 复制代码
// UDP Echo 服务器
addr, _ := net.ResolveUDPAddr("udp", ":9000")
conn, _ := net.ListenUDP("udp", addr)
buf := make([]byte, 1024)
for {
    n, remote, _ := conn.ReadFromUDP(buf)
    conn.WriteToUDP(buf[:n], remote)
}

// TCP 客户端
conn, _ := net.Dial("tcp", "localhost:8000")
fmt.Fprintln(conn, "Hello TCP")
response, _ := bufio.NewReader(conn).ReadString('\n')
fmt.Println("Server replied:", response)
Python语言
python 复制代码
# UDP 客户端
import socket
udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp.sendto(b"Ping", ("localhost", 9000))
msg, _ = udp.recvfrom(1024)
print(msg)

# TCP 服务器
import socket
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srv.bind(("0.0.0.0", 8000))
srv.listen(1)
conn, addr = srv.accept()
data = conn.recv(1024)
conn.sendall(b"Hello TCP")
conn.close()

6. 调优

  1. TIME_WAIT消耗: 大量短连接会产生大量的TIME_WAIT,建议启动连接复用、长连接或HTTP/2多路复用。
  2. MTU与分片: 超大UDP报文易被分片丢弃,建议应用层自行分片与重组。
  3. Nagle算法: TCP默认启用Nagle,合并小包,可能会增加延迟,需要根据场景选择是否禁用。
  4. 内核缓冲区调整: 高带宽-时延环境下,增大send/recv缓冲区以避免吞吐瓶颈。
  5. 拥塞算法: Linux默认CUBIC,若要更低时延可使用BBR。
相关推荐
雍凉明月夜几秒前
深度学习网络笔记Ⅱ(常见网络分类1)
人工智能·笔记·深度学习
卷心菜_几秒前
代码随想录笔记-背包问题
笔记
北岛寒沫1 分钟前
北京大学国家发展研究院 经济学辅修 经济学原理课程笔记(第十三课 垄断竞争)
人工智能·经验分享·笔记
love530love1 小时前
【笔记】Intel oneAPI 开发环境配置
人工智能·windows·笔记·oneapi·onednn·deep neural
HansenPole8251 小时前
元编程笔记
笔记·网络协议·rpc
charlie1145141911 小时前
Git团队协作完全入门指南(上)
笔记·git·学习·教程·工程
迷茫的启明星1 小时前
Git命令学习
git·学习
全栈陈序员2 小时前
说说你对 Vue 的理解
前端·javascript·vue.js·学习·前端框架
im_AMBER2 小时前
Leetcode 85 【滑动窗口(不定长)】最多 K 个重复元素的最长子数组
c++·笔记·学习·算法·leetcode·哈希算法