Laravel WebSockets:订阅私人频道不起作用

Laravel WebSockets: Subscribing to private channels does not work

软件:

在 websockets.php (complete file) 中,我使用证书设置了 local_certlocal_pk。如果我将此选项留空,我什至无法连接。我也将 verify_peer 设置为 false,因为如果我不这样做,我也无法连接。

broadcasting.php:

'pusher' => [
        'driver' => 'pusher',
        'key' => env('PUSHER_APP_KEY'),
        'secret' => env('PUSHER_APP_SECRET'),
        'app_id' => env('PUSHER_APP_ID'),
        'options' => [
            'cluster' => env('PUSHER_APP_CLUSTER'),
            'host' => '127.0.0.1',
            'port' => 6001,
            'scheme' => 'https',
            'curl_options' => [
                CURLOPT_SSL_VERIFYHOST => 0,
                CURLOPT_SSL_VERIFYPEER => 0,
            ]
        ],
    ],

如果我去掉 curl 选项,我会得到一个空的广播异常 like described here

bootstrap.js:

window.Pusher = require('pusher-js');
window.Echo = new Echo({
    broadcaster: 'pusher',
    key: '7d23096ae0ab2d02d220',
    wsHost: window.location.hostname,
    wsPort: 6001,
    wssPort: 6001,
    encrypted: true,
    disableStats: true,
    auth: {
        headers: {
            'X-CSRF-TOKEN': window.App.csrfToken,
        },
    },
})

这是我在 运行 php artisan websockets:serve:

之后从日志中得到的全部信息
New connection opened for app key 7d23096ae0ab2d02d220.
Connection id 49092664.114416323 sending message {"event":"pusher:connection_established","data":"{\"socket_id\":\"49092664.114416323\",\"activity_timeout\":30}"}

我应该得到的是关于收听/加入频道和发送消息等消息。但所有这些目前都不起作用。我有这样的东西:

Echo.private('notifications.' + this.user.id)
                .listen('UserNotificationSent', (e) => {
                    console.log(e)
                })

事件:例如UserNotificationSent.php

当然在内部我也设置了其他一切:带身份验证的通道等。一切都在我机器上的较低 Laravel 版本 (5.4) 上本地工作。但我最近更新到 5.8 并部署到服务器,现在我为此苦苦挣扎。

我也opened an issue on github.

重要更新 这实际上不是由于部署,我在本地设置中遇到了同样的问题。有趣的是,通过 Echo.channel() 收听频道是有效的,但是 .private() 无效。在Github(上面的link)我遇到了一个有完全相同问题的人。我们还没有找到解决方案。

对事件使用 broadcastAs() 方法

public function broadcastAs()
{
    return 'UserNotificationSent';
}

然后这样听。

.listen('.UserNotificationSent', function (e) {
    ....
});

在 UserNotificationSent 事件前加上点 (.) 以监听它

看这里https://laravel.com/docs/6.x/broadcasting#broadcast-name

发生这种情况是因为 6001 端口在实时服务器上的 nginx 中保留(底部有解释)。我需要在 nginx 上使用反向代理才能使其工作 - 并在实时服务器中为 websockets 使用端口 6002。

nginx (根据要求,我添加了完整的nginx代码):

server {

  #The nginx domain configurations
  root /var/www/laravel/public;
  index index.html index.htm index.php index.nginx-debian.html;
  server_name example.com www.example.com;

  #WHAT YOU NEED IS FROM HERE...
  location / {
      try_files $uri $uri/ /index.php?$query_string;

      # "But why port 6000, 6002 and 433? Scroll at the bottom"

      proxy_pass                          http://127.0.0.1:6001;
      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  https;
      proxy_set_header X-VerifiedViaNginx yes;
      proxy_read_timeout                  60;
      proxy_connect_timeout               60;
      proxy_redirect                      off;

      # Specific for websockets: force the use of HTTP/1.1 and set the Upgrade header
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection 'upgrade';
      proxy_set_header Host $host;
      proxy_cache_bypass $http_upgrade;
 }
 #..UNTIL HERE - The rest are classic nginx config and certbot

 #The default Laravel nginx config
 location ~ \.php$ {
      try_files $uri =404;
      fastcgi_split_path_info ^(.+\.php)(/.+)$;
      fastcgi_pass unix:/run/php/php7.2-fpm.sock;
      fastcgi_index index.php;
      fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
      include fastcgi_params;
 }

 #SSL by certbot
 listen [::]:443 ssl ipv6only=on; # managed by Certbot
 listen 443 ssl; # managed by Certbot

 ssl                         on;
 ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
 ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
 include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
 ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

 ssl_session_cache           shared:SSL:30m;
 ssl_protocols               TLSv1.1 TLSv1.2;

 # Diffie Hellmann performance improvements
 ssl_ecdh_curve              secp384r1;
}

通过 TLS 连接到您的域的所有内容都将以纯文本形式代理到端口 6001 上的本地服务。这会将所有 TLS(和证书管理)卸载到 Nginx,使您的 websocket 服务器配置尽可能干净和简单。

这也使得通过 Let's Encrypt 实现自动化变得更加容易,因为已经有一些实现可以管理 Nginx 中的证书配置并在需要时重新加载它们。 - Source - Mattias Geniar

回声设置:

let isProduction = process.env.MIX_WS_CONNECT_PRODUCTION === 'true';

Vue.prototype.Echo = new LaravelEcho({
    broadcaster: 'pusher',
    key: process.env.MIX_PUSHER_APP_KEY,
    wssHost: window.location.hostname,
    wssPort: isProduction ? 6002 : 6001,
    wsHost: window.location.hostname,
    wsPort: isProduction ? 6002 : 6001,
    disableStats: false,
    encrypted: isProduction,
    enabledTransports: ['ws', 'wss'],
    disabledTransports: ['sockjs', 'xhr_polling', 'xhr_streaming']
});

websockets.php

'apps' => [
    [
        'id' => env('MIX_PUSHER_APP_ID'),
        'name' => env('APP_NAME'),
        'key' => env('MIX_PUSHER_APP_KEY'),
        'secret' => env('MIX_PUSHER_APP_SECRET'),
        'enable_client_messages' => false,
        'enable_statistics' => true,
    ],
],

// I kept them null but I use LetsEncrypt for SSL certs too.
'ssl' => [
   'local_cert' => null,
   'local_pk' => null,
   'passphrase' => null,
]

broadcasting.php

'pusher' => [
     'driver' => 'pusher',
     'key' => env('MIX_PUSHER_APP_KEY'),
     'secret' => env('MIX_PUSHER_APP_SECRET'),
     'app_id' => env('MIX_PUSHER_APP_ID'),
     'options' => [
         'cluster' => env('MIX_PUSHER_APP_CLUSTER'),
         'encrypted' => env('MIX_WS_CONNECT_PRODUCTION'),
         'host' => '127.0.0.1',
         'port' => env('MIX_WS_CONNECT_PRODUCTION') ? 6002 : 6001,
         'scheme' => 'http'
     ],
 ],

这是我的完整周期,使它发挥作用。希望对你有帮助。


Quoting from Alex Bouma's explanation:

"But why port 6000, 6002 and 433, what a mess!?"

I hear ya! Let me explain a bit, it will hopefully all make sense afterwards.

Here is the thing, opening an port on your server can only be done by only one application at a time (technically that is not true, but let's keep it simple here). So if we would let NGINX listen on port 6001 we cannot start our websockets server also on port 6001 since it will conflict with NGINX and the other way around, therefore we let NGINX listen on port 6002 and let it proxy (NGINX is a reverse proxy after all) all that traffic to port 6001 (the websockets server) over plain http. Stripping away the SSL so the websockets server has no need to know how to handle SSL.

So NGINX will handle all the SSL magic and forward the traffic in plain http to port 6001 on your server where the websockets server is listening for requests.

The reason we are not configuring any SSL in the websockets.php config and we define the scheme in our broadcasting.php as http and use port 6001 is to bypass NGINX and directly communicate with the websockets server locally without needing SSL which faster (and easier to configure and maintain).

我发现了问题。

我的 web.php:

Route::post('/broadcasting/auth', function (Illuminate\Http\Request $req) {
if ($req->channel_name == 'users') {
    return Broadcast::auth($req);
}
});

我不完全记得我添加这个的原因和时间,但它可能来自 here。由于某种原因,这没有产生任何错误。无论如何,我摆脱了这个,现在它工作得很好。