React Native(Expo)与PHP WebSocket连接失败问题求助
问题:移动端WebSocket连接被拒绝,无法接收数据库变更通知
我是移动开发新手,从没搭建过Socket服务器。现在开发一款移动端应用,想要在数据库表发生变更时向相关用户发送通知,于是搭建了基于Ratchet的PHP WebSocket服务器,计划通过前端推送通知(即使应用处于关闭状态),但目前前端无法连接到服务器,报错如下:
LOG WebSocket error: {"isTrusted": false, "message": "The operation couldn’t be completed. Connection refused"}
LOG WebSocket is closed.
PHP WebSocket服务器代码
<?php require __DIR__ . '/vendor/autoload.php'; use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; use React\EventLoop\Loop; use Ratchet\App; class DatabaseWatcher implements MessageComponentInterface { protected $clients; protected $lastMaxId; public function __construct() { $this->clients = new \SplObjectStorage; $this->lastMaxId = $this->getMaxId(); } public function onOpen(ConnectionInterface $conn) { $this->clients->attach($conn, ['ref_restaurant' => null]); echo "New connection! ({$conn->resourceId})\n"; } public function onMessage(ConnectionInterface $from, $msg) { $data = json_decode($msg, true); if (isset($data['ref_restaurant'])) { $this->clients[$from]['ref_restaurant'] = $data['ref_restaurant']; echo "Client {$from->resourceId} set ref_restaurant to {$data['ref_restaurant']}\n"; } } public function onClose(ConnectionInterface $conn) { $this->clients->detach($conn); echo "Connection {$conn->resourceId} has disconnected\n"; } public function onError(ConnectionInterface $conn, \Exception $e) { echo "An error has occurred: {$e->getMessage()}\n"; $conn->close(); } public function getMaxId() { try { $pdo = new PDO('mysql:host=193.203.168.77;dbname=u440859155_db_yumeats', 'u440859155_yumeats', 'YumEats2024@'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $stmt = $pdo->query('SELECT MAX(id) AS max_id FROM orders'); $result = $stmt->fetch(PDO::FETCH_ASSOC); return $result['max_id'] ?? 0; } catch (PDOException $e) { echo "Database error: " . $e->getMessage() . "\n"; return 0; } } public function checkDatabase() { try { $pdo = new PDO('mysql:host=193.203.168.77;dbname=u440859155_db_yumeats', 'u440859155_yumeats', 'YumEats2024@'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $stmt = $pdo->prepare('SELECT * FROM orders WHERE id > :lastMaxId'); $stmt->execute(['lastMaxId' => $this->lastMaxId]); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); if (!empty($rows)) { $this->lastMaxId = max(array_column($rows, 'id')); echo "New entries found:\n"; print_r($rows); // Log the new entries foreach ($this->clients as $client) { $clientRestaurant = $this->clients[$client]['ref_restaurant']; $filteredRows = array_filter($rows, function ($row) use ($clientRestaurant) { return $row['ref_restaurant'] === $clientRestaurant; }); if (!empty($filteredRows)) { $client->send(json_encode($filteredRows)); } } } } catch (PDOException $e) { echo "Database error: " . $e->getMessage() . "\n"; } } } $app = new Ratchet\App('0.0.0.0', 8080); $loop = Loop::get(); $databaseWatcher = new DatabaseWatcher(); $loop->addPeriodicTimer(1, function () use ($databaseWatcher) { $databaseWatcher->checkDatabase(); }); $app->route('/database-watcher', $databaseWatcher, ['*']); $app->run();
前端(React)连接代码
const ws = useRef(null); const initializeWebSocket = (refRestaurant) => { ws.current = new WebSocket('ws://localhost:8080/database-watcher'); ws.current.onopen = () => { console.log('WebSocket is connected.'); ws.current.send(JSON.stringify({ ref_restaurant: refRestaurant })); }; ws.current.onmessage = (event) => { const data = JSON.parse(event.data); console.log('Received data:', data); setOrdersAndClients(prevOrders => [...prevOrders, ...data]); Alert.alert( "Nouvelle commande", "Une nouvelle commande a été reçue.", [{ text: "OK" }] ); }; ws.current.onclose = () => { console.log('WebSocket is closed.'); }; ws.current.onerror = (error) => { console.log('WebSocket error:', error); }; };
排查与修复方案
1. 修正连接地址(最核心问题)
前端代码中使用ws://localhost:8080仅适用于本地模拟器/浏览器测试:
- 真机测试时,必须替换为服务器的公网IP或域名(比如你的数据库IP
193.203.168.77),即ws://193.203.168.77:8080/database-watcher - Android模拟器可使用
10.0.2.2映射主机的localhost,iOS模拟器可直接用localhost(前提是服务器和模拟器在同一局域网)
2. 开放服务器端口
- 在服务器防火墙(如ufw、iptables)中放行8080端口的TCP入站连接
- 云服务器需在控制台安全组中添加8080端口的允许规则
3. 确保服务器正常运行
- 启动服务器后,终端需保持运行状态,若没有报错且能打印日志,说明服务器在正常监听
- 生产环境建议用
supervisor等工具守护进程,避免终端关闭后服务器停止
4. 处理协议与跨域问题
- 若前端使用HTTPS环境,必须改用
wss://加密连接,可通过Nginx反向代理WebSocket请求并配置SSL证书 - 确保服务器允许跨域连接(Ratchet的
route方法已设置['*'],这部分无需修改)
代码优化建议
- 复用数据库连接:当前每次检查数据库都新建PDO连接,建议在构造函数中初始化一次,减少资源消耗
- 替换轮询逻辑:每秒轮询数据库效率低,可改用MySQL Binlog监听或触发器+消息队列的方式,更高效捕获数据变更
- 后台通知补充:WebSocket仅能在应用前台在线时推送,若要实现应用关闭时的通知,需结合FCM(Android)或APNs(iOS)推送服务
内容的提问来源于stack exchange,提问作者dwain
相关产品推荐
相关产品推荐

