TCP 和 UDP 的区别
在计算机网络中,TCP 和 UDP 是两种常见的传输层协议。它们的设计目标和适用场景有很大不同。以下是两者的详细对比:

1. 面向连接 vs 无连接
-
TCP 是面向连接的协议。
- 在数据传输之前,客户端和服务端需要通过三次握手建立连接。
- 这种"连接"实际上是在双方维护了一些状态信息(例如接收窗口、序列号等),用于保证可靠性和顺序性。
- 为什么需要建立连接?因为 TCP 需要确保双方的状态同步,以便后续的数据传输能够有序且可靠。
-
UDP 是无连接的协议。
- 数据直接发送,无需建立连接。
- UDP 不维护任何状态信息,也不关心数据是否被对方正确接收。
代码示例:
go
// TCP 示例:建立连接并发送数据
package main
import (
"fmt"
"net"
)
func main() {
// 创建一个 TCP 服务器
listener, err := net.Listen("tcp", "127.0.0.1:8080")
if err != nil {
fmt.Println("Error:", err)
return
}
defer listener.Close()
go func() {
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("Error accepting connection:", err)
continue
}
go handleConnection(conn)
}
}()
fmt.Println("TCP Server is running on port 8080...")
select {}
}
func handleConnection(conn net.Conn) {
defer conn.Close()
buffer := make([]byte, 1024)
n, err := conn.Read(buffer)
if err != nil {
fmt.Println("Error reading:", err)
return
}
fmt.Printf("Received: %s\n", buffer[:n])
}
go
// UDP 示例:直接发送数据
package main
import (
"fmt"
"net"
)
func main() {
// 创建一个 UDP 服务器
addr, _ := net.ResolveUDPAddr("udp", "127.0.0.1:8080")
conn, err := net.ListenUDP("udp", addr)
if err != nil {
fmt.Println("Error:", err)
return
}
defer conn.Close()
fmt.Println("UDP Server is running on port 8080...")
buffer := make([]byte, 1024)
for {
n, remoteAddr, err := conn.ReadFromUDP(buffer)
if err != nil {
fmt.Println("Error reading:", err)
continue
}
fmt.Printf("Received from %v: %s\n", remoteAddr, buffer[:n])
}
}
2. 可靠性
-
TCP 提供可靠交付:
- 数据包无差错、不丢失、不重复,并按序到达。
- 如果某个包丢失,TCP 会自动重传。
- TCP 使用序列号和确认机制来确保数据完整性。
-
UDP 不提供可靠性:
- 数据包可能会丢失、重复或乱序。
- UDP 不会进行重传或其他补救措施。
3. 数据传输模式
-
TCP 是面向字节流的:
- 数据以连续的字节流形式发送,没有明确的边界。
- TCP 自己维护缓冲区,并根据网络状况调整发送策略。
-
UDP 是基于数据报的:
- 每个数据包独立发送,有明确的边界。
- 应用层需要自行处理数据包的顺序和完整性。
4. 拥塞控制
-
TCP 有拥塞控制机制:
- 当网络拥塞时,TCP 会主动降低发送速率(例如慢启动、拥塞避免)。
- 这是为了避免网络过载,但可能导致延迟增加。
-
UDP 没有拥塞控制:
- 应用层告诉 UDP 发送数据,它就直接发。
- 即使网络拥塞,UDP 也不会主动退缩。
UDP 的三大使用场景
-
资源消耗少,对丢包不敏感的应用
- 适用于局域网或网络环境较好的场景。
- 示例:DNS 查询(通常使用 UDP)。
-
广播或多播应用
- UDP 支持一对多通信(广播或多播),而 TCP 只支持一对一通信。
- 示例:在线视频会议、实时语音通信。
-
低延迟、高吞吐量需求的应用
- 对于要求快速响应、容忍少量丢包的场景,UDP 更适合。
- 示例:实时游戏、直播流媒体。
总结
特性 | TCP | UDP |
---|---|---|
连接方式 | 面向连接 | 无连接 |
可靠性 | 可靠(重传、排序、去重) | 不可靠(可能丢失或乱序) |
数据模式 | 字节流 | 数据报 |
拥塞控制 | 有 | 无 |
适用场景 | 文件传输、HTTP 等 | 实时音视频、DNS 等 |
代码示例总结
TCP 客户端
go
package main
import (
"fmt"
"net"
)
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:8080")
if err != nil {
fmt.Println("Error:", err)
return
}
defer conn.Close()
message := "Hello, TCP!"
_, err = conn.Write([]byte(message))
if err != nil {
fmt.Println("Error sending data:", err)
}
}
UDP 客户端
go
package main
import (
"fmt"
"net"
)
func main() {
addr, _ := net.ResolveUDPAddr("udp", "127.0.0.1:8080")
conn, err := net.DialUDP("udp", nil, addr)
if err != nil {
fmt.Println("Error:", err)
return
}
defer conn.Close()
message := "Hello, UDP!"
_, err = conn.Write([]byte(message))
if err != nil {
fmt.Println("Error sending data:", err)
}
}
通过以上代码示例,可以清楚地看到 TCP 和 UDP 的实现差异以及各自的适用场景。