You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用PHP Ratchet实现关联手机号的WebSocket服务器管理Socket?

解决方案:用Ratchet实现手机号与Amazon Connect Socket的绑定转发

核心架构思路

放弃Webhook直接创建Amazon Connect Socket的方式,改用独立运行的Ratchet服务器作为中间层:

  • Ratchet维护全局连接池,每个手机号对应一个持久的Amazon Connect Socket连接
  • Webhook仅负责接收WhatsApp消息,通过HTTP接口将消息转发给Ratchet
  • Ratchet根据手机号匹配对应Socket,将消息转发给Amazon Connect;同时监听Amazon Connect的返回消息,再转发回WhatsApp

实现步骤与代码示例

1. 创建Ratchet连接管理类

这个类负责维护手机号与Amazon Connect连接的映射,处理双向消息转发:

use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use React\Socket\Connector;
use React\Promise\PromiseInterface;

class AmazonConnectGateway implements MessageComponentInterface {
    // 存储手机号到Amazon Connect连接的映射
    private array $phoneToAcConn = [];
    // 连接互斥锁,避免并发创建重复连接
    private \React\Mutex\Mutex $connMutex;

    public function __construct() {
        $this->connMutex = new \React\Mutex\Mutex();
    }

    // 获取或创建对应手机号的Amazon Connect连接
    public function getOrCreateAcConnection(string $phoneNumber): PromiseInterface|null {
        if (isset($this->phoneToAcConn[$phoneNumber])) {
            return \React\Promise\resolve($this->phoneToAcConn[$phoneNumber]);
        }

        return $this->connMutex->lock()->then(function () use ($phoneNumber) {
            // 再次检查,避免锁等待期间已创建连接
            if (isset($this->phoneToAcConn[$phoneNumber])) {
                return $this->phoneToAcConn[$phoneNumber];
            }

            $connector = new Connector();
            return $connector->connect('tcp://your-amazon-connect-socket:port')->then(function ($acConn) use ($phoneNumber) {
                $this->phoneToAcConn[$phoneNumber] = $acConn;

                // 监听Amazon Connect消息,转发回WhatsApp
                $acConn->on('data', function ($data) use ($phoneNumber) {
                    $this->sendToWhatsApp($phoneNumber, $data);
                });

                // 连接关闭时清理映射
                $acConn->on('close', function () use ($phoneNumber) {
                    unset($this->phoneToAcConn[$phoneNumber]);
                });

                // 心跳保活(根据Amazon Connect要求调整间隔)
                $heartbeatInterval = \React\EventLoop\Loop::get()->addPeriodicTimer(30, function () use ($acConn, $phoneNumber) {
                    if (!$acConn->isReadable()) {
                        unset($this->phoneToAcConn[$phoneNumber]);
                        \React\EventLoop\Loop::get()->cancelTimer($this);
                        return;
                    }
                    $acConn->write('PING');
                });

                return $acConn;
            });
        })->finally(function () {
            $this->connMutex->unlock();
        });
    }

    // 转发WhatsApp消息到Amazon Connect
    public function forwardToAc(string $phoneNumber, string $message): void {
        $this->getOrCreateAcConnection($phoneNumber)->then(function ($acConn) use ($message) {
            $acConn->write($message);
        }, function ($error) {
            error_log("转发失败: {$error->getMessage()}");
        });
    }

    // 调用WhatsApp API发送消息
    private function sendToWhatsApp(string $phoneNumber, string $message): void {
        // 替换为实际WhatsApp API调用逻辑
        $payload = json_encode([
            'to' => $phoneNumber,
            'text' => ['body' => $message]
        ]);

        $ch = curl_init('https://graph.facebook.com/v18.0/your-whatsapp-number/messages');
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'Authorization: Bearer your-access-token'
        ]);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
        curl_exec($ch);
        curl_close($ch);
    }

    // MessageComponentInterface 空实现(若无需外部WebSocket客户端可忽略)
    public function onOpen(ConnectionInterface $conn) {}
    public function onMessage(ConnectionInterface $from, $msg) {}
    public function onClose(ConnectionInterface $conn) {}
    public function onError(ConnectionInterface $conn, \Exception $e) {}
}

2. 启动Ratchet服务器与HTTP接口

创建启动脚本,同时运行WebSocket服务(可选)和HTTP接口供Webhook调用:

use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use React\Http\HttpServer as ReactHttpServer;
use React\Socket\SocketServer;

require __DIR__ . '/vendor/autoload.php';

$gateway = new AmazonConnectGateway();

// 启动HTTP服务器,接收Webhook转发请求
$httpServer = new ReactHttpServer(function ($request, $response) use ($gateway) {
    if ($request->getMethod() !== 'POST' || $request->getPath() !== '/forward') {
        return $response->withStatus(404)->write('Not Found');
    }

    $body = (string)$request->getBody();
    $data = json_decode($body, true);

    if (!isset($data['phone'], $data['message'])) {
        return $response->withStatus(400)->write('Missing phone or message');
    }

    $gateway->forwardToAc($data['phone'], $data['message']);
    return $response->withStatus(200)->write('Forwarded');
});

$httpSocket = new SocketServer('0.0.0.0:8081');
$httpServer->listen($httpSocket);

// 启动WebSocket服务器(若需要外部客户端连接可保留)
$wsServer = IoServer::factory(
    new HttpServer(new WsServer($gateway)),
    8080
);

// 启动事件循环
\React\EventLoop\Loop::get()->run();

3. 修改WhatsApp Webhook脚本

Webhook仅需接收消息,然后调用Ratchet的HTTP接口转发:

// WhatsApp Webhook接收脚本
$input = file_get_contents('php://input');
$data = json_decode($input, true);

// 提取发送方手机号和消息内容(根据WhatsApp Webhook格式调整)
$phone = $data['entry'][0]['changes'][0]['value']['messages'][0]['from'];
$message = $data['entry'][0]['changes'][0]['value']['messages'][0]['text']['body'];

// 调用Ratchet HTTP接口转发
$ch = curl_init('http://localhost:8081/forward');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'phone' => $phone,
    'message' => $message
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_exec($ch);
curl_close($ch);

echo 'OK';

关键注意事项

  • 连接保活:添加心跳机制避免Amazon Connect Socket超时断开
  • 并发安全:用互斥锁防止同一手机号重复创建连接
  • 错误重试:当Amazon Connect连接断开时,下次消息会自动重建连接
  • 日志监控:添加详细日志,方便排查连接或转发失败问题

内容的提问来源于stack exchange,提问作者Marco

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 17:55:17