laravel使用websocket做简易聊天室监听不到信息的问题

原本想做个简易聊天室玩玩,但其他地方都检查完了也没发现什么问题,就是监听总收不到消息
执行 laravel-echo-server start
发送信息后命令行显示
Channel: laravel_database_chat
Event: message.posted
不知道是不是监听频道有问题,要做项目了,大家帮忙看一下到底是哪里的问题,谢谢大家

运行环境

本地宝塔LNMP 前端使用vue 用的是laravel-echo用redis驱动

前端main.js

import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import 'element-plus/theme-chalk/el-icon.css';
import axios from 'axios';
import Echo from 'laravel-echo';

window.io = require('socket.io-client');
// 假设你的 Laravel Echo Server 运行在同一个域名下的 6001 端口
window.Echo = new Echo({
    broadcaster: 'socket.io',
    host:  'example.top:6001'
});


// 配置 axios 请求拦截器
axios.interceptors.request.use(config => {
    // 尝试从 localStorage 获取 token
    const token = localStorage.getItem('access_token');
    if (token) {
        // 如果 token 存在,则在所有请求的头部添加 Authorization
        config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
}, error => {
    // 对请求错误做些什么
    return Promise.reject(error);
});

const app = createApp(App)
app.use(router)
app.use(ElementPlus)
app.config.compilerOptions.isCustomElement = tag => tag === 'emoji-picker';
app.mount('#app')

前端聊天室WeTalk.vue(去除样式)部分代码

<template>
  <el-container>
    <el-header>聊天室</el-header>
    <el-main ref="chatArea">
      <!-- 修改这部分以显示 "昵称:信息" -->
      <div class="message" v-for="(msg, index) in messages" :key="index">
        <el-card :class="{'self-message': msg.self}">
          <div><strong>{{ msg.nickname }}</strong>{{ msg.content }}</div>
          <div class="message-meta">{{ msg.time }}</div>
        </el-card>
      </div>
    </el-main>
    <el-footer>
      <div @click.stop="toggleEmojiPicker" class="emoji-button">😀</div>
      <el-popover v-model:visible="showEmojiPicker" trigger="click" placement="top-start" @show="handlePopoverShow">
        <emoji-picker @emoji-click="addEmoji" />
      </el-popover>
      <el-input v-model="newMessage" class="input-with-select chat-container chat-area message-input" type="textarea" placeholder="输入新消息" @keyup.enter="send" ref="messageInput" :rows="5"></el-input>
      <el-button type="primary" @click="send">发送</el-button>
    </el-footer>
  </el-container>
</template>

<script>
import { ref, nextTick, onMounted, onUnmounted } from 'vue';
import { useRoute } from 'vue-router';
import 'emoji-picker-element';
import axios from 'axios';

export default {
  setup() {
    // 一个简单的 UUID 生成函数
    function generateUUID() {
      return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        const r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
      });
    }

    const userId = ref('');
    const route = useRoute();
    const nickname = route.query.nickname;
    const newMessage = ref('');
    const showEmojiPicker = ref(false);
    const messages = ref([{ content: `欢迎 ${nickname} 进入聊天室!`, self: false, time: '10:00 AM' }]);
    const chatArea = ref(null);
    const messageInput = ref(null);

    // 初始化 Echo 监听
    onMounted(() => {
      let storedUserId = localStorage.getItem('userId');
      if (!storedUserId) {
        storedUserId = generateUUID();
        localStorage.setItem('userId', storedUserId);
      }
      userId.value = storedUserId; // 此处修正

      window.Echo.channel('chat')
          .listen('.message.posted', (e) => {
            alert(1);
            console.log("Received message:", e); // 添加这行来查看接收到的数
            messages.value.push({
              nickname: e.nickname,
              content: e.message,
              self: e.userId === userId.value, // 这里假设 userId 已经正确设置
              time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
            });
            nextTick(() => {
              chatArea.value.scrollTop = chatArea.value.scrollHeight;
            });
          });
    });



    // 移除监听器
    onUnmounted(() => {
      window.Echo.leave('chat');
    });

    const toggleEmojiPicker = () => {
      showEmojiPicker.value = !showEmojiPicker.value;
    };

    const handlePopoverShow = () => {
      document.addEventListener('click', closeEmojiPicker);
    };

    const closeEmojiPicker = () => {
      showEmojiPicker.value = false;
      document.removeEventListener('click', closeEmojiPicker);
    };

    const addEmoji = (event) => {
      newMessage.value += event.detail.unicode;
      nextTick(() => {
        messageInput.value.focus();
      });
    };

    const send = async () => {
      if (!newMessage.value.trim()) return;

      const payload = {
        userId: userId.value, // 用户的唯一标识符
        nickname: nickname, // 用户的昵称
        message: newMessage.value, // 消息内容
      };

      try {
        // 发送消息到你的后端
        await axios.post(`${process.env.VUE_APP_API_BASE_URL}/api/v1/message`, payload);

        // 消息发送成功后,清空输入框
        newMessage.value = '';

        // 并将消息追加到本地消息列表中
        messages.value.push({
          ...payload,
          self: true, // 标记为自己的消息
          time: new Date().toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'}), // 生成消息时间
        });

        // 确保聊天框滚动到最新的消息
        await nextTick();
        chatArea.value.scrollTop = chatArea.value.scrollHeight;
      } catch (error) {
        console.error('Failed to send message:', error);
        // 在这里处理错误情况,如显示错误消息给用户
      }
    }

    return {
      userId,
      newMessage,
      showEmojiPicker,
      messages,
      toggleEmojiPicker,
      handlePopoverShow,
      addEmoji,
      send,
      chatArea,
      messageInput
    };
  },
};
</script>

后端laravel-echo-server.json配置文件

{
    "authHost": "http://localhost",
    "authEndpoint": "/broadcasting/auth",
    "clients": [],
    "database": "redis",
    "databaseConfig": {
        "redis": {},
        "sqlite": {
            "databasePath": "/database/laravel-echo-server.sqlite"
        }
    },
    "devMode": true,
    "host": null,
    "port": "6001",
    "protocol": "http",
    "socketio": {},
    "secureOptions": 67108864,
    "sslCertPath": "",
    "sslKeyPath": "",
    "sslCertChainPath": "",
    "sslPassphrase": "",
    "subscribers": {
        "http": true,
        "redis": true
    },
    "apiOriginAllow": {
        "allowCors": true,
        "allowOrigin": "*",
        "allowMethods": "*",
        "allowHeaders": "*"
    }
}

后端config/broadcasting.php文件

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Default Broadcaster
    |--------------------------------------------------------------------------
    |
    | This option controls the default broadcaster that will be used by the
    | framework when an event needs to be broadcast. You may set this to
    | any of the connections defined in the "connections" array below.
    |
    | Supported: "pusher", "ably", "redis", "log", "null"
    |
    */

    'default' => env('BROADCAST_DRIVER', 'null'),

    /*
    |--------------------------------------------------------------------------
    | Broadcast Connections
    |--------------------------------------------------------------------------
    |
    | Here you may define all of the broadcast connections that will be used
    | to broadcast events to other systems or over websockets. Samples of
    | each available type of connection are provided inside this array.
    |
    */

    'connections' => [

        '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'),
                'useTLS' => true,
            ],
        ],

        'ably' => [
            'driver' => 'ably',
            'key' => env('ABLY_KEY'),
        ],

        'redis' => [
            'driver' => 'redis',
            'connection' => 'default',
        ],

        'log' => [
            'driver' => 'log',
        ],

        'null' => [
            'driver' => 'null',
        ],

    ],

];

后端发送信息控制器

<?php

namespace App\Http\Controllers\Api;

use Illuminate\Http\Request;
use App\Events\MessagePosted;

class ChatController extends Controller
{
    public function sendMessage(Request $request)
    {
        $userId = $request->userId;
        $nickname = $request->nickname;
        $message = $request->message;

        event(new MessagePosted($userId, $nickname, $message));

        return response()->json(['message' => 'Message sent successfully.']);
    }


}

后端广播

<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;


class MessagePosted implements ShouldBroadcast
{
    use InteractsWithSockets, SerializesModels;

    public $message;
    public $userId;
    public $nickname;

    public function __construct($userId, $nickname, $message)
    {
        $this->userId = $userId;
        $this->nickname = $nickname;
        $this->message = $message;
    }

    public function broadcastOn()
    {
        return new Channel('chat');
    }

    // 可选:定义广播数据,如果你想自定义广播的数据结构
    public function broadcastWith()
    {
        return [
            'message' => $this->message,
            'userId' => $this->userId,
            'nickname' => $this->nickname,
        ];
    }

    /**
     * @return string
     */
    public function broadcastAs()
    {
        return 'message.posted';
    }
}
理想的光照不到现实的黑暗,明灯是黑夜中的奢侈品。如果你接受不了真实生活千疮百孔的消极,那么,请移步儿童区...
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 2
sanders

建议看一下日志,比如控制台的 ws 消息 和 laravel-echo-server 控制台输出。

1个月前 评论
working (楼主) 1个月前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!