linux socket编程通过地址族(af_)和套接字类型(sock_)灵活适配多种协议栈:af_inet/af_inet6用于ipv4/ipv6网络通信,af_unix用于本地进程间通信,af_packet用于数据链路层操作,af_netlink用于用户态与内核态通信;需按协议栈正确选择socket参数、地址结构及i/o方式,并注意多协议共存时的资源隔离与错误处理。

Linux socket 编程本身不绑定特定协议栈,而是通过地址族(AF_*)和套接字类型(SOCK_*)灵活适配不同协议栈。关键在于明确你要通信的协议栈类型,并正确初始化 socket、地址结构和协议行为。
支持哪些协议栈
socket 接口可接入多种内核协议栈,常见包括:
-
AF_INET / AF_INET6:对应 IPv4/IPv6 协议栈,搭配 TCP(
SOCK_STREAM)或 UDP(SOCK_DGRAM)使用 - AF_UNIX / AF_LOCAL:本地进程间通信(Unix Domain Socket),绕过网络协议栈,零丢包、高效率,适用于同一主机上的服务协作
- AF_PACKET:直接操作数据链路层(如以太网帧),常用于抓包(tcpdump)、自定义二层协议或网络设备调试
- AF_NETLINK:用户态与内核态通信专用,用于获取路由表、接口状态、netfilter 事件等,不是传统“数据传输”,但属于跨协议栈控制通道
如何选择并初始化对应协议栈
创建 socket 前需根据目标协议栈确定参数组合,不能混用:
- IPv4 TCP:用
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP),地址结构为struct sockaddr_in - IPv6 UDP:用
socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP),地址结构为struct sockaddr_in6,注意启用IPV6_V6ONLY避免双栈冲突 - Unix 域通信:用
socket(AF_UNIX, SOCK_STREAM, 0)或SOCK_SEQPACKET,地址结构为struct sockaddr_un,路径长度受sizeof(sa_un.sun_path)限制(通常 108 字节) - 原始链路层:用
socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)),需CAP_NET_RAW权限,绑定时指定网卡索引(if_nametoindex())
数据收发时的协议栈差异处理
不同协议栈对 send/recv 行为、错误语义和缓冲机制有明显区别:
- TCP 是流式、有序、可靠传输,
send()只保证入内核发送队列,不等于已送达;recv()可能分多次返回,需自行拼包 - UDP 是消息边界保留的不可靠传输,
sendto()/recvfrom()必须显式传地址;单次recv()对应一个完整 datagram,超长则截断并置MSG_TRUNC - Unix Domain Socket 支持传递文件描述符(
SCM_RIGHTS)和凭据(SCM_CREDENTIALS),需用sendmsg()/recvmsg()配合struct msghdr和控制消息 - AF_PACKET 发送需手动填充以太网头(MAC 目的/源、type)、IP 头等;接收时需解析帧头,注意网卡可能做 LRO/GRO 合并,影响原始帧还原
共存与切换策略
一个程序可同时使用多个协议栈,但需独立管理各 socket 文件描述符:
- 服务器监听多协议:例如同时 bind
AF_INET和AF_UNIXsocket,分别响应网络请求和本地管理命令 - 客户端自动降级:尝试 IPv6 连接失败后 fallback 到 IPv4(需检查
getaddrinfo()返回的 addrinfo 链表) - 避免协议混淆:不要对
AF_UNIXsocket 调用connect()时传sockaddr_in;也不要对AF_PACKET使用listen() - 资源隔离:不同协议栈的 socket 错误码含义不同(如
ECONNREFUSED对 TCP 有效,对 UDS 表示路径不存在或权限不足),应按上下文分别处理











