一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

Swoole中sendwait同步发送的差异

时间:2026-07-07 09:55:57 编辑:袖梨 来源:一聚教程网

send 和 sendwait 并非并列 API,而是同一发送行为在阻塞/非阻塞上下文中的不同表现:前者依赖 socket 模式与上层等待逻辑,后者是 swConnection 内部标识发送等待状态的字段,用于事件循环重试。

sendsendwait 不是 Swoole 提供的两个并列 API,而是同一发送行为在不同上下文中的表现逻辑——关键区别在于「连接是否处于阻塞模式」以及「调用方是否主动等待写就绪」。

swConnection_send 是底层统一入口

无论你用的是 swoole_client 同步模式、异步模式,还是协程客户端,最终发数据都会走到 swConnection_send。这个函数本身不做“同步/异步”判断,它只负责调用系统 send() 系统调用:

  • 如果 socket 是阻塞的(如 swoole_client 同步模式下 connect 时未设 SWOOLE_SOCK_ASYNC),send() 会等内核把数据拷进 TCP 发送缓冲区才返回,看起来就是“同步发送”
  • 如果 socket 是非阻塞的(如 Reactor 线程中所有连接、协程客户端底层),send() 可能立刻返回 -1,且 errno == EAGAINEWOULDBLOCK
  • 此时真正决定“要不要等”的,不是 send 函数本身,而是上层逻辑:比如 swClient_tcp_send_sync 在遇到 EAGAIN 时会主动调用 swSocket_wait 阻塞等待 1 秒;而 swConnection_buffer_send(用于 Reactor 线程)则直接标记 send_wait = 1 并返回,让事件循环下次再试

sendwait 不是公开 API,而是内部状态标识

你在源码里看到的 send_wait = 1,是 swConnection 结构体里的一个字段,用于通知 Reactor:“这个连接当前不可写,别再往它 buffer 里塞数据了,等可写事件来了再重试”。它不是你手动调用的函数,也不该被业务代码感知:

  • send_wait1 时,swConnection_buffer_send 会跳过发送,避免反复 send 失败
  • Reactor 收到 EPOLLOUT 事件后,会重新触发该连接的发送逻辑
  • 如果你在协程中用 $client->send(),底层自动处理了等待逻辑,你完全看不到 send_wait
  • 但如果你手写非阻塞 socket + epoll,就得自己维护类似 send_wait 的状态,否则会忙等

同步发送容易卡住 reactor 线程

在 Reactor 线程(比如 swoole_server 的主循环)里,绝对不能出现真正意义上的“同步发送”:

  • swClient_tcp_send_sync 这类函数只允许在独立进程/线程中使用(如 Worker 进程里起一个同步 swoole_client
  • 若在 Reactor 线程中强行用阻塞 socket 调 send,一旦网络卡顿或对端接收窗口满,整个事件循环就会卡住,新连接、心跳、定时器全停摆
  • 协程看似“同步写法”,但 $client->send() 底层仍是非阻塞 + yield,不会阻塞 reactor
  • 错误示范:stream_socket_client(..., $err, $msg, 30) 创建阻塞 socket 后直接 fwrite,在协程环境里也会破坏调度

真正要注意的不是“选 send 还是 sendwait”,而是:你当前代码运行在哪种执行模型下(同步进程 / Reactor 线程 / 协程)、socket 是否被正确设为非阻塞、以及上层有没有帮你兜底处理 EAGAIN。这些细节一旦错位,轻则发不出数据,重则整条 reactor 线程挂死。

热门栏目