ip黑名单拦截应放在onconnect中,因其是连接建立后首个回调,可获取真实ip并立即断开恶意连接,避免资源浪费。

在 Workerman 4 中,onConnect 回调是客户端 TCP 连接刚建立、尚未发送任何数据时触发的最早钩子。利用它做 IP 黑名单拦截,既高效又安全——连接在协议层就被拒绝,不占用后续资源。
IP 黑名单拦截应放在 onConnect 中
Workerman 的 onConnect 是连接握手完成后的第一个回调,此时已能获取客户端真实 IP(即使经过 Nginx 转发,只要配置了 X-Real-IP 或 X-Forwarded-For 并启用 $worker->reusePort = true 及正确设置 $worker->transport),但尚未读取应用层数据。在这个时机拦截,可避免进入 onMessage 流程,节省内存与 CPU。
注意:不能在 onMessage 或 onWorkerStart 做此判断——前者太晚,连接已建立并可能携带恶意载荷;后者无法获取单个连接的客户端 IP。
获取真实客户端 IP 的关键配置
Workerman 默认使用 $connection->getRemoteIp() 获取 IP,但若服务前有反向代理(如 Nginx),需确保:
- Nginx 正确透传 IP:
proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - Workerman 启用 IP 透传解析(v4.0.21+ 支持):
在 Worker 初始化后添加:$worker->connectionClass = \Workerman\Connection\TcpConnection::class;$worker->context = ['ssl' => []]; // 若用 SSL,否则可省略
更重要的是,在onConnect中手动解析头信息(Workerman 不自动处理 HTTP 头,仅适用于 HTTP 协议 worker;TCP/WS 需自行约定或依赖代理层设置 socket option)。 - 对纯 TCP/WS 类型服务,真实 IP 就是
$connection->getRemoteIp()返回值(内核层面已知),无需额外解析。
在 onConnect 中实现黑名单拦截
示例代码(基于 TcpConnection):
$worker->onConnect = function ($connection) {
$ip = $connection->getRemoteIp();
// 简单数组黑名单(适合少量 IP)
$blacklist = ['192.168.1.100', '203.0.113.5'];
if (in_array($ip, $blacklist)) {
$connection->close(); // 立即断开,不响应任何数据
return;
}
// 或使用 Redis 查询(推荐用于动态管理)
// $redis = new Redis();
// $redis->connect('127.0.0.1', 6379);
// if ($redis->sIsMember('ip:blacklist', $ip)) {
// $connection->close();
// return;
// }
// 连接合法,继续后续逻辑(如设置 heartbeat、绑定 session 等)
$connection->heartbeatIdleTime = 60;
};
关键点:
- 调用
$connection->close()即可终止连接,无需 throw 异常或 return false; - 不要在
onConnect中执行阻塞操作(如 MySQL 查询),否则会卡住整个 event loop;Redis 推荐用 pconnect + pipeline 或改用 Swoole 协程 client; - 可结合限流(如基于 IP 的连接频次)一并在此处判断,提升防护粒度。
进阶:支持 CIDR 段匹配与持久化管理
生产环境建议支持网段拦截(如 192.168.0.0/16),可用 PHP 内置函数 ip_in_range() 或第三方库(如 spatie/ip-address):
use Spatie\IpAddress\IpAddress;
if (IpAddress::fromString($ip)->matches('192.168.0.0/16')) {
$connection->close();
return;
}
黑名单数据建议存于 Redis Set 或本地文件(配合 inotify 监听热更新),避免每次 reload worker 才生效。











