
Go 的 net.ListenIP 创建的是原始套接字(raw socket),仅能接收内核未处理的协议(如 ICMP 副本),而 TCP/UDP 等标准协议由内核协议栈独占处理,不会被 ListenIP 截获;因此 ListenIP 与 ListenTCP 可共存,互不干扰。
go 的 `net.listenip` 创建的是原始套接字(raw socket),仅能接收内核未处理的协议(如 icmp 副本),而 tcp/udp 等标准协议由内核协议栈独占处理,不会被 `listenip` 截获;因此 `listenip` 与 `listentcp` 可共存,互不干扰。
在 Go 网络编程中,常有人误以为调用 net.ListenIP("ip4", &net.IPAddr{IP: net.ParseIP("192.168.1.1")}) 能“捕获发往该 IP 的所有入站数据包”,进而担心它会抢占 net.ListenTCP("192.168.1.1:80") 的 TCP 流量。这种理解是错误的——Linux 内核对原始套接字(SOCK_RAW)有明确的协议分发策略,它不会将 TCP 或 UDP 数据包递交给用户态 raw socket。
根据 Go 源码(如你所见),net.ListenIP 底层调用 internetSocket(..., syscall.SOCK_RAW, proto, ...) 创建一个 IPPROTO_RAW 类型的原始套接字。但需注意:
-
IPPROTO_RAW在 Linux 中仅用于发送(send-only),不能用于接收; - 实际接收时,Go 会根据传入的
netProto(如"ip4")自动选择合适的协议号(例如IPPROTO_ICMP),从而创建可接收对应协议的 raw socket; -
关键规则:内核仅向匹配协议号的 raw socket 传递 未被内核协议栈处理的报文副本。
- ✅ ICMP 报文:内核在处理完自身响应(如 ping reply)后,还会向
IPPROTO_ICMPraw socket 发送一份副本(可通过ICMP_FILTER控制); - ❌ TCP/UDP 报文:一旦内核识别为 TCP 或 UDP,就会严格交由对应的传输层子系统处理(绑定到监听 socket 或 ESTABLISHED 连接),绝不会投递给任何 raw socket;
- ⚠️ 其他协议(如自定义协议号 99):若无内核模块处理,则整包直接送达 raw socket。
- ✅ ICMP 报文:内核在处理完自身响应(如 ping reply)后,还会向
因此,在你的场景中:
- 进程 p1 执行
ListenIP("ip4", &IPAddr{IP: net.ParseIP("192.168.1.1")})→ 实际创建IPPROTO_ICMP(或类似)raw socket,仅可能收到 ICMP 副本; - 进程 p2 执行
ListenTCP("192.168.1.1:80")→ 创建标准 TCP listening socket; - 所有发往
192.168.1.1:80的 TCP SYN 包均由内核 TCP 栈接收并完成三次握手,p2 正常Accept();p1 完全无感知。
✅ 正确示例(监听 ICMP Echo Request):
conn, err := net.ListenIP("ip4:icmp", &net.IPAddr{IP: net.ParseIP("0.0.0.0")})
if err != nil {
log.Fatal(err)
}
defer conn.Close()
buf := make([]byte, 1500)
for {
n, addr, err := conn.ReadFrom(buf)
if err != nil {
log.Printf("read error: %v", err)
continue
}
// 解析 ICMP header(需手动解析,Go 不提供高层封装)
if len(buf) >= 8 && buf[0] == 8 { // Type 8 = Echo Request
log.Printf("ICMP Echo from %v, len=%d", addr, n)
}
}⚠️ 注意事项:
- 使用 raw socket 通常需要
CAP_NET_RAW权限(Linux)或管理员权限(Windows/macOS); -
ListenIP不能监听 TCP/UDP 端口,也不参与端口复用(SO_REUSEADDR对其无效); - 若需真正抓取 所有经过网卡的 IP 包(含 TCP/UDP),应使用
pcap库(如gopacket)配合AF_PACKET或libpcap,而非net.ListenIP; - 生产环境慎用 raw socket —— 它绕过内核协议栈优化,性能低且易引入安全风险。
总结:net.ListenIP 是面向协议分析与 ICMP 处理的底层接口,不是通用包捕获工具。TCP/UDP 流量始终由内核协议栈专有处理,与 raw socket 并行不悖。理解这一分发机制,是设计可靠网络服务与诊断工具的前提。

















