SignalR——聊天室实践

SignalR 是一个为 ASP.NET 开发者设计的库,它简化了在 Web 应用程序中添加实时功能的过程。实时功能指的是服务器能够在客户端没有发起请求的情况下主动向客户端推送内容的能力。这种技术使得服务器和客户端之间的通信更加动态和即时,非常适合需要实时更新数据的应用场景,如聊天应用、实时数据分析、多人协作编辑工具等。

SignalR 的特点:
**双向通信:**SignalR 允许服务器和客户端之间进行双向通信,即服务器可以主动向客户端发送数据,而不仅仅限于传统的客户端请求-服务器响应模式。

**自动处理连接状态:**SignalR 自动处理客户端的连接状态,包括连接、断开连接以及重新连接等操作。

**多种传输方式:**SignalR 支持多种传输方式,如 WebSockets、Server-Sent Events (SSE)、Long Polling 等,以确保在不同的浏览器和网络环境下都能实现最佳的通信效果。

易于使用的 API: SignalR 提供了一个简单易用的 API,使得开发者可以轻松地在服务器端实现远程过程调用 (RPC),即在服务器端调用客户端的 JavaScript 函数,反之亦然。

**客户端支持:**除了 JavaScript 客户端之外,SignalR 还支持其他客户端平台,如 iOS、Android 和 .NET 客户端。

**分组和过滤:**SignalR 允许你将连接到同一个 Hub 的客户端分组,并对特定组发送消息。
使用场景:

实时聊天应用:实现即时消息传送。

在线协作编辑:允许多个用户同时编辑同一个文档。

股票市场或体育赛事直播:实时更新数据到用户的界面。

在线游戏:实现玩家之间的实时互动。

SignalR 适用于 ASP.NETASP.NET Core 应用程序,并且是开源的,这意味着它可以免费使用,并且有一个活跃的社区支持。如果你正在使用 ASP.NETASP.NET Core 构建 Web 应用程序,并且需要实现实时功能,那么 SignalR 将是一个很好的选择。

下面使用ASP.NET Core应用程序跟做一个项目:使用VS

参考视频:011.SqlSugar介绍和安装_哔哩哔哩_bilibili

项目初始化

不要选错喽~

新建完成后的目录结构:

运行截图:

绘制聊天界面

修改Index.cshtml页面

html 复制代码
@{
    ViewData["Title"] = "Home Page";
}

<div class="container">
    <p> 当前登录用户:@ViewBag.UserName [<a href="/Home/LogOut"> 退出</a>]</p>
    <div id="content"></div>
    <div contenteditable="true" id="messageInput"></div>
    <div class="row">
        <div class="col-6">
            <input type="button" id="sendButton" value="Send Message"/>
            <input type="button" id="findButton" value="Find Message" />

        </div>
    </div>
    <div id="historyMessage"></div>
</div>
<style>
    .container{
        width:800px;
        margin:0px auto;

    }
    #content{
        width:100%;
        height:300px;
       /*  溢出隐藏  有滚轮 */
        overflow-x:hidden;
        border:1px solid #ccc;
    }
    #messageInput{
        width:100%;
        min-height:100px;
        border:1px solid #bbb;
    }
    #historyMessage{
        display:none;
        width:100%;
        height:300px;
        overflow:hidden;
        border:1px solid #ccc;
    }
</style>

其中@ViewBag.UserName 需要在HomeController中设置一个默认值,如Rise,如图

运行结果:

新增登录页面

在HomeController中新增控制器

cs 复制代码
 public IActionResult Login()
 {
     return View();
 }

右键Login新增视图或者在View爆红处Alt+Enter提示添加视图

会新增一个Login.cshtml文件

添加代码

html 复制代码
<div id="login">
    <h1 class="title"> 登录聊天室</h1>
    <form action="/Home/Submit">
        <table>
            <tr>
                <td>
                    用户名:
                </td>
                <td> <input placeholder="请输入用户名" name="name"/></td>


            </tr>
            <tr>
                <td>密码:</td>
                <td> <input placeholder="请输入密码" name="password" type="password"/></td>

            </tr>
            <tr>
                <td colspan="2">
                    <div class="operation">
                        <button id="submit" type="submit"> 登录</button>
                        <button id="cancel" > 取消</button>

                    </div>
                </td>
            </tr>
        </table>
    </form>
</div>
<style>
    table{
        margin :200px auto;
        border-collapse:separate;
        border-spacing:10px;

    }

    .operation{
        display:flex;
        justify-content:space-evenly;
    }
    button{
        width:80px;
    }
    .title{
        justify-content: space-evenly;
    }
</style>

/Home/Login 查看实现样式

介绍SignalR

SignalR是一个继承的客户端与服务器库 ‌,它基于浏览器的客户端和ASP.NET的服务器组件,允许进行双向多步对话。这种对话不受限制,可以进行单个无状态请求/响应数据交换,直到明确关闭。对话通过永久连接进行,使得客户端和服务器能够发送多个消息,并允许服务器做出相应答复。特别的是,SignalR还允许服务器向服务端发送异步消息,这与 Ajax技术相似,都是基于现有的技术实现的。 在实现客户端和服务端通信时,SignalR通常会使用JS的长轮询(long polling)。此外,随着WebSockets的出现,SignalR也支持WebSockets通信,从而提供了更高效的实时通信解决方案。

SignalR的应用场景非常广泛,例如实现实时聊天、服务器广播高频率实时功能 。通过SignalR,开发人员可以轻松地构建实时Web应用程序,实现服务器与客户端之间的实时数据同步,从而提高用户体验和应用程序的交互性 。此外,SignalR还提供了丰富的文档和示例,帮助开发人员快速入门和使用。无论是通过Visual Studio的不同版本进行开发,还是利用SignalR实现单页应用程序的实时更新,SignalR都为Web开发提供了强大的支持‌

官方介绍:SignalR 简介 | Microsoft Learn

添加SignalR客户端库

右键项目-点击添加-点击客户端库,如图

提供程序选择unpkg,输入库(不要输错,输入完成回车),点击选择特定文件就自动带出来了,勾选两个js,修改目标位置即可,如图,点击安装。

等待几秒看到解决方案管理器中的相对路径中是否存在两个js文件

存在即安装好了

创建SignalR中心

中心是一个类,用于处理客户端和服务器通信的高级管道,在SignalRWebApp项目文件夹中,创建Hubs文件夹。在Hubs文件夹中,使用以下代码创建ChatHub类.

项目右键,新建文件夹Hubs

新建的文件夹下,右键新建一个类ChartHub

ChatHub.cs中添加代码

cs 复制代码
using Microsoft.AspNetCore.SignalR;

namespace SignalRWebWebApp.Hubs
{
    /// <summary>
    ///
    ///创建SignalR中心
    /// </summary>
    public class ChatHub:Hub
    {
        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="user">用户名</param>
        /// <param name="message">密码</param>
        /// <returns></returns>
        public async Task SendMessage(String user, String message)
        {
            await Clients.All.SendAsync("ReceiveMessage",user,message);
        }
    }
}

配置SignalR

必须将SignalR服务器配置为将SignalR请求传递给SignalR。

将一下突出显示的代码添加到Program.cs文件中

cs 复制代码
builder.Services.AddSignalR();
app.MapHub<ChatHub>("/chatHub");

program.cs

cs 复制代码
using SignalRWebWebApp.Hubs;

var builder = WebApplication.CreateBuilder(args);
//添加signalR服务
builder.Services.AddSignalR();
// Add services to the container.
builder.Services.AddControllersWithViews();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

//配置
app.MapHub<ChatHub>("/chatHub");


app.Run();

添加SignalR客户端代码

wwwroot下的js文件夹,右键新建项

新增chat.js(javaScript文件)

在chat.js中添加打印代码

console.log("执行客户端代码");

在Views-Home-Index.cshtml中引入js文件

cs 复制代码
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="js/signalr/dist/browser/signalr.min.js"></script>
<script src="js/chat.js"></script>

运行检查是否引入成功

F12查看,说明引入成功

修改chat.js代码

cs 复制代码
"use strict";

//const { signalR } = require("./signalr/dist/browser/signalr");
//连接服务
var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();

//禁止发送按钮,直到建立连接
$("#sendButton").hide();
//建立连接
connection.start().then(function () {
    //连接成功,则显示发送按钮
    $("#sendButton").show();
      console.log("连接成功!");

}).catch(function (err) {
    //连接失败则返回错误信息
    return console.error(err.toString());
});

运行,存在SendMessage按钮说明连接成功

chat.js中添加代码,如下所示,已经可以互通了

cs 复制代码
"use strict";

//const { signalR } = require("./signalr/dist/browser/signalr");
//连接服务
var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();

//禁止发送按钮,直到建立连接
$("#sendButton").hide();
//建立连接
connection.start().then(function () {
    //连接成功,则显示发送按钮
    $("#sendButton").show();
    console.log("连接成功!");

}).catch(function (err) {
    //连接失败则返回错误信息
    return console.error(err.toString());
});
//发送消息
$("#sendButton").click(function () {
    var user = $("#userInput").val();
    var message = $("#messageInput").text();
    connection.invoke("SendMessage", user, message).catch(function (err) {
        return console.error(err.toString);
    })
});

//接收消息
connection.on("ReceiveMessage", function (user, message, time) {
    $("#content").append(`<p>${user} ${time}</p><p>${message}</p><br>`);
    $("#content").animate({ scrollTop: 100000 });
})

sqlSugar介绍和安装

官网:SqlSugar .Net ORM 5.X 官网 、文档、教程 - SqlSugar 5x - .NET果糖网 (donet5.com)

ISqlSugarClient

在依赖项中右键管理NuGet程序包,搜索sqlSugarCore安装

依赖项中出现包说明安装成功

appsettings.json文件中添加连接数据库的字符串

在Program.cs中添加注入SqlSugar服务的

cs 复制代码
builder.Services.AddTransient<ISqlSugarClient>(provider =>
{
    SqlSugarClient db = new SqlSugarClient(new ConnectionConfig()
    {
        ConnectionString = builder.Configuration.GetSection("conn").Value,
        DbType = DbType.MySql,
        IsAutoCloseConnection = true
    });
    return db;
});

UserService

新增service文件夹并新增两个文件IUserService和UserService

program.cs中注册服务

cs 复制代码
//注册用户服务
builder.Services.AddTransient<IUserService, UserService>();

CodeFirst

添加

UserInfo和Usermessage

UserInfo代码

cs 复制代码
using SqlSugar;

namespace SignalRWebWebApp.Models
{
    public class UserInfo
    {
        [SugarColumn(IsPrimaryKey = true)]
        public int Id { get; set; }

        public string Name { get; set; }
        public string Password { get; set; }
    }
}

UserMessage代码

cs 复制代码
using SqlSugar;

namespace SignalRWebWebApp.Models
{
    public class Usermessage
    {
        [SugarColumn(IsPrimaryKey = true)]
        public int Id { get; set; }
        public string UserName { get; set; }
        public string Content { get; set; }
        public DateTime CreateDateTime { get; set; }
    }
}

修改UserService.cs(注意两个命名空间名字)

cs 复制代码
using System.Reflection;
using SqlSugar;

namespace SignalRWebWebApp.Service
{
    public class UserService : IUserService
    {
        private readonly ISqlSugarClient _db;
        public UserService(ISqlSugarClient db)
        {
            _db = db;
        }   

        public bool CodeFirst()
        {
            try
            {
                //创建数据库
                _db.DbMaintenance.CreateDatabase();
                string nspace = "SignalRWebWebApp.Models";
                Type[] ass = Assembly.LoadFrom(AppContext.BaseDirectory + "SignalRWebWebApp.dll").GetTypes()
                    .Where(p => p.Namespace == nspace).ToArray();
                _db.CodeFirst.SetStringDefaultLength(200).InitTables(ass);
            }
            catch (Exception e)
            {
                
                return false;
            }
            return true;
        }
    }
}

修改IUserSercice.cs

cs 复制代码
namespace SignalRWebWebApp.Service
{
    public interface IUserService
    {
        bool CodeFirst();
    }
}

修改HomeController

cs 复制代码
using Microsoft.AspNetCore.Mvc;
using SignalRWebWebApp.Models;
using System.Diagnostics;
using SignalRWebWebApp.Service;

namespace SignalRWebWebApp.Controllers
{
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;
        private readonly IUserService _user;

        public HomeController(ILogger<HomeController> logger,IUserService user)
        {
            _logger = logger;
            _user = user;
        }

        public bool CodeFirst()
        {
            return _user.CodeFirst();
        }

        public IActionResult Index()
        {
            ViewBag.UserName = "Rise";
            return View();
        }


        public IActionResult Login()
        {
            return View();
        }

        public IActionResult Privacy()
        {
            return View();
        }


    }
}

运行访问/Home/CodeFirst 如果是true则数据库和表新建成功

如果是false则排查问题

获取注册用户信息接口

在UserService和IUserService中添加相关代码

IUserService

cs 复制代码
using SignalRWebWebApp.Models;

namespace SignalRWebWebApp.Service
{
    public interface IUserService
    {
        bool CodeFirst();
        UserInfo GetUser(string name, string password);
    }
}

UserService

cs 复制代码
  public UserInfo GetUser(string name, string password)
  {
      UserInfo userInfo = _db.Queryable<UserInfo>().First(n=>n.Name==name);
      if (userInfo == null)
      {
          UserInfo newInfo = new UserInfo()
          {
              Id = Convert.ToInt32(Guid.NewGuid().ToString()),
              Name = name,
              Password = password
          };
          return _db.Insertable(newInfo).ExecuteReturnEntity();
      }
      else if (userInfo.Password == password)
      {
          return new UserInfo();
      }

      return userInfo;
  }

获取消息记录接口

UserService、IUserService和HomeController中新增内容

IUserService

cs 复制代码
List<UserMessage> GetMessages(int pageIndex,int pageSize);

UserService

cs 复制代码
        public List<UserMessage> GetMessages(int pageIndex, int pageSize)
        {
            return _db.Queryable<UserMessage>().ToOffsetPage(pageIndex, pageSize);
        }

HomeController

cs 复制代码
  public JsonResult GetMessages(int pageIndex,int pageSize )
  {
      return Json(_user.GetMessages(pageIndex, pageSize));
  }

首页重定向

program.cs添加

cs 复制代码
//添加Session服务
builder.Services.AddSession();
//使用
app.UseSession();

修改HomeController

cs 复制代码
    public IActionResult Index()
    {
        var userName = HttpContext.Session.GetString("userName");
        if (string.IsNullOrEmpty(userName))
        {
            return Redirect("/Home/Login");
        }
        

        ViewBag.UserName = userName;
        return View();
    }

登录逻辑

在HomeController中添加代码

cs 复制代码
    public IActionResult Submit(string name, string password)
    {
        UserInfo userInfo = _user.GetUser(name, password);
        if (string.IsNullOrEmpty(userInfo.Id))
        {
            return Redirect("/Home/Error");
        }
        HttpContext.Session.SetString("userName",userInfo.Name);
        Response.Cookies.Append("userName",userInfo.Name);
        return Redirect("/");
    }

退出逻辑

在HomeController中添加代码

cs 复制代码
 public IActionResult LogOut()
 {
     HttpContext.Session.Remove("userName");
     Response.Cookies.Delete("userName");
    return  Redirect("/Home/Login");
 }

补充SignalR中心逻辑

ChatHub中新增

cs 复制代码
using Microsoft.AspNetCore.SignalR;
using SignalRWebWebApp.Models;
using SqlSugar;

namespace SignalRWebWebApp.Hubs
{
    /// <summary>
    ///
    ///创建SignalR中心
    /// </summary>
    public class ChatHub:Hub
    {
        private ISqlSugarClient _db;
        public ChatHub(ISqlSugarClient db)
        {
            _db = db;
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="user">用户名</param>
        /// <param name="message">密码</param>
        /// <returns></returns>
        public async Task SendMessage(String user, String message)
        {
            DateTime time= DateTime.Now;
            UserMessage userMessage = new UserMessage()
            {
                Id = Guid.NewGuid().ToString(),
                UserName = user,
                Content = message,
                CreateDateTime = time,
            };
            _db.Insertable(userMessage).ExecuteCommand();
            await Clients.All.SendAsync("ReceiveMessage",user,message,time.ToString());
        }
    }
}

查看历史聊天记录

在chat.js中新增

cs 复制代码
//查看聊天记录
var pageIndex = 1;
$("#findMessage").click(function () {
    $("#historyMessage").fadeIn();
    $.post("/Home/GetMessages", { pageIndex: pageIndex, pageSize: 10 }, function (data) {
        $.each(data, function (i, e) {
            $("#historyMessage").append(`<p>${e.userName} ${e.createDateTime}</p><p>${e.content}</p><br>`);

        });
    })

})
相关推荐
高山我梦口香糖3 分钟前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235246 分钟前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_748240251 小时前
前端如何检测用户登录状态是否过期
前端
black^sugar1 小时前
纯前端实现更新检测
开发语言·前端·javascript
寻找沙漠的人2 小时前
前端知识补充—CSS
前端·css
GISer_Jing2 小时前
2025前端面试热门题目——计算机网络篇
前端·计算机网络·面试
m0_748245522 小时前
吉利前端、AI面试
前端·面试·职场和发展
理想不理想v2 小时前
webpack最基础的配置
前端·webpack·node.js
pubuzhixing2 小时前
开源白板新方案:Plait 同时支持 Angular 和 React 啦!
前端·开源·github
2401_857600952 小时前
SSM 与 Vue 共筑电脑测评系统:精准洞察电脑世界
前端·javascript·vue.js