【学习笔记】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。
相关推荐
励志不掉头发的内向程序员18 小时前
C++进阶——多态
开发语言·c++·学习
魏 无羡18 小时前
k8s 获取真实ip地址
tcp/ip·容器·kubernetes
eqwaak018 小时前
Matplotlib 动态显示详解:技术深度与创新思考
网络·python·网络协议·tcp/ip·语言模型·matplotlib
西猫雷婶18 小时前
scikit-learn/sklearn学习|广义线性回归损失函数的基本表达式
深度学习·神经网络·学习·机器学习·线性回归·scikit-learn·概率论
Lynnxiaowen19 小时前
今天继续学习shell脚本
linux·运维·学习·云计算·bash
落羽的落羽20 小时前
【C++】C++11的包装器:function与bind简介
c++·学习
sucool_lb20 小时前
GEM5学习(5): ARM 架构功耗仿真
arm开发·学习
尚久龙20 小时前
安卓学习 之 图片控件和图片按钮
android·java·学习·手机·android studio·安卓
守.护20 小时前
云计算学习笔记——HTTP服务、NFS服务篇
笔记·学习·云计算
wdfk_prog21 小时前
[Linux]学习笔记系列 -- lib/dump_stack.c 栈回溯打印(Stack Trace Dumping) 内核调试与错误诊断的基石
linux·运维·服务器·c语言·笔记·学习