c#实现简单的对话通讯
原创
©著作权归作者所有:来自51CTO博客作者星星猫的原创作品,请联系作者获取转载授权,否则将追究法律责任
一、核心功能设计
- 消息模型:定义消息结构(发送者、接收者、内容、时间、状态等)
- 通讯服务:负责消息的发送、接收、存储
- 实时通知:使用 WebSocket 或 SignalR 实现实时推送
- 消息存储:基于数据库保存消息记录
二、技术选型
- 实时通讯:ASP.NET Core SignalR(简化实时通讯开发)
- 数据库:SQL Server(存储消息和用户关系)
- 后端框架:ASP.NET Core(提供 API 和 SignalR 服务)
- 前端:可搭配 Blazor 或 JavaScript 调用 SignalR 客户端
三、代码实现
1. 消息模型定义
// 消息实体
public class Message
{
public int Id { get; set; }
public string SenderId { get; set; } // 发送者ID
public string ReceiverId { get; set; } // 接收者ID
public string Content { get; set; } // 消息内容
public DateTime SendTime { get; set; } = DateTime.Now;
public MessageStatus Status { get; set; } = MessageStatus.Sent; // 状态:发送/已读/未读
}
// 消息状态枚举
public enum MessageStatus
{
Sent, // 已发送
Delivered, // 已送达
Read // 已读
}
2. 数据库上下文(EF Core)
public class OaDbContext : DbContext
{
public OaDbContext(DbContextOptions<OaDbContext> options) : base(options) { }
public DbSet<Message> Messages { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 配置索引(优化查询)
modelBuilder.Entity<Message>()
.HasIndex(m => new { m.SenderId, m.ReceiverId });
}
}
3. SignalR 通讯集线器(核心)
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
// 对话通讯集线器
public class ChatHub : Hub
{
private readonly OaDbContext _dbContext;
public ChatHub(OaDbContext dbContext)
{
_dbContext = dbContext;
}
// 连接时加入个人用户组(用于定向推送)
public override async Task OnConnectedAsync()
{
var userId = Context.UserIdentifier; // 需要配置身份认证获取用户ID
if (!string.IsNullOrEmpty(userId))
{
await Groups.AddToGroupAsync(Context.ConnectionId, $"user_{userId}");
}
await base.OnConnectedAsync();
}
// 发送消息
public async Task SendMessage(string receiverId, string content)
{
var senderId = Context.UserIdentifier;
if (string.IsNullOrEmpty(senderId) || string.IsNullOrEmpty(receiverId))
return;
// 1. 保存消息到数据库
var message = new Message
{
SenderId = senderId,
ReceiverId = receiverId,
Content = content
};
_dbContext.Messages.Add(message);
await _dbContext.SaveChangesAsync();
// 2. 实时推送给接收者(通过用户组)
await Clients.Group($"user_{receiverId}").SendAsync(
"ReceiveMessage",
senderId,
content,
message.SendTime,
message.Id
);
// 3. 通知发送者消息已送达
await Clients.Caller.SendAsync("MessageDelivered", message.Id);
}
// 标记消息为已读
public async Task MarkAsRead(int messageId)
{
var message = await _dbContext.Messages.FindAsync(messageId);
if (message != null && message.ReceiverId == Context.UserIdentifier)
{
message.Status = MessageStatus.Read;
await _dbContext.SaveChangesAsync();
// 通知发送者消息已读
await Clients.Group($"user_{message.SenderId}").SendAsync("MessageRead", messageId);
}
}
}
4. 配置 SignalR 服务(Program.cs)
var builder = WebApplication.CreateBuilder(args);
// 添加数据库上下文
builder.Services.AddDbContext<OaDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("OaDb")));
// 添加SignalR服务
builder.Services.AddSignalR();
// 添加身份认证(根据实际需求配置,如JWT)
builder.Services.AddAuthentication();
var app = builder.Build();
// 配置中间件
app.UseAuthentication();
app.UseAuthorization();
// 映射SignalR集线器端点
app.MapHub<ChatHub>("/chathub");
app.Run();
5. 前端调用示例(JavaScript)
<!-- 引入SignalR客户端 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.min.js"></script>
<script>
// 连接到SignalR集线器
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub", {
accessTokenFactory: () => "用户认证Token" // 需传入登录用户的Token
})
.build();
// 接收消息事件
connection.on("ReceiveMessage", (senderId, content, sendTime, messageId) => {
console.log(`收到来自${senderId}的消息:${content}`);
// 显示消息到界面
// 调用标记已读
connection.invoke("MarkAsRead", messageId);
});
// 连接启动
connection.start().catch(err => console.error(err.toString()));
// 发送消息函数
function sendMessage(receiverId, content) {
connection.invoke("SendMessage", receiverId, content)
.catch(err => console.error(err.toString()));
}
</script>
6. 消息历史查询 API
[ApiController]
[Route("api/messages")]
public class MessageController : ControllerBase
{
private readonly OaDbContext _dbContext;
public MessageController(OaDbContext dbContext) => _dbContext = dbContext;
// 获取与指定用户的聊天历史
[HttpGet("history/{targetUserId}")]
public async Task<IActionResult> GetHistory(string targetUserId)
{
var currentUserId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var messages = await _dbContext.Messages
.Where(m => (m.SenderId == currentUserId && m.ReceiverId == targetUserId) ||
(m.SenderId == targetUserId && m.ReceiverId == currentUserId))
.OrderBy(m => m.SendTime)
.ToListAsync();
return Ok(messages);
}
}