基于ASP.NET Core SignalR實(shí)現(xiàn)實(shí)時(shí)消息提醒與聊天功能
基于ASP.NET Core SignalR實(shí)現(xiàn)實(shí)時(shí)消息提醒與聊天功能,包含服務(wù)端架構(gòu)、客戶端交互及關(guān)鍵優(yōu)化點(diǎn):
一、服務(wù)端實(shí)現(xiàn)(ASP.NET Core 7+)
1. 消息模型定義
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
}
2. SignalR Hub核心實(shí)現(xiàn)
using Microsoft.AspNetCore.SignalR;
using System.Collections.Concurrent;
public class ChatHub : Hub
{
private static readonly ConcurrentDictionary<string, List<string>> _connections =
new ConcurrentDictionary<string, List<string>>();
// 用戶連接管理
public override async Task OnConnectedAsync()
{
var userId = Context.UserIdentifier;
if (!_connections.TryGetValue(userId, out var connections))
{
connections = new List<string>();
_connections[userId] = connections;
}
connections.Add(Context.ConnectionId);
// 通知好友在線狀態(tài)
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);
}
// 發(fā)送消息
public async Task SendMessage(string receiverId, string content)
{
var senderId = Context.UserIdentifier;
var message = new ChatMessage
{
SenderId = senderId,
ReceiverId = receiverId,
Content = content
};
// 存儲(chǔ)消息(可選)
await SaveMessageToDb(message);
// 推送消息
await Clients.Group($"user_{receiverId}").SendAsync(
"ReceiveMessage",
senderId,
content,
message.Timestamp
);
// 標(biāo)記已送達(dá)
await Clients.Caller.SendAsync("MessageDelivered", message.Id);
}
// 標(biāo)記已讀
public async Task MarkAsRead(int messageId)
{
// 更新數(shù)據(jù)庫(kù)狀態(tài)
var message = await GetMessageById(messageId);
if (message?.ReceiverId == Context.UserIdentifier)
{
message.Status = MessageStatus.Read;
await SaveMessageToDb(message);
// 通知發(fā)送者
await Clients.Group($"user_{message.SenderId}").SendAsync("MessageRead", messageId);
}
}
private static void UpdateUserStatus(string userId, bool isOnline)
{
// 更新全局狀態(tài)緩存
// 觸發(fā)好友狀態(tài)變更通知
Clients.All.SendAsync("UserStatusChanged", userId, isOnline);
}
}
二、客戶端實(shí)現(xiàn)(Web前端)
1. HTML結(jié)構(gòu)
<div id="chat-container">
<div id="online-users"></div>
<div id="message-list"></div>
<input type="text" id="receiverId" placeholder="接收者ID">
<input type="text" id="message-input">
<button id="send-btn">發(fā)送</button>
</div>
2. JavaScript連接與事件處理
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect()
.build();
// 接收消息
connection.on("ReceiveMessage", (senderId, content, timestamp) => {
const msgElement = document.createElement("div");
msgElement.innerHTML = `
<strong>${senderId}</strong> (${new Date(timestamp).toLocaleTimeString()}):
<span>${content}</span>
`;
document.getElementById("message-list").appendChild(msgElement);
});
// 用戶狀態(tài)更新
connection.on("UserStatusChanged", (userId, isOnline) => {
const statusElement = document.getElementById(`user-${userId}-status`);
statusElement.textContent = isOnline ? "在線" : "離線";
});
// 連接建立
connection.start().then(() => {
console.log("已連接到SignalR服務(wù)");
}).catch(err => console.error(err.toString()));
// 發(fā)送消息
document.getElementById("send-btn").addEventListener("click", async () => {
const receiverId = document.getElementById("receiverId").value;
const content = document.getElementById("message-input").value;
await connection.invoke("SendMessage", receiverId, content);
});
三、關(guān)鍵功能擴(kuò)展
1. 消息持久化(Entity Framework Core)
public class AppDbContext : DbContext
{
public DbSet<ChatMessage> Messages { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ChatMessage>()
.HasIndex(m => new { m.SenderId, m.ReceiverId })
.IsUnique(false);
}
}
// 保存消息到數(shù)據(jù)庫(kù)
public async Task SaveMessageToDb(ChatMessage message)
{
using var dbContext = new AppDbContext();
dbContext.Messages.Add(message);
await dbContext.SaveChangesAsync();
}
2. 實(shí)時(shí)通知增強(qiáng)
// 彈窗通知(使用iziToast)
connection.on("ReceiveMessage", async (senderId, content, timestamp) => {
iziToast.info({
title: senderId,
message: content,
position: 'topRight',
timeout: 5000
});
});
3. 消息狀態(tài)同步
// 前端標(biāo)記已讀
async function markMessageAsRead(messageId) {
await connection.invoke("MarkAsRead", messageId);
}
// 接收已讀確認(rèn)
connection.on("MessageRead", (messageId) => {
document.querySelector(`[data-id="${messageId}"]`).classList.add("read");
});
四、服務(wù)端配置
1. Program.cs配置
var builder = WebApplication.CreateBuilder(args);
// 添加SignalR服務(wù)
builder.Services.AddSignalR(hubOptions => {
hubOptions.EnableDetailedErrors = true;
hubOptions.KeepAliveInterval = TimeSpan.FromMinutes(2);
});
// 添加身份認(rèn)證(JWT示例)
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => {
options.TokenValidationParameters = new TokenValidationParameters {
ValidateIssuer = true,
ValidateAudience = true
};
});
var app = builder.Build();
// 路由配置
app.UseAuthentication();
app.UseAuthorization();
app.MapHub<ChatHub>("/chathub");
app.Run();
參考代碼 使用SignalR實(shí)現(xiàn)消息提醒(聊天源碼) www.youwenfan.com/contentcso/92650.html
五、性能優(yōu)化方案
連接管理 使用
ConcurrentDictionary維護(hù)用戶連接池 實(shí)現(xiàn)連接心跳檢測(cè)機(jī)制消息分片
// 大文件分片傳輸 public async Task SendLargeFile(IFormFile file) { var buffer = new byte[4096]; using (var stream = file.OpenReadStream()) { int bytesRead; while ((bytesRead = await stream.ReadAsync(buffer)) > 0) { await Clients.Caller.SendAsync("ReceiveFileChunk", file.Name, buffer, bytesRead); } } }負(fù)載均衡
- 配置Redis作為SignalR后端存儲(chǔ)
builder.Services.AddSignalR().AddStackExchangeRedis("redis_connection_string");
到此這篇關(guān)于基于ASP.NET Core SignalR實(shí)現(xiàn)實(shí)時(shí)消息提醒與聊天功能的文章就介紹到這了,更多相關(guān)ASP.NET Core SignalR實(shí)時(shí)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Asp.net中的數(shù)據(jù)綁定Eval和Bind應(yīng)用示例
這篇文章主要介紹了Asp.net中的數(shù)據(jù)綁定Eval和Bind的應(yīng)用,需要的朋友可以參考下2014-05-05
log4net教程日志分類和自動(dòng)維護(hù)示例
log4net能不能按照功能分類呢?如果通過(guò)配置不同的logger,然后功能根據(jù)不同的LoggerName加載Ilog實(shí)例,是可以做到。但由于這些功能的log配置差異性極小,也許僅僅就是文件名不同。于是想通過(guò)代碼進(jìn)行配置,下面把方法分享如下2014-01-01
ASP.NET中實(shí)現(xiàn)把Json數(shù)據(jù)轉(zhuǎn)換為ADO.NET DataSet對(duì)象
這篇文章主要介紹了ASP.NET中實(shí)現(xiàn)把Json數(shù)據(jù)轉(zhuǎn)換為ADO.NET DataSet對(duì)象,本文講解設(shè)計(jì)及實(shí)現(xiàn)方法,相關(guān)代碼托管到GITHUB,需要的朋友可以參考下2015-03-03
.Net?Core實(shí)現(xiàn)第三方QQ掃碼登錄
這篇文章介紹了.Net?Core實(shí)現(xiàn)第三方QQ掃碼登錄的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-02-02
.Net WebApi消息攔截器之MessageHandler的示例
這篇文章主要介紹了.Net WebApi消息攔截器之MessageHandler的示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-08-08
ASP.NET Core對(duì)Controller進(jìn)行單元測(cè)試的完整步驟
這篇文章主要給大家介紹了關(guān)于ASP.NET Core對(duì)Controller進(jìn)行單元測(cè)試的完整步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用ASP.NET Core具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
關(guān)于.NET Attribute在數(shù)據(jù)校驗(yàn)中的應(yīng)用教程
這篇文章主要給大家介紹了關(guān)于.NET Attribute在數(shù)據(jù)校驗(yàn)中的應(yīng)用的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用.NET具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05

