基于Vue+.NET全面实现WebSocket聊天室
作者:百锦再@新空间
这篇文章主要为大家详细介绍了如何基于Vue+.NET全面实现WebSocket聊天室,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
实现思路
后端(.NET):使用ASP.NET Core WebSocket实现实时通信
前端(Vue):使用原生WebSocket API与后端通信
功能:实时消息、用户列表、在线状态、消息历史
完整代码实现
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>.NET + Vue WebSocket聊天室</title> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } body { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: #333; min-height: 100vh; padding: 20px; display: flex; justify-content: center; align-items: center; } .chat-container { width: 90%; max-width: 1000px; background: rgba(255, 255, 255, 0.95); border-radius: 15px; overflow: hidden; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); display: flex; flex-direction: column; height: 90vh; } .chat-header { background: #4a6fc0; color: white; padding: 20px; text-align: center; position: relative; } .status-indicator { position: absolute; top: 20px; right: 20px; display: flex; align-items: center; font-size: 14px; } .status-dot { width: 10px; height: 10px; border-radius: 50%; margin-right: 8px; } .connected { background: #2ecc71; } .disconnected { background: #e74c3c; } .chat-main { display: flex; flex: 1; overflow: hidden; } .users-panel { width: 250px; background: #f5f7fa; border-right: 1px solid #e0e6ed; padding: 20px; overflow-y: auto; } .users-list { list-style: none; } .user-item { display: flex; align-items: center; padding: 10px; margin-bottom: 10px; border-radius: 8px; background: white; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05); } .user-avatar { width: 40px; height: 40px; border-radius: 50%; background: #4a6fc0; color: white; display: flex; align-items: center; justify-content: center; font-weight: bold; margin-right: 10px; } .chat-content { flex: 1; display: flex; flex-direction: column; } .messages-container { flex: 1; padding: 20px; overflow-y: auto; background: white; } .message { margin-bottom: 15px; max-width: 80%; display: flex; flex-direction: column; } .message-self { align-self: flex-end; align-items: flex-end; } .message-other { align-self: flex-start; align-items: flex-start; } .message-bubble { padding: 12px 15px; border-radius: 18px; margin-bottom: 5px; position: relative; word-break: break-word; } .message-self .message-bubble { background: #4a6fc0; color: white; border-bottom-right-radius: 4px; } .message-other .message-bubble { background: #f0f2f5; color: #333; border-bottom-left-radius: 4px; } .message-sender { font-size: 12px; color: #7f8c8d; margin-bottom: 3px; } .message-time { font-size: 11px; color: #95a5a6; } .input-area { padding: 20px; background: #f5f7fa; border-top: 1px solid #e0e6ed; display: flex; } .message-input { flex: 1; padding: 12px 15px; border: 1px solid #dce4ec; border-radius: 25px; outline: none; font-size: 16px; transition: border-color 0.3s; } .message-input:focus { border-color: #4a6fc0; } .send-button { margin-left: 10px; padding: 12px 25px; background: #4a6fc0; color: white; border: none; border-radius: 25px; cursor: pointer; font-weight: bold; transition: background 0.3s; } .send-button:hover { background: #3b5998; } .send-button:disabled { background: #acb5c1; cursor: not-allowed; } .login-panel { padding: 30px; text-align: center; background: white; } .username-input { padding: 12px 15px; border: 1px solid #dce4ec; border-radius: 25px; outline: none; font-size: 16px; width: 300px; margin-right: 10px; } .join-button { padding: 12px 25px; background: #4a6fc0; color: white; border: none; border-radius: 25px; cursor: pointer; font-weight: bold; } @media (max-width: 768px) { .chat-main { flex-direction: column; } .users-panel { width: 100%; border-right: none; border-bottom: 1px solid #e0e6ed; max-height: 150px; } .message { max-width: 90%; } } </style> </head> <body> <div id="app"> <div class="chat-container"> <div class="chat-header"> <h1>WebSocket 聊天室</h1> <div class="status-indicator"> <div class="status-dot" :class="isConnected ? 'connected' : 'disconnected'"></div> {{ isConnected ? '已连接' : '未连接' }} </div> </div> <div v-if="!username" class="login-panel"> <h2>请输入用户名加入聊天</h2> <div style="margin-top: 20px;"> <input v-model="inputUsername" @keyup.enter="joinChat" class="username-input" placeholder="用户名"> <button @click="joinChat" class="join-button">加入聊天</button> </div> </div> <div v-else class="chat-main"> <div class="users-panel"> <h3>在线用户 ({{ users.length }})</h3> <ul class="users-list"> <li v-for="user in users" :key="user" class="user-item"> <div class="user-avatar">{{ user.charAt(0).toUpperCase() }}</div> <div>{{ user }}</div> </li> </ul> </div> <div class="chat-content"> <div class="messages-container" ref="messagesContainer"> <div v-for="(message, index) in messages" :key="index" :class="['message', message.sender === username ? 'message-self' : 'message-other']"> <div v-if="message.sender !== username" class="message-sender">{{ message.sender }}</div> <div class="message-bubble">{{ message.text }}</div> <div class="message-time">{{ message.time }}</div> </div> </div> <div class="input-area"> <input v-model="inputMessage" @keyup.enter="sendMessage" class="message-input" placeholder="输入消息..." :disabled="!isConnected"> <button @click="sendMessage" class="send-button" :disabled="!isConnected">发送</button> </div> </div> </div> </div> </div> <script> new Vue({ el: '#app', data: { ws: null, isConnected: false, inputUsername: '', username: '', inputMessage: '', messages: [], users: [], reconnectAttempts: 0, maxReconnectAttempts: 5 }, mounted() { // 尝试从本地存储获取用户名 const savedUsername = localStorage.getItem('chat_username'); if (savedUsername) { this.inputUsername = savedUsername; } }, methods: { joinChat() { if (!this.inputUsername.trim()) return; this.username = this.inputUsername.trim(); localStorage.setItem('chat_username', this.username); this.connectWebSocket(); }, connectWebSocket() { // 在实际项目中,这里应该是你的后端WebSocket地址 // const wsUrl = 'wss://yourdomain.com/ws'; // 本地开发时可以使用以下URL const wsUrl = 'ws://localhost:5000/ws'; try { this.ws = new WebSocket(wsUrl); this.ws.onopen = () => { console.log('WebSocket连接已建立'); this.isConnected = true; this.reconnectAttempts = 0; // 发送加入聊天室的消息 this.sendWebSocketMessage({ type: 'join', username: this.username }); }; this.ws.onmessage = (event) => { try { const message = JSON.parse(event.data); this.handleWebSocketMessage(message); } catch (error) { console.error('消息解析错误:', error); } }; this.ws.onclose = () => { console.log('WebSocket连接已关闭'); this.isConnected = false; this.attemptReconnect(); }; this.ws.onerror = (error) => { console.error('WebSocket错误:', error); this.isConnected = false; }; } catch (error) { console.error('WebSocket连接失败:', error); this.attemptReconnect(); } }, attemptReconnect() { if (this.reconnectAttempts < this.maxReconnectAttempts) { this.reconnectAttempts++; const delay = Math.min(1000 * this.reconnectAttempts, 10000); console.log(`尝试重新连接 (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`); setTimeout(() => { this.connectWebSocket(); }, delay); } }, sendWebSocketMessage(message) { if (this.ws && this.ws.readyState === WebSocket.OPEN) { this.ws.send(JSON.stringify(message)); } }, handleWebSocketMessage(message) { switch (message.type) { case 'message': this.messages.push({ sender: message.sender, text: message.text, time: this.formatTime(new Date()) }); this.scrollToBottom(); break; case 'users': this.users = message.users; break; case 'system': this.messages.push({ sender: '系统', text: message.text, time: this.formatTime(new Date()) }); this.scrollToBottom(); break; } }, sendMessage() { if (!this.inputMessage.trim() || !this.isConnected) return; this.sendWebSocketMessage({ type: 'message', sender: this.username, text: this.inputMessage.trim() }); this.inputMessage = ''; }, scrollToBottom() { this.$nextTick(() => { const container = this.$refs.messagesContainer; if (container) { container.scrollTop = container.scrollHeight; } }); }, formatTime(date) { return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' }); } }, beforeDestroy() { if (this.ws) { this.ws.close(); } } }); </script> </body> </html>
.NET 后端实现步骤
1. 创建ASP.NET Core项目
dotnet new web -n ChatServer cd ChatServer
2. 添加WebSocket中间件 (Program.cs)
using System.Net.WebSockets; using System.Text; using System.Text.Json; var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.UseWebSockets(); // 存储所有连接的WebSocket和用户名 var connections = new Dictionary<WebSocket, string>(); app.Map("/ws", async context => { if (context.WebSockets.IsWebSocketRequest) { using var webSocket = await context.WebSockets.AcceptWebSocketAsync(); await HandleWebSocketConnection(webSocket, connections); } else { context.Response.StatusCode = 400; } }); async Task HandleWebSocketConnection(WebSocket webSocket, Dictionary<WebSocket, string> connections) { var buffer = new byte[1024 * 4]; try { // 接收第一条消息(用户加入) var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); while (!result.CloseStatus.HasValue) { var message = Encoding.UTF8.GetString(buffer, 0, result.Count); var jsonDoc = JsonDocument.Parse(message); var type = jsonDoc.RootElement.GetProperty("type").GetString(); if (type == "join") { var username = jsonDoc.RootElement.GetProperty("username").GetString(); connections[webSocket] = username; // 通知所有用户更新用户列表 await BroadcastUserList(connections); // 发送欢迎消息 var welcomeMsg = new { type = "system", text = $"{username} 加入了聊天室" }; await BroadcastMessage(JsonSerializer.Serialize(welcomeMsg), connections); } else if (type == "message") { var sender = jsonDoc.RootElement.GetProperty("sender").GetString(); var text = jsonDoc.RootElement.GetProperty("text").GetString(); var chatMsg = new { type = "message", sender = sender, text = text }; await BroadcastMessage(JsonSerializer.Serialize(chatMsg), connections); } result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); } await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); } catch (Exception ex) { Console.WriteLine($"WebSocket错误: {ex.Message}"); } finally { // 移除连接并通知其他用户 if (connections.TryGetValue(webSocket, out var username)) { connections.Remove(webSocket); // 通知用户离开 var leaveMsg = new { type = "system", text = $"{username} 离开了聊天室" }; await BroadcastMessage(JsonSerializer.Serialize(leaveMsg), connections); // 更新用户列表 await BroadcastUserList(connections); } } } async Task BroadcastUserList(Dictionary<WebSocket, string> connections) { var userListMsg = new { type = "users", users = connections.Values.ToArray() }; var json = JsonSerializer.Serialize(userListMsg); await BroadcastMessage(json, connections); } async Task BroadcastMessage(string message, Dictionary<WebSocket, string> connections) { var bytes = Encoding.UTF8.GetBytes(message); var data = new ArraySegment<byte>(bytes); foreach (var socket in connections.Keys) { if (socket.State == WebSocketState.Open) { await socket.SendAsync(data, WebSocketMessageType.Text, true, CancellationToken.None); } } } app.Run();
3. 启用静态文件服务 (添加以下代码到Program.cs)
// 在var app = builder.Build();后添加 app.UseDefaultFiles(); app.UseStaticFiles();
4. 运行应用
dotnet run
实现原理
WebSocket连接建立:
- 客户端通过WebSocket API连接到服务器
- 服务器接受连接并保持持久连接
消息协议:
- 使用JSON格式进行消息交换
- 消息类型包括:join(加入)、message(消息)、users(用户列表)、system(系统消息)
广播机制:
- 服务器维护所有活跃的WebSocket连接
- 当收到消息时,服务器将消息广播给所有连接的客户端
用户管理:
- 使用字典存储WebSocket连接与用户的映射关系
- 当用户加入或离开时更新用户列表并广播
部署说明
- 将Vue前端代码放入wwwroot文件夹
- 配置WebSocket中间件和路由
- 部署到支持WebSocket的服务器(如IIS、Kestrel、Nginx等)
- 生产环境应使用WSS(WebSocket Secure)协议
这个实现提供了一个完整的实时聊天室,包括用户加入/离开通知、实时消息传递和用户列表更新等功能。
到此这篇关于基于Vue+.NET全面实现WebSocket聊天室的文章就介绍到这了,更多相关Vue .NET实现WebSocket聊天室内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!