栋林 發表於 2026-1-5 10:53:34

基于ASP.NET Core SignalR实现实时消息提醒与聊天功能

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">一、服务端实现(ASP.NET Core 7+)</a></li><ul class="second_class_ul"><li><a href="#_lab2_0_0">1. 消息模型定义</a></li><li><a href="#_lab2_0_1">2. SignalR Hub核心实现</a></li></ul><li><a href="#_label1">二、客户端实现(Web前端)</a></li><ul class="second_class_ul"><li><a href="#_lab2_1_2">1. HTML结构</a></li><li><a href="#_lab2_1_3">2. JavaScript连接与事件处理</a></li></ul><li><a href="#_label2">三、关键功能扩展</a></li><ul class="second_class_ul"><li><a href="#_lab2_2_4">1. 消息持久化(Entity Framework Core)</a></li><li><a href="#_lab2_2_5">2. 实时通知增强</a></li><li><a href="#_lab2_2_6">3. 消息状态同步</a></li></ul><li><a href="#_label3">四、服务端配置</a></li><ul class="second_class_ul"><li><a href="#_lab2_3_7">1. Program.cs配置</a></li></ul><li><a href="#_label4">五、性能优化方案</a></li><ul class="second_class_ul"></ul></ul></div><p>基于ASP.NET Core SignalR实现实时消息提醒与聊天功能,包含服务端架构、客户端交互及关键优化点:</p>
<p class="maodian"><a name="_label0"></a></p><h2>一、服务端实现(ASP.NET Core 7+)</h2>
<p class="maodian"><a name="_lab2_0_0"></a></p><h3>1. 消息模型定义</h3>
<div class="jb51code"><pre class="brush:csharp;">public class ChatMessage
{
    public string SenderId { get; set; }
    public string ReceiverId { get; set; }
    public string Content { get; set; }
    public DateTime Timestamp { get; set; } = DateTime.UtcNow;
    public MessageStatus Status { get; set; } = MessageStatus.Sent;
}

public enum MessageStatus
{
    Sent,
    Delivered,
    Read
}
</pre></div>
<p class="maodian"><a name="_lab2_0_1"></a></p><h3>2. SignalR Hub核心实现</h3>
<div class="jb51code"><pre class="brush:csharp;">using Microsoft.AspNetCore.SignalR;
using System.Collections.Concurrent;

public class ChatHub : Hub
{
    private static readonly ConcurrentDictionary&lt;string, List&lt;string&gt;&gt; _connections =
      new ConcurrentDictionary&lt;string, List&lt;string&gt;&gt;();

    // 用户连接管理
    public override async Task OnConnectedAsync()
    {
      var userId = Context.UserIdentifier;
      if (!_connections.TryGetValue(userId, out var connections))
      {
            connections = new List&lt;string&gt;();
            _connections = connections;
      }
      connections.Add(Context.ConnectionId);
      
      // 通知好友在线状态
      await UpdateUserStatus(userId, true);
      await base.OnConnectedAsync();
    }

    public override async Task OnDisconnectedAsync(Exception? exception)
    {
      if (_connections.TryRemove(Context.UserIdentifier, out var connections))
      {
            await UpdateUserStatus(Context.UserIdentifier, false);
            connections.Remove(Context.ConnectionId);
      }
      await base.OnDisconnectedAsync(exception);
    }

    // 发送消息
    public async Task SendMessage(string receiverId, string content)
    {
      var senderId = Context.UserIdentifier;
      var message = new ChatMessage
      {
            SenderId = senderId,
            ReceiverId = receiverId,
            Content = content
      };

      // 存储消息(可选)
      await SaveMessageToDb(message);

      // 推送消息
      await Clients.Group($"user_{receiverId}").SendAsync(
            "ReceiveMessage",
            senderId,
            content,
            message.Timestamp
      );

      // 标记已送达
      await Clients.Caller.SendAsync("MessageDelivered", message.Id);
    }

    // 标记已读
    public async Task MarkAsRead(int messageId)
    {
      // 更新数据库状态
      var message = await GetMessageById(messageId);
      if (message?.ReceiverId == Context.UserIdentifier)
      {
            message.Status = MessageStatus.Read;
            await SaveMessageToDb(message);

            // 通知发送者
            await Clients.Group($"user_{message.SenderId}").SendAsync("MessageRead", messageId);
      }
    }

    private static void UpdateUserStatus(string userId, bool isOnline)
    {
      // 更新全局状态缓存
      // 触发好友状态变更通知
      Clients.All.SendAsync("UserStatusChanged", userId, isOnline);
    }
}
</pre></div>
<p class="maodian"><a name="_label1"></a></p><h2>二、客户端实现(Web前端)</h2>
<p class="maodian"><a name="_lab2_1_2"></a></p><h3>1. HTML结构</h3>
<div class="jb51code"><pre class="brush:xhtml;">&lt;div id="chat-container"&gt;
    &lt;div id="online-users"&gt;&lt;/div&gt;
    &lt;div id="message-list"&gt;&lt;/div&gt;
    &lt;input type="text" id="receiverId" placeholder="接收者ID"&gt;
    &lt;input type="text" id="message-input"&gt;
    &lt;button id="send-btn"&gt;发送&lt;/button&gt;
&lt;/div&gt;
</pre></div>
<p class="maodian"><a name="_lab2_1_3"></a></p><h3>2. JavaScript连接与事件处理</h3>
<div class="jb51code"><pre class="brush:js;">const connection = new signalR.HubConnectionBuilder()
    .withUrl("/chathub")
    .withAutomaticReconnect()
    .build();

// 接收消息
connection.on("ReceiveMessage", (senderId, content, timestamp) =&gt; {
    const msgElement = document.createElement("div");
    msgElement.innerHTML = `
      &lt;strong&gt;${senderId}&lt;/strong&gt; (${new Date(timestamp).toLocaleTimeString()}):
      &lt;span&gt;${content}&lt;/span&gt;
    `;
    document.getElementById("message-list").appendChild(msgElement);
});

// 用户状态更新
connection.on("UserStatusChanged", (userId, isOnline) =&gt; {
    const statusElement = document.getElementById(`user-${userId}-status`);
    statusElement.textContent = isOnline ? "在线" : "离线";
});

// 连接建立
connection.start().then(() =&gt; {
    console.log("已连接到SignalR服务");
}).catch(err =&gt; console.error(err.toString()));

// 发送消息
document.getElementById("send-btn").addEventListener("click", async () =&gt; {
    const receiverId = document.getElementById("receiverId").value;
    const content = document.getElementById("message-input").value;
    await connection.invoke("SendMessage", receiverId, content);
});
</pre></div>
<p class="maodian"><a name="_label2"></a></p><h2>三、关键功能扩展</h2>
<p class="maodian"><a name="_lab2_2_4"></a></p><h3>1. 消息持久化(Entity Framework Core)</h3>
<div class="jb51code"><pre class="brush:csharp;">public class AppDbContext : DbContext
{
    public DbSet&lt;ChatMessage&gt; Messages { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
      modelBuilder.Entity&lt;ChatMessage&gt;()
            .HasIndex(m =&gt; new { m.SenderId, m.ReceiverId })
            .IsUnique(false);
    }
}

// 保存消息到数据库
public async Task SaveMessageToDb(ChatMessage message)
{
    using var dbContext = new AppDbContext();
    dbContext.Messages.Add(message);
    await dbContext.SaveChangesAsync();
}
</pre></div>
<p class="maodian"><a name="_lab2_2_5"></a></p><h3>2. 实时通知增强</h3>
<div class="jb51code"><pre class="brush:csharp;">// 弹窗通知(使用iziToast)
connection.on("ReceiveMessage", async (senderId, content, timestamp) =&gt; {
    iziToast.info({
      title: senderId,
      message: content,
      position: 'topRight',
      timeout: 5000
    });
});
</pre></div>
<p class="maodian"><a name="_lab2_2_6"></a></p><h3>3. 消息状态同步</h3>
<div class="jb51code"><pre class="brush:csharp;">// 前端标记已读
async function markMessageAsRead(messageId) {
    await connection.invoke("MarkAsRead", messageId);
}

// 接收已读确认
connection.on("MessageRead", (messageId) =&gt; {
    document.querySelector(``).classList.add("read");
});
</pre></div>
<p class="maodian"><a name="_label3"></a></p><h2>四、服务端配置</h2>
<p class="maodian"><a name="_lab2_3_7"></a></p><h3>1. Program.cs配置</h3>
<div class="jb51code"><pre class="brush:csharp;">var builder = WebApplication.CreateBuilder(args);

// 添加SignalR服务
builder.Services.AddSignalR(hubOptions =&gt; {
    hubOptions.EnableDetailedErrors = true;
    hubOptions.KeepAliveInterval = TimeSpan.FromMinutes(2);
});

// 添加身份认证(JWT示例)
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =&gt; {
      options.TokenValidationParameters = new TokenValidationParameters {
            ValidateIssuer = true,
            ValidateAudience = true
      };
    });

var app = builder.Build();

// 路由配置
app.UseAuthentication();
app.UseAuthorization();
app.MapHub&lt;ChatHub&gt;("/chathub");
app.Run();
</pre></div>
<p>参考代码 使用SignalR实现消息提醒(聊天源码) www.youwenfan.com/contentcso/92650.html</p>
<p class="maodian"><a name="_label4"></a></p><h2>五、性能优化方案</h2>
<ol><li><p><strong>连接管理</strong> 使用<code>ConcurrentDictionary</code>维护用户连接池 实现连接心跳检测机制</p></li><li><p><strong>消息分片</strong></p>
<div class="jb51code"><pre class="brush:csharp;">// 大文件分片传输
public async Task SendLargeFile(IFormFile file)
{
    var buffer = new byte;
    using (var stream = file.OpenReadStream())
    {
      int bytesRead;
      while ((bytesRead = await stream.ReadAsync(buffer)) &gt; 0)
      {
            await Clients.Caller.SendAsync("ReceiveFileChunk",
                file.Name, buffer, bytesRead);
      }
    }
}
</pre></div></li><li><p><strong>负载均衡</strong></p>
<ul><li>配置Redis作为SignalR后端存储</li></ul>
<div class="jb51code"><pre class="brush:csharp;">builder.Services.AddSignalR().AddStackExchangeRedis("redis_connection_string");
</pre></div></li></ol>
頁: [1]
查看完整版本: 基于ASP.NET Core SignalR实现实时消息提醒与聊天功能