什么是全双工和半双工?
按"能不能同时双向传输"划分:单工只能一个方向,半双工能双向但同一时刻只能一个方向,全双工可以同时收发。 电话是全双工,对讲机是半双工,广播是单工。这个区别在链路层表现为"用几对线/几个频段", 在应用层表现为"能不能边收边发"。
1. 三种模式
| 模式 | 方向 | 同时双向 | 典型例子 | 实现方式 |
|---|---|---|---|---|
| 单工 Simplex | 单向 | 否 | 广播、键盘到主机 | 一对线,固定方向 |
| 半双工 Half-duplex | 双向 | 否 | 对讲机、RS-485、集线器以太网 | 一对线分时复用 |
| 全双工 Full-duplex | 双向 | 是 | 电话、交换式以太网、TCP | 两对线或频分/回声消除 |
单工: A ───────────────► B
半双工: A ◄──── 分时轮流 ────► B (发的时候不能收,收的时候不能发)
全双工: A ──── 发送通道 ────► B
A ◄─── 接收通道 ──── B (两条独立通道,可同时进行)
2. 以太网为什么从半双工变成全双工
早期用集线器(hub)时,所有主机共享一条总线:同一时刻只能有一台在发,否则冲突, 靠 CSMA/CD(载波监听多路访问/冲突检测)协调,这就是半双工。
换成交换机(switch)后,每个端口是一条独立的点到点链路,收发各用一对双绞线, 冲突域消失,CSMA/CD 被关闭,于是变成全双工——带宽也从"共享 10Mbps"变成"每端口独享"。
集线器(半双工): 交换机(全双工):
A ─┐ A ────┬──── B
B ─┼─ 共享总线,冲突 C ────┴──── D
C ─┘ 每个端口独立,无冲突
3. TCP 是全双工的
TCP 连接的两端各有独立的发送序号空间和接收缓冲区,可以同时发数据而互不干扰。 这也是为什么关闭连接需要四次挥手:两个方向要各自发 FIN。
// 全双工:同一个连接上,一边持续读,一边持续写
conn, _ := net.Dial("tcp", "127.0.0.1:9000")
go func() { // 写方向
for i := 0; ; i++ {
fmt.Fprintf(conn, "ping %d\n", i)
time.Sleep(time.Second)
}
}()
buf := make([]byte, 1024) // 读方向,与写并行
for {
n, err := conn.Read(buf)
if err != nil { break }
fmt.Printf("recv: %s", buf[:n])
}
# 查看网卡协商出来的双工模式与速率
ethtool eth0 | grep -E 'Speed|Duplex'
# 强制全双工(对端必须也一致,否则会大量 CRC 错误)
ethtool -s eth0 speed 1000 duplex full autoneg off
全双工链路如果两端协商不一致(一端全双工、一端半双工),会出现大量冲突与 CRC 错误, 表现为"能通但极慢、丢包"。
4. 常见追问
Q:半双工是不是"轮流发"?靠什么协调? 是分时复用,靠协议协调。以太网用 CSMA/CD(先听后发、边发边听、冲突后退避), RS-485 用主从轮询或令牌,无线 WiFi 用 CSMA/CA(因为无法边发边听)。
Q:为什么无线网络不能算真正的全双工? 同一频段上,设备自身发射的信号会淹没微弱的接收信号(自干扰),所以 WiFi 本质是半双工, 靠时分(TDD)交替收发。要实现全双工需要额外的干扰消除技术。
Q:HTTP 是半双工吗? 语义上是"请求—响应"交替,HTTP/1.1 管线化也只是把请求排队,响应必须按序返回。 HTTP/2 的流(stream)可以在一条连接上多路复用,允许服务端推送,但单个流内部仍是请求响应配对。 真正的双向流要用 WebSocket 或 gRPC streaming。
Q:全双工一定比半双工快吗? 在同样的物理介质上,全双工省掉了冲突与退避,吞吐和时延都更好;但它需要两倍的线路资源 (两对线或两个频段)。光纤用一根纤芯加波分复用也能做到全双工。
小结
单工、半双工、全双工的区别就是"能不能同时双向"。工程上值得记住两点: 以太网从 hub 到 switch 的演进把半双工变成全双工;TCP 因为双向独立序号空间而是全双工, 所以关闭要四次挥手。答题时补一句"全双工需要两条独立通道",就把物理层和应用层连起来了。