linux socket异步回调本质是事件通知+用户分发,主流方案为epoll+函数指针:创建epoll实例、注册fd及事件、关联含on_read/on_write的conn_ctx结构体、epoll_wait后调用对应回调;避免sigio(难区分fd、信号不安全)和posix aio(socket支持差)。

Linux socket 异步编程中的回调机制,本质不是内核直接“调用你的函数”,而是通过事件通知 + 用户主动分发,来模拟出“回调”的行为。它依赖的是 I/O 多路复用(如 epoll)或信号(如 SIGIO),再配合用户态的事件循环和函数指针调度——这才是实际可行、主流且高效的做法。
epoll + 回调函数指针是推荐方式
这是目前最常用、最健壮的异步 socket 回调实现路径:
- 用 epoll_create 创建 epoll 实例,用 epoll_ctl 注册 socket fd 及关注事件(如 EPOLLIN、EPOLLOUT)
- 每个 socket 关联一个结构体(例如 struct conn_ctx),其中包含 fd、读写缓冲区、状态字段,以及两个函数指针:on_read 和 on_write
- 主循环中调用 epoll_wait 阻塞等待事件;返回后遍历就绪事件,根据 fd 查到对应结构体,再调用其 on_read() 或 on_write()
- 回调函数本身由你定义,比如处理 HTTP 请求、解析协议包、触发业务逻辑,完全可控
不要依赖 SIGIO 作主回调机制
虽然可以用 fcntl(fd, F_SETOWN, getpid()) + fcntl(fd, F_SETFL, O_ASYNC) 启用 SIGIO,并用 sigaction 绑定信号处理器,但存在明显局限:
- 信号是进程级的,多个 socket 触发 SIGIO 时无法区分来源,需在信号 handler 中再次轮询所有 fd(退化为 poll)
- 信号处理函数中不能安全调用大多数 libc 函数(如 malloc、printf),限制业务逻辑表达能力
- 信号可能丢失(尤其高频事件),且上下文切换开销大,不适合高吞吐场景
- 无法区分 EPOLLIN/EPOLLOUT/EPOLLERR 等细粒度事件,实用性低
POSIX AIO 不适合 socket
Linux 的 aio_read/aio_write 主要面向普通文件(支持 O_DIRECT 等),对 socket fd 的支持非常有限:
- 绝大多数发行版中,对 socket 使用 aio_read 会直接返回 ENOTSUP
- 即使部分内核打了补丁支持,其回调机制(信号或线程)也难以与 socket 生命周期、连接管理自然融合
- 没有 epoll 那样的就绪状态反馈,无法做到“可读才读、可写才写”,容易导致 EAGAIN/EWOULDBLOCK 频发
回调需要你自己维护上下文和生命周期
真正的“异步回调”不等于自动内存管理。你需要显式设计:
- 每个连接的上下文(struct conn_ctx *)必须动态分配(如 malloc),并在 close 或错误时 free
- 回调函数内部若需发起新操作(如 send 后等待下一次可写),应重新注册 EPOLLOUT 事件,避免重复触发
- 避免在回调中阻塞(如 sleep、同步 DNS 查询),否则会拖慢整个事件循环
- 如需跨线程通信(如 worker 线程处理计算),可用 pipe 或 eventfd 通知主线程 epoll,再由主线程调用业务回调











