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';
}
}
推荐文章: