> 技术文档 > .NET + Vue 基于 WebSocket 的聊天室全面实现

.NET + Vue 基于 WebSocket 的聊天室全面实现

.NET + Vue 基于 WebSocket 的聊天室全面实现

文章目录

    • 实现思路
    • 完整代码实现
    • .NET 后端实现步骤
      • 1. 创建ASP.NET Core项目
      • 2. 添加WebSocket中间件 (Program.cs)
      • 3. 启用静态文件服务 (添加以下代码到Program.cs)
      • 4. 运行应用
    • 实现原理
    • 部署说明

.NET + Vue 基于 WebSocket 的聊天室全面实现

实现思路

  1. 后端(.NET):使用ASP.NET Core WebSocket实现实时通信
  2. 前端(Vue):使用原生WebSocket API与后端通信
  3. 功能:实时消息用户列表、在线状态、消息历史

完整代码实现

<!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 ChatServercd 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

实现原理

.NET + Vue 基于 WebSocket 的聊天室全面实现

  1. WebSocket连接建立

    • 客户端通过WebSocket API连接到服务器
    • 服务器接受连接并保持持久连接
  2. 消息协议

    • 使用JSON格式进行消息交换
    • 消息类型包括:join(加入)、message(消息)、users(用户列表)、system(系统消息)
  3. 广播机制

    • 服务器维护所有活跃的WebSocket连接
    • 当收到消息时,服务器将消息广播给所有连接的客户端
  4. 用户管理

    • 使用字典存储WebSocket连接与用户的映射关系
    • 当用户加入或离开时更新用户列表并广播

部署说明

  1. 将Vue前端代码放入wwwroot文件夹
  2. 配置WebSocket中间件和路由
  3. 部署到支持WebSocket的服务器(如IIS、Kestrel、Nginx等)
  4. 生产环境应使用WSS(WebSocket Secure)协议

这个实现提供了一个完整的实时聊天室,包括用户加入/离开通知、实时消息传递和用户列表更新等功能。