五:tcp异步服务端
服务端 有 异步非阻塞/协程
客户端 有 同步非阻塞/协程
TCP异步服务端

这样就创建了一个 TCP 服务器,监听本机 9501 端口。它的逻辑很简单,当客户端 Socket 通过网络发送一个 hello 字符串时,服务器会回复一个 Server: hello 字符串。
Server 是异步服务器,所以是通过监听事件的方式来编写程序的。当对应的事件发生时底层会主动回调指定的函数。如当有新的 TCP 连接进入时会执行 onConnect 事件回调,当某个连接向服务器发送数据时会回调 onReceive 函数。
- 服务器可以同时被成千上万个客户端连接,
$fd就是客户端连接的唯一标识符 - 调用
$server->send()方法向客户端连接发送数据,参数就是$fd客户端标识符 - 调用
$server->close()方法可以强制关闭某个客户端连接 - 客户端可能会主动断开连接,此时会触发 onClose 事件回调
执行程序 php tcp_server.php

在命令行下运行 server.php 程序,启动成功后可以使用 netstat 工具看到已经在监听 9501 端口。
这时就可以使用 telnet/netcat 工具连接服务器

以下将是Swoole\Server 类的全部方法、属性、配置项以及所有的事件。Swoole\Server 类是所有异步风格服务器的基类,后面章节的 Http\Server、WebSocket\Server、Redis\Server 都继承于它。
方法:
(1):__construct()
创建一个异步 IO 的 Server 对象。
new swoole_server()创建连接的时候会调用到此方法;
Swoole\Server::__construct(string $host = '0.0.0.0', int $port = 0, int $mode = SWOOLE_PROCESS, int $sockType = SWOOLE_SOCK_TCP): \Swoole\Server
-
参数
-
string $host- 功能:指定监听的 ip 地址
- 默认值:无
- 其它值:无
IPv4 使用
127.0.0.1表示监听本机,0.0.0.0表示监听所有地址
IPv6 使用::1表示监听本机,::(相当于0:0:0:0:0:0:0:0) 表示监听所有地址 -
int $port- 功能:指定监听的端口,如
9501 - 默认值:无
- 其它值:无
如果
$sockType值为 UnixSocket Stream/Dgram,此参数将被忽略
监听小于1024端口需要root权限
如果此端口被占用server->start时会失败 - 功能:指定监听的端口,如
-
int $mode- 功能:指定运行模式
- 默认值:SWOOLE_PROCESS 多进程模式(默认)
- 其它值:SWOOLE_BASE 基本模式
-
int $sockType- 功能:指定这组 Server 的类型
- 默认值:无
- 其它值:
SWOOLE_TCP/SWOOLE_SOCK_TCPtcp ipv4 socketSWOOLE_TCP6/SWOOLE_SOCK_TCP6tcp ipv6 socketSWOOLE_UDP/SWOOLE_SOCK_UDPudp ipv4 socketSWOOLE_UDP6/SWOOLE_SOCK_UDP6udp ipv6 socket- SWOOLE_UNIX_DGRAM unix socket dgram
- SWOOLE_UNIX_STREAM unix socket stream
使用
$sock_type|SWOOLE_SSL可以启用SSL隧道加密。启用SSL后必须配置 ssl_key_file 和 ssl_cert_file
-
// 可以混合使用UDP/TCP,同时监听内网和外网端口,多端口监听参考 addlistener 小节。
$server = new \Swoole\Server($host, $port = 0, $mode = SWOOLE_PROCESS, $sockType = SWOOLE_SOCK_TCP);
$server->addlistener("127.0.0.1", 9502, SWOOLE_SOCK_TCP); // 添加 TCP
$server->addlistener("192.168.1.100", 9503, SWOOLE_SOCK_TCP); // 添加 Web Socket
$server->addlistener("0.0.0.0", 9504, SWOOLE_SOCK_UDP); // UDP
$server->addlistener("/var/run/myserv.sock", 0, SWOOLE_UNIX_STREAM); //UnixSocket Stream
$server->addlistener("127.0.0.1", 9502, SWOOLE_SOCK_TCP | SWOOLE_SSL); //TCP + SSL
$port = $server->addListener("0.0.0.0", 0, SWOOLE_SOCK_TCP); // 系统随机分配端口,返回值为随机分配的端口
echo $port->port;
(2):set()
用于设置运行时的各项参数。服务器启动后通过 $serv->setting 来访问 Server->set 方法设置的参数数组。
Swoole\Server->set(array $setting): void
Server->set 必须在 Server->start 前调用
示例
$server->set(array(
'reactor_num' => 2, // reactor thread num
'worker_num' => 4, // worker process num
'backlog' => 128, // listen backlog
'max_request' => 50,
'dispatch_mode' => 1,
));
(3):on()
注册 Server 的事件回调函数。
Swoole\Server->on(string $event, mixed $callback): void
重复调用 on 方法时会覆盖上一次的设定
-
参数
-
string $event- 功能:回调事件名称
- 默认值:无
- 其它值:无
大小写不敏感,具体有哪些事件回调参考事件,事件名称字符串不要加
on -
mixed $callback- 功能:回调函数
- 默认值:无
- 其它值:无
可以是函数名的字符串,类静态方法,对象方法数组,匿名函数 参考此节。
-
(4):addListener()
增加监听的端口。业务代码中可以通过调用 Server->getClientInfo 来获取某个连接来自于哪个端口。
Swoole\Server->addListener(string $host, int $port, int $sockType): bool|Swoole\Server\Port
监听 1024 以下的端口需要 root 权限
主服务器是 WebSocket 或 HTTP 协议,新监听的 TCP 端口默认会继承主 Server 的协议设置。必须单独调用 set 方法设置新的协议才会启用新协议
-
string $host- 功能:与
__construct()的$host相同 - 默认值:与
__construct()的$host相同 - 其它值:与
__construct()的$host相同
- 功能:与
-
int $port- 功能:与
__construct()的$port相同 - 默认值:与
__construct()的$port相同 - 其它值:与
__construct()的$port相同
- 功能:与
-
int $sockType- 功能:与
__construct()的$sockType相同 - 默认值:与
__construct()的$sockType相同 - 其它值:与
__construct()的$sockType相同
- 功能:与
-Unix Socket 模式下 $host 参数必须填写可访问的文件路径,$port 参数忽略
-Unix Socket 模式下,客户端 $fd 将不再是数字,而是一个文件路径的字符串
-Linux 系统下监听 IPv6 端口后使用 IPv4 地址也可以进行连接
(5):listen()
此方法是 addlistener 的别名。
(6):addProcess()
添加一个用户自定义的工作进程。此函数通常用于创建一个特殊的工作进程,用于监控、上报或者其他特殊的任务。
Swoole\Server->addProcess(Swoole\Process $process): int
不需要执行 start。在 Server 启动时会自动创建进程,并执行指定的子进程函数
-
参数
-
- 功能:
Swoole\Process对象 - 默认值:无
- 其它值:无
- 功能:
-
-
注意
- 创建的子进程可以调用
$server对象提供的各个方法,如getClientList/getClientInfo/stats
- 在Worker/Task进程中可以调用$process提供的方法与子进程进行通信
- 在用户自定义进程中可以调用$server->sendMessage与Worker/Task进程通信
- 用户进程内不能使用Server->task/taskwait接口
- 用户进程内可以使用Server->send/close等接口
- 用户进程内应当进行while(true)(如下边的示例) 或 EventLoop 循环 (例如创建个定时器),否则用户进程会不停地退出重启 -
生命周期
- 用户进程的生存周期与
Master和 Manager 是相同的,不会受到 reload 影响
- 用户进程不受reload指令控制,reload时不会向用户进程发送任何信息
- 在shutdown关闭服务器时,会向用户进程发送SIGTERM信号,关闭用户进程
- 自定义进程会托管到Manager进程,如果发生致命错误,Manager进程会重新创建一个
- 自定义进程也不会触发onWorkerStop等事件
示例:
![]()
(7):start()
启动服务器,监听所有 TCP/UDP 端口。
Swoole\Server->start(): bool
-
提示
-
扩展
-
Master 主进程
- 主进程内有多个 Reactor 线程,基于
epoll/kqueue进行网络事件轮询。收到数据后转发到Worker进程去处理
- 主进程内有多个 Reactor 线程,基于
-
Manager 进程
- 对所有
Worker进程进行管理,Worker进程生命周期结束或者发生异常时自动回收,并创建新的Worker进程
- 对所有
-
Worker 进程
- 对收到的数据进行处理,包括协议解析和响应请求。未设置
worker_num,底层会启动与CPU数量一致的Worker进程。 - 启动失败扩展内会抛出致命错误,请检查
php error_log的相关信息。errno={number}是标准的Linux Errno,可参考相关文档。 - 如果开启了
log_file设置,信息会打印到指定的Log文件中。
- 对收到的数据进行处理,包括协议解析和响应请求。未设置
-
(8):reload()
安全地重启所有 Worker/Task 进程。
Swoole\Server->reload(bool $only_reload_taskworker = false): bool
例如:一台繁忙的后端服务器随时都在处理请求,如果管理员通过 kill 进程方式来终止 / 重启服务器程序,可能导致刚好代码执行到一半终止。
这种情况下会产生数据的不一致。如交易系统中,支付逻辑的下一段是发货,假设在支付逻辑之后进程被终止了。会导致用户支付了货币,但并没有发货,后果非常严重。Swoole 提供了柔性终止 / 重启的机制,管理员只需要向 Server 发送特定的信号,Server 的 Worker 进程可以安全的结束。参考 如何正确的重启服务
-
参数
-
bool $only_reload_taskworker- 功能:是否仅重启 Task 进程
- 默认值:false
- 其它值:无
-
-reload 有保护机制,当一次 reload 正在进行时,收到新的重启信号会丢弃
- 如果设置了 user/group,Worker 进程可能没有权限向 master 进程发送信息,这种情况下必须使用 root 账户,在 shell 中执行 kill 指令进行重启
-reload 指令对 addProcess 添加的用户进程无效
扩展
-
发送信号
SIGTERM: 向主进程 / 管理进程发送此信号服务器将安全终止- 在 PHP 代码中可以调用
$serv->shutdown()完成此操作 SIGUSR1: 向主进程 / 管理进程发送SIGUSR1信号,将平稳地restart所有Worker进程和TaskWorker进程SIGUSR2: 向主进程 / 管理进程发送SIGUSR2信号,将平稳地重启所有Task进程- 在 PHP 代码中可以调用
$serv->reload()完成此操作
# 重启所有worker进程
kill -USR1 主进程PID
# 仅重启task进程
kill -USR2 主进程PID
-
Process 模式
在
Process启动的进程中,来自客户端的TCP连接是在Master进程内维持的,worker进程的重启和异常退出,不会影响连接本身。 -
Base 模式
在
Base模式下,客户端连接直接维持在Worker进程中,因此reload时会切断所有连接。
Base 模式不支持 reload Task 进程
-
Reload 有效范围
Reload操作只能重新载入Worker进程启动后加载的 PHP 文件,使用get_included_files函数来列出哪些文件是在WorkerStart之前就加载的 PHP 文件,在此列表中的 PHP 文件,即使进行了reload操作也无法重新载入。要关闭服务器重新启动才能生效。
$serv->on('WorkerStart', function(Swoole\Server $server, int $workerId) {
var_dump(get_included_files()); //此数组中的文件表示进程启动前就加载了,所以无法reload
});
-
-
APC/OPcache
如果
PHP开启了APC/OPcache,reload重载入时会受到影响,有2种解决方案- 打开
APC/OPcache的stat检测,如果发现文件更新APC/OPcache会自动更新OPCode - 在
onWorkerStart中加载文件(require、include 等函数)之前执行apc_clear_cache或opcache_reset刷新OPCode缓存
- 打开
-
-
注意
- 平滑重启只对
onWorkerStart或 onReceive 等在Worker进程中include/require的 PHP 文件有效
-Server启动前就已经include/require的 PHP 文件,不能通过平滑重启重新加载
- 对于Server的配置即$serv->set()中传入的参数设置,必须关闭 / 重启整个Server才可以重新加载
-Server可以监听一个内网端口,然后可以接收远程的控制命令,去重启所有Worker进程
(9):stop()
使当前 Worker 进程停止运行,并立即触发 onWorkerStop 回调函数。
Swoole\Server->stop(int $workerId = -1, bool $waitEvent = false): bool
-
参数
-
int $workerId- 功能:指定
worker id - 默认值:-1
- 其它值:无
- 功能:指定
-
bool $waitEvent- 功能:控制退出策略,
false表示立即退出,true表示等待事件循环为空时再退出 - 默认值:false
- 其它值:true
- 功能:控制退出策略,
-
-
提示
- 异步 IO 服务器在调用
stop退出进程时,可能仍然有事件在等待。比如使用了Swoole\MySQL->query,发送了SQL语句,但还在等待MySQL服务器返回结果。这时如果进程强制退出,SQL的执行结果就会丢失了。
- 设置$waitEvent = true后,底层会使用异步安全重启策略。先通知Manager进程,重新启动一个新的Worker来处理新的请求。当前旧的Worker会等待事件,直到事件循环为空或者超过max_wait_time后,退出进程,最大限度的保证异步事件的安全性。
(10):shutdown()
关闭服务 Swoole\Server->shutdown(): void
-
提示
- 此函数可以用在
Worker进程内 - 向主进程发送
SIGTERM也可以实现关闭服务
- 此函数可以用在
kill -15 主进程PID
(11):tick()
添加 tick 定时器,可以自定义回调函数。此函数是 Swoole\Timer::tick 的别名。
Swoole\Server->tick(int $millisecond, mixed $callback): void
-
参数
-
int $millisecond- 功能:间隔时间【毫秒】
- 默认值:无
- 其它值:无
-
mixed $callback- 功能:回调函数
- 默认值:无
- 其它值:无
-
-
注意
-
Worker进程结束运行后,所有定时器都会自动销毁
-tick/after定时器不能在Server->start之前使用
![]()
(12):after()
添加一个一次性定时器,执行完成后就会销毁。此函数是 Swoole\Timer::after 的别名。
Swoole\Server->after(int $millisecond, mixed $callback)
-
参数
-
int $millisecond- 功能:执行时间【毫秒】
- 默认值:无
- 其它值:无
- 版本影响:在
Swoole v4.2.10以下版本最大不得超过86400000
-
mixed $callback- 功能:回调函数,必须是可以调用的,
callback函数不接受任何参数 - 默认值:无
- 其它值:无
- 功能:回调函数,必须是可以调用的,
-
-
注意
- 定时器的生命周期是进程级的,当使用
reload或kill重启关闭进程时,定时器会全部被销毁
- 如果有某些定时器存在关键逻辑和数据,请在onWorkerStop回调函数中实现,或参考 如何正确的重启服务
(13):defer()
延后执行一个函数,是 Event::defer 的别名
Swoole\Server->defer(callable $callback): void
-
参数
-
callable $callback- 功能:回调函数【必填】,可以是可执行的函数变量,可以是字符串、数组、匿名函数
- 默认值:无
- 其它值:无
-
-
注意
- 底层会在 EventLoop 循环完成后执行此函数。此函数的目的是为了让一些 PHP 代码延后执行,程序优先处理其他的
IO事件。比如某个回调函数有 CPU 密集计算又不是很着急,可以让进程处理完其他的事件再去 CPU 密集计算
- 底层不保证defer的函数会立即执行,如果是系统关键逻辑,需要尽快执行,请使用after定时器实现
- 在onWorkerStart回调中执行defer时,必须要等到有事件发生才会回调
示例
function query($server, $db) {
$server->defer(function() use ($db) {
$db->close();
});
}
(14):clearTimer()
清除 tick/after 定时器,此函数是 Swoole\Timer::clear 的别名。
Swoole\Server->clearTimer(int $timerId): bool
-
参数
-
int $timerId- 功能:指定定时器 id
- 默认值:无
- 其它值:无
-
-
注意
clearTimer仅可用于清除当前进程的定时器
示例
$timerId = $server->tick(1000, function ($id) use ($server) {
$server->clearTimer($id);//$id是定时器的id
});
(15):close()
关闭客户端连接
Swoole\Server->close(int $fd, bool $reset = false): bool
示例
$server->on('request', function ($request, $response) use ($server) {
$server->close($response->fd);
});
(16):send()
向客户端发送数据。
Swoole\Server->send(int $fd, string $data, int $serverSocket = -1): bool
-
参数
-
int $fd- 功能:指定客户端的文件描述符
- 默认值:无
- 其它值:无
-
string $data- 功能:发送的数据,
TCP协议最大不得超过2M,可修改 buffer_output_size 改变允许发送的最大包长度 - 默认值:无
- 其它值:无
- 功能:发送的数据,
-
int $serverSocket- 功能:向 UnixSocket DGRAM 对端发送数据时需要此项参数,TCP 客户端不需要填写
- 默认值:-1
- 其它值:无
-
-
提示
发送过程是异步的,底层会自动监听可写,将数据逐步发送给客户端,也就是说不是
send返回后对端就收到数据了。-
安全性
send操作具有原子性,多个进程同时调用send向同一个TCP连接发送数据,不会发生数据混杂
-
长度限制
- 如果要发送超过
2M的数据,可以将数据写入临时文件,然后通过sendfile接口进行发送 - 通过设置 buffer_output_size 参数可以修改发送长度的限制
- 在发送超过
8K的数据时,底层会启用Worker进程的共享内存,需要进行一次Mutex->lock操作
- 如果要发送超过
-
缓存区
- 当
Worker进程的 unixSocket 缓存区已满时,发送8K数据将启用临时文件存储 - 如果连续向同一个客户端发送大量数据,客户端来不及接收会导致
Socket内存缓存区塞满,Swoole 底层会立即返回false,false时可以将数据保存到磁盘,等待客户端收完已发送的数据后再进行发送
- 当
-
- 在协程模式开启了 send_yield 情况下
send遇到缓存区已满时会自动挂起,当数据被对端读走一部分后恢复协程,继续发送数据。
- 在协程模式开启了 send_yield 情况下
-
(17):sendfile()
发送文件到 TCP 客户端连接。
Swoole\Server->sendfile(int $fd, string $filename, int $offset = 0, int $length = 0): bool
-
参数
-
int $fd- 功能:指定客户端的文件描述符
- 默认值:无
- 其它值:无
-
string $filename- 功能:要发送的文件路径,如果文件不存在会返回
false - 默认值:无
- 其它值:无
- 功能:要发送的文件路径,如果文件不存在会返回
-
int $offset- 功能:指定文件偏移量,可以从文件的某个位置起发送数据
- 默认值:0 【默认为
0,表示从文件头部开始发送】 - 其它值:无
-
int $length- 功能:指定发送的长度
- 默认值:文件尺寸
- 其它值:无
-
-
注意
此函数与
Server->send都是向客户端发送数据,不同的是sendfile的数据来自于指定的文件
(18):sendto()
向任意的客户端 IP:PORT 发送 UDP 数据包。
向任意的客户端 IP:PORT 发送 UDP 数据包。
Swoole\Server->sendto(string $ip, int $port, string $data, int $serverSocket = -1): bool
-
参数
-
string $ip- 功能:指定客户端
ip - 默认值:无
- 其它值:无
$ip为IPv4或IPv6字符串,如192.168.1.102。如果IP不合法会返回错误 - 功能:指定客户端
-
int $port- 功能:指定客户端
port - 默认值:无
- 其它值:无
$port为1-65535的网络端口号,如果端口错误发送会失败 - 功能:指定客户端
-
string $data- 功能:要发送的数据内容,可以是文本或者二进制内容
- 默认值:无
- 其它值:无
-
int $serverSocket- 功能:指定使用哪个端口发送数据包的对应端口
server_socket描述符【可以在 onPacket 事件的$clientInfo中获取】 - 默认值:无
- 其它值:无
服务器可能会同时监听多个
UDP端口,参考多端口监听,此参数可以指定使用哪个端口发送数据包 - 功能:指定使用哪个端口发送数据包的对应端口
-
-
注意
必须监听了
UDP的端口,才可以使用向IPv4地址发送数据
必须监听了UDP6的端口,才可以使用向IPv6地址发送数据 -
示例
//向IP地址为220.181.57.216主机的9502端口发送一个hello world字符串。
$server->sendto('220.181.57.216', 9502, "hello world");
//向IPv6服务器发送UDP数据包
$server->sendto('2600:3c00::f03c:91ff:fe73:e98f', 9501, "hello world");
(19):sendwait()
同步地向客户端发送数据。
Swoole\Server->sendwait(int $fd, string $data): bool
-
参数
-
int $fd- 功能:指定客户端的文件描述符
- 默认值:无
- 其它值:无
-
string $data- 功能:指定客户端的文件描述符
- 默认值:无
- 其它值:无
-
-
提示
-
有一些特殊的场景,
Server需要连续向客户端发送数据,而Server->send数据发送接口是纯异步的,大量数据发送会导致内存发送队列塞满。 -
使用
Server->sendwait就可以解决此问题,Server->sendwait会等待连接可写。直到数据发送完毕才会返回。
-
-
注意
sendwait目前仅可用于 SWOOLE_BASE 模式sendwait只用于本机或内网通信,外网连接请勿使用sendwait,在enable_coroutine=>true (默认开启) 的时候也不要用这个函数,会卡死其他协程,只有同步阻塞的服务器才可以用。
(20):sendMessage()
向任意 Worker 进程或者 Task 进程发送消息。在非主进程和管理进程中可调用。收到消息的进程会触发 onPipeMessage 事件。
Swoole\Server->sendMessage(string $message, int $workerId): bool
-
参数
-
string $message- 功能:为发送的消息数据内容,没有长度限制,但超过
8K时会启动内存临时文件 - 默认值:无
- 其它值:无
- 功能:为发送的消息数据内容,没有长度限制,但超过
-
int $workerId- 功能:目标进程的
ID,范围参考 $worker_id - 默认值:无
- 其它值:无
- 功能:目标进程的
-
-
提示
- 在
Worker进程内调用sendMessage是异步 IO 的,消息会先存到缓冲区,可写时向 unixSocket 发送此消息 - 在 Task 进程 内调用
sendMessage默认是同步 IO,但有些情况会自动转换成异步 IO,参考同步 IO 转换成异步 IO - 在 User 进程 内调用
sendMessage和 Task 一样,默认同步阻塞的,参考同步 IO 转换成异步 IO
- 在
-
注意
- 如果
sendMessage()是异步 IO 的,如果对端进程因为种种原因不接收数据,千万不要一直调用sendMessage(),会导致占用大量的内存资源。可以增加一个应答机制,如果对端不回应就暂停调用;
-MacOS/FreeBSD下超过2K就会使用临时文件存储;
- 使用 sendMessage 必须注册onPipeMessage事件回调函数;
- 设置了 task_ipc_mode = 3 将无法使用 sendMessage 向特定的 task 进程发送消息。
示例

(21):exist()
检测 fd 对应的连接是否存在。
Swoole\Server->exist(int $fd): bool
-
参数
-
int $fd- 功能:文件描述符
- 默认值:无
- 其它值:无
-
-
提示
- 此接口是基于共享内存计算,没有任何
IO操作
- 此接口是基于共享内存计算,没有任何
(22):pause()
停止接收数据。
Swoole\Server->pause(int $fd)
-
参数
-
int $fd- 功能:指定文件描述符
- 默认值:无
- 其它值:无
-
-
提示
(23):resume()
恢复数据接收。与 pause 方法成对使用。
Swoole\Server->resume(int $fd)
-
参数
-
int $fd- 功能:指定文件描述符
- 默认值:无
- 其它值:无
-
-
提示
- 调用此函数后会将连接重新加入到 EventLoop 中,继续接收客户端数据
(24):getCallback()
获取 Server 指定名称的回调函数
Swoole\Server->getCallback(string $event_name)
-
参数
-
string $event_name- 功能:事件名称,不需要加
on,不区分大小写 - 默认值:无
- 其它值:参考 事件
- 功能:事件名称,不需要加
-
-
返回值
- 对应回调函数存在时,根据不同的回调函数设置方式返回
Closure/string/array - 对应回调函数不存在时,返回
null
- 对应回调函数存在时,根据不同的回调函数设置方式返回
(25):getClientInfo()
获取连接的信息,别名是 Swoole\Server->connection_info()
Swoole\Server->getClientInfo(int $fd, int $reactorId, bool $ignoreError = false): bool|array
-
参数
-
int $fd- 功能:指定文件描述符
- 默认值:无
- 其它值:无
-
int $reactorId- 功能:连接所在的 Reactor 线程
ID - 默认值:无
- 其它值:无
- 功能:连接所在的 Reactor 线程
-
bool $ignoreError- 功能:是否忽略错误,如果设置为
true,即使连接关闭也会返回连接的信息 - 默认值:无
- 其它值:无
- 功能:是否忽略错误,如果设置为
-
-
提示
-
客户端证书
- 仅在 onConnect 触发的进程中才能获取到证书
- 格式为
x509格式,可使用openssl_x509_parse函数获取到证书信息
-
当使用 dispatch_mode = 1/3 配置时,考虑到这种数据包分发策略用于无状态服务,当连接断开后相关信息会直接从内存中删除,所以
Server->getClientInfo是获取不到相关连接信息的。
-
-
返回值
- 调用失败返回
false - 调用成功返回
array
- 调用失败返回
示例


(26):getClientList()
遍历当前 Server 所有的客户端连接,Server::getClientList 方法是基于共享内存的,不存在 IOWait,遍历的速度很快。另外 getClientList 会返回所有 TCP 连接,而不仅仅是当前 Worker 进程的 TCP 连接。别名是 Swoole\Server->connection_list()
Swoole\Server->getClientList(int $start_fd = 0, int $pageSize = 10): bool|array
-
参数
-
int $start_fd- 功能:指定起始
fd - 默认值:无
- 其它值:无
- 功能:指定起始
-
int $pageSize- 功能:每页取多少条,最大不得超过
100 - 默认值:无
- 其它值:无
- 功能:每页取多少条,最大不得超过
-
-
返回值
- 调用成功将返回一个数字索引数组,元素是取到的
$fd。数组会按从小到大排序。最后一个$fd作为新的start_fd再次尝试获取 - 调用失败返回
false
- 调用成功将返回一个数字索引数组,元素是取到的
-
提示
- 推荐使用 Server::$connections 迭代器来遍历连接
getClientList仅可用于TCP客户端,UDP服务器需要自行保存客户端信息- SWOOLE_BASE 模式下只能获取当前进程的连接

(27):bind()
将连接绑定一个用户定义的 UID,可以设置 dispatch_mode=5 设置以此值进行 hash 固定分配。可以保证某一个 UID 的连接全部会分配到同一个 Worker 进程。
Swoole\Server->bind(int $fd, int $uid): bool
-
注意
- 仅在设置 dispatch_mode=5 时有效
- 未绑定 UID 时默认使用 fd 取模进行分配
- 同一个连接只能被 bind 一次,如果已经绑定了 UID,再次调用 bind 会返回 false
-
参数
-
int $fd- 功能:指定连接的
fd - 默认值:无
- 其它值:无
- 功能:指定连接的
-
int $uid- 功能:要绑定的
UID,必须为非0的数字 - 默认值:无
- 其它值:
UID最大不能超过4294967295,最小不能小于-2147483648
- 功能:要绑定的
-
-
提示
-
可以使用
$serv->getClientInfo($fd)查看连接所绑定UID的值 -
在默认的 dispatch_mode=2 设置下,
Server会按照socket fd来分配连接数据到不同的Worker进程。因为fd是不稳定的,一个客户端断开后重新连接,fd会发生改变。这样这个客户端的数据就会被分配到别的Worker。使用bind之后就可以按照用户定义的UID进行分配。即使断线重连,相同UID的TCP连接数据会被分配相同的Worker进程。 -
时序问题
- 客户端连接服务器后,连续发送多个包,可能会存在时序问题。在
bind操作时,后续的包可能已经dispatch,这些数据包仍然会按照fd取模分配到当前进程。只有在bind之后新收到的数据包才会按照UID取模分配。 - 因此如果要使用
bind机制,网络通信协议需要设计握手步骤。客户端连接成功后,先发一个握手请求,之后客户端不要发任何包。在服务器bind完后,并回应之后。客户端再发送新的请求。
- 客户端连接服务器后,连续发送多个包,可能会存在时序问题。在
-
重新绑定
- 某些情况下,业务逻辑需要用户连接重新绑定
UID。这时可以切断连接,重新建立TCP连接并握手,绑定到新的UID。
- 某些情况下,业务逻辑需要用户连接重新绑定
-

(28):stats()
得到当前 Server 的活动 TCP 连接数,启动时间等信息,accept/close(建立连接 / 关闭连接) 的总次数等信息。
Swoole\Server->stats(): array
![]()
![]()
(29):task()
投递一个异步任务到 task_worker 池中。此函数是非阻塞的,执行完毕会立即返回。Worker 进程可以继续处理新的请求。使用 Task 功能,必须先设置 task_worker_num,并且必须设置 Server 的 onTask 和 onFinish 事件回调函数。
Swoole\Server->task(mixed $data, int $dstWorkerId = -1, callable $finishCallback): int
-
参数
-
mixed $data- 功能:要投递的任务数据,必须是可序列化的 PHP 变量
- 默认值:无
- 其它值:无
-
int $dstWorkerId -
callable $finishCallback- 功能:
finish回调函数,如果任务设置了回调函数,Task返回结果时会直接执行指定的回调函数,不再执行Server的 onFinish 回调,只有在Worker进程中投递任务才可触发 - 默认值:
null - 其它值:无
- 功能:
-
-
返回值
- 调用成功,返回值为整数
$task_id,表示此任务的ID。如果有finish回调,onFinish 回调中会携带$task_id参数 - 调用失败,返回值为
false,$task_id可能为0,因此必须使用===判断是否失败
- 调用成功,返回值为整数
-
提示
- 此功能用于将慢速的任务异步地去执行,比如一个聊天室服务器,可以用它来进行发送广播。当任务完成时,在 task 进程中调用
$serv->finish("finish")告诉worker进程此任务已完成。当然Swoole\Server->finish是可选的。 task底层使用 unixSocket 通信,是全内存的,没有IO消耗。单进程读写性能可达100万/s,不同的进程使用不同的unixSocket通信,可以最大化利用多核。- 未指定目标 Task 进程,调用
task方法会判断 Task 进程的忙闲状态,底层只会向处于空闲状态的 Task 进程投递任务。如果所有 Task 进程均处于忙的状态,底层会轮询投递任务到各个进程。可以使用 server->stats 方法获取当前正在排队的任务数量。 - 第三个参数,可以直接设置 onFinish 函数,如果任务设置了回调函数,
Task返回结果时会直接执行指定的回调函数,不再执行Server的 onFinish 回调,只有在Worker进程中投递任务才可触发
$server->task($data, -1, function (Swoole\Server $server, $task_id, $data) { echo "Task Callback: "; var_dump($task_id, $data); }); - 此功能用于将慢速的任务异步地去执行,比如一个聊天室服务器,可以用它来进行发送广播。当任务完成时,在 task 进程中调用
-
$task_id是从0-42亿的整数,在当前进程内是唯一的- 默认不启动
task功能,需要在手动设置task_worker_num来启动此功能 TaskWorker的数量在 Server->set() 参数中调整,如task_worker_num => 64,表示启动64个进程来接收异步任务
-
配置参数
Server->task/taskwait/finish3个方法当传入的$data数据超过8K时会启用临时文件来保存。当临时文件内容超过 server->package_max_length 时底层会抛出一个警告。此警告不影响数据的投递,过大的Task可能会存在性能问题。
WARN: task package is too big. -
单向任务
- 从
Master、Manager、UserProcess进程中投递的任务,是单向的,在TaskWorker进程中无法使用return或Server->finish()方法返回结果数据。
- 从
-
注意
-
task方法不能在 task 进程中调用
- 使用task必须为Server设置 onTask 和 onFinish 回调,否则Server->start会失败
-task操作的次数必须小于 onTask 处理速度,如果投递容量超过处理能力,task数据会塞满缓存区,导致Worker进程发生阻塞。Worker进程将无法接收新的请求
- 使用 addProcess 添加的用户进程中可以使用task单向投递任务,但不能返回结果数据。请使用 sendMessage 接口与Worker/Task进程通信




