React Native无法连接DigitalOcean上Laravel的Soketi Pusher服务器求助
Laravel + Soketi + React Native 连接失败排查
在DigitalOcean的Ubuntu服务器部署了Laravel应用与Soketi服务器,React Native应用无法连接,以下是配置信息与错误日志,附排查方案:
broadcasting.php 配置
'pusher' => [ 'driver' => 'pusher', 'key' => env('PUSHER_APP_KEY'), 'secret' => env('PUSHER_APP_SECRET'), 'app_id' => env('PUSHER_APP_ID'), 'log' => true, 'options' => [ 'host' => env('PUSHER_HOST', '127.0.0.1'), 'port' => env('PUSHER_PORT', 6001), 'scheme' => env('PUSHER_SCHEME', 'http'), 'encrypted' => true, 'useTLS' => env('PUSHER_SCHEME') === 'https', 'curl_options' => [ CURLOPT_SSL_VERIFYHOST => 0, CURLOPT_SSL_VERIFYPEER => 0, ], ], ],
.env 变量(已修改凭据)
PUSHER_APP_KEY="9b9a489dffdf0c38f28f43777" PUSHER_APP_SECRET="26574dsfsfsf57220cf042bd77" PUSHER_APP_ID="13023345464644" PUSHER_APP_CLUSTER="eu" # SOKETI SERVER DETAILS PUSHER_HOST=127.0.0.1 PUSHER_PORT=6001 PUSHER_SCHEME=https
React Native Echo 启动代码
Pusher.logToConsole = true; let myIp = 'laraapp.com'; let port = 6001; let options = { key: env.PUSHER_APP_KEY, wsHost: myIp, wsPort: port, wssPort: port, enableStats: true, logToConsole: true, encrypted: true, forceTLS: false, enabledTransports: ['ws', 'wss'], }; let PusherClient = new Pusher(options.key, options); PusherClient.connection.bind('error', () => console.log('PusherClient::error', arguments)); const echo = new Echo({ broadcaster: 'pusher', client: PusherClient, ...options });
Supervisor 配置(启动 Soketi)
[program:soketi] process_name=%(program_name)s_%(process_num)02d command=soketi start autostart=true autorestart=true stopasgroup=true killasgroup=true user=Gabotron numprocs=1 redirect_stderr=true stderr_logfile=/home/Gabotron/logs/soketi.err.log stdout_logfile=/home/Gabotron/logs/soketi.out.log stopwaitsecs=60 stopsignal=sigint minfds=10240 environment=SOKETI_SSL_CERT="/etc/letsencrypt/live/laraapp.com/fullchain.pem",SOKETI_SSL_KEY="/etc/letsencrypt/live/laraapp.com/privkey.pem", PATH="/home/Gabotron/.nvm/versions/node/v14.21.3/bin:%(ENV_PATH)s",NODE_ENV="production",
Nginx 配置
server { server_name laraapp.com www.laraapp.com; root /var/www/laraapp.com/public; index index.php index.html; access_log /var/log/laraapp.com/access.log; error_log /var/log/laraapp.com/error.log; # serve static files directly location ~* \.(jpg|jpeg|gif|css|png|js|ico|html)$ { access_log off; expires max; log_not_found off; } # removes trailing slashes (prevents SEO duplicate content issues) if (!-d $request_filename) { rewrite ^/(.+)/$ /$1 permanent; } # enforce NO www if ($host ~* ^www\.(.*)) { set $host_without_www $1; rewrite ^/(.*)$ $scheme://$host_without_www/$1 permanent; } # unless the request is for a valid file (image, js, css, etc.), send to bootstrap if (!-e $request_filename) { rewrite ^/(.*)$ /index.php?/$1 last; break; } location / { try_files $uri $uri/ /index.php?$query_string; } location ~* \.php$ { try_files $uri = 404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass unix:/var/run/php/php7.3-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } location ~ /\.ht { deny all; } location /ws/ { proxy_pass http://127.0.0.1:6001; # Point to the Soketi port proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; } listen 443 ssl; # managed by Certbot ssl_certificate /etc/letsencrypt/live/laraapp.com/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/laraapp.com/privkey.pem; # managed by Certbot include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot } server { if ($host = www.laraapp.com) { return 301 https://$host$request_uri; } # managed by Certbot if ($host = laraapp.com) { return 301 https://$host$request_uri; } # managed by Certbot listen 80; server_name laraapp.com www.laraapp.com; return 404; # managed by Certbot }
前端连接日志
Pusher : : ["Connecting",{"transport":"wss","url":"wss://laraapp.com:6001/app/9b9a4890c38f28f43777?protocol=7&client=js&version=7.0.6&flash=false"}] LOG Pusher : : ["State changed","connecting -> unavailable"] LOG Pusher : : ["Connecting",{"transport":"ws","url":"ws://laraapp.com:6001/app/9b9a4890c38f28f43777?protocol=7&client=js&version=7.0.6&flash=false"}] LOG Pusher : : ["Connecting",{"transport":"wss","url":"wss://laraapp.com:6001/app/9b9a4890c38f28f43777?protocol=7&client=js&version=7.0.6&flash=false"}]
Laravel 广播错误日志
[2024-01-11 17:27:21] production.ERROR: Pusher error: . {"exception":"[object] (Illuminate\\Broadcasting\\BroadcastException(code: 0): Pusher error: . at /Users/peter/Desktop/proyectos/laraapp.com/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/PusherBroadcaster.php:128)
排查步骤
修复凭据不一致问题
前端日志中的PUSHER_APP_KEY是9b9a4890c38f28f43777,但.env里的是9b9a489dffdf0c38f28f43777,两者缺失了dffdf部分,必须完全统一。更新React Native代码中的密钥,确保和Laravel.env一致。调整前端连接方式(使用Nginx代理)
Nginx已经配置了/ws/代理到Soketi,无需让前端直连6001端口(可能被防火墙拦截)。修改React Native的Echo配置:let options = { key: env.PUSHER_APP_KEY, wsHost: 'laraapp.com', wsPort: 443, wssPort: 443, path: '/ws', // 新增代理路径 encrypted: true, forceTLS: true, enabledTransports: ['wss'], };这样前端通过443端口的Nginx代理连接Soketi,避免端口开放问题。
验证Soketi服务状态
- 查看Soketi日志:
cat /home/Gabotron/logs/soketi.err.log和cat /home/Gabotron/logs/soketi.out.log,确认服务是否正常启动,无SSL证书错误或端口占用。 - 本地测试Soketi连接:使用
wscat(需先安装npm install -g wscat)执行:
若能成功连接并收到Pusher协议消息,说明Soketi服务正常。wscat -c wss://127.0.0.1:6001/app/9b9a489dffdf0c38f28f43777?protocol=7&client=js&version=7.0.6
- 查看Soketi日志:
检查防火墙规则
- 若坚持直连6001端口,需在DigitalOcean控制面板的防火墙中添加入站规则,允许TCP 6001端口;同时在服务器上执行
ufw allow 6001/tcp开放端口。 - 若使用Nginx代理,只需确保443端口已开放(已由Certbot配置)。
- 若坚持直连6001端口,需在DigitalOcean控制面板的防火墙中添加入站规则,允许TCP 6001端口;同时在服务器上执行
优化Laravel日志获取详细错误
- 在.env中设置
LOG_LEVEL=debug,重新触发广播,查看storage/logs/laravel.log中的详细错误信息,定位Pusher连接失败的具体原因(如SSL验证、端口不通、密钥错误等)。
- 在.env中设置
确认Soketi与Laravel的SSL配置匹配
Supervisor中Soketi已配置SSL证书,所以Laravel的.env中PUSHER_SCHEME=https是正确的,但需确保Soketi监听的是6001端口的HTTPS,而非HTTP。可在Soketi启动命令中添加--port=6001 --ssl-cert=... --ssl-key=...(虽然环境变量已配置,但显式指定更保险)。
内容的提问来源于stack exchange,提问作者gabogabans
相关产品推荐
相关产品推荐

