SignalR使用记录(一)

最近公司在做plc对接的时候不知道抽什么风,要求上位机不用桌面端来实现,要求嵌入到管理平台的web程序中,于是在和KIMI、元宝头脑风暴了N次之后选择单独做一个单例服务去做opcua、socket客户端、socket服务端融合到一起。但是现在在做实时推送opcua数据到前端页面时犯了难,一开始使用的websocket,之后发现我使用默认的websocket配置经常会掉线,然后前端配置的重连又无效。之后同事说让我使用SignalR。正好就当学点新技术,也就记录一下。

首先新建一个.net core mvc或者api项目。

在项目中添加一个Hubs文件夹。在文件夹下创建三个文件:

user.cs:

public class User
{
    public User(string ConnectionID, string Id, string Name)
    {
        this.ConnectionID = ConnectionID;
        this.Id = Id;
        this.Name = Name;
    }
    /// <summary>
    /// 连接ID
    /// </summary>
    [Key]
    public string ConnectionID { get; set; }
​
    public HubCallerContext Context { get; set; }
​
    public IHubCallerClients Clients { get; set; }
​
    public string Id { get; set; }
​
    public string Name { get; set; }
}

ChatUser.cs:

public static class ChatUser
{
    //创建用户集合,用于存储所有链接的用户数据
    public static List<User> users = new List<User>();
}

ChatHub.cs:

public class ChatHub : Hub
{
    public async Task SendMessage(string user, string message)
    {
        if (new Random().Next(1, 3) == 1)
        {
            await Clients.Caller.SendAsync("NoticeMessage", "随机通知自己888");
        }
        else
        {
            await Clients.Others.SendAsync("NoticeMessage", "随机通知其他人");
        }
​
        await Clients.All.SendAsync("ReceiveMessage", user, message);
    }
​
    public async Task NoticeMessage(string message)
    {
        await Clients.All.SendAsync("NoticeMessage", message);
    }
​
    public override async Task OnConnectedAsync()
    {
        string connectionID = Context.ConnectionId;
        //查询用户
        var user = ChatUser.users.SingleOrDefault(u => u.ConnectionID == connectionID);
        //判断用户是否存在,否则添加集合
        if (user == null)
        {
            user = new User(Context.ConnectionId, new Guid().ToString(), "已绑定用户");
            user.Clients = Clients;
            user.Context = Context;
            ChatUser.users.Add(user);
        }
        await Clients.All.SendAsync("ConnectMessage", "用户上线", connectionID);
    }
​
    public override async Task OnDisconnectedAsync(Exception? exception)
    {
        string connectionID = Context.ConnectionId;
        //查询用户
        var user = ChatUser.users.SingleOrDefault(u => u.ConnectionID == connectionID);
        //判断用户是否存在,否则添加集合
        if (user != null)
        {
            ChatUser.users.Remove(user);
        }
        await Clients.All.SendAsync("ConnectMessage", "用户" + connectionID + "由于" + exception?.Message + "掉线");
    }
}

创建完成之后将在Startup.cs中添加上配置:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSignalR();   //添加SignalR
}

 

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseRouting();
    app.UseAuthorization();
    app.UseCors(option => option.WithOrigins("http://localhost:53649").AllowCredentials().AllowAnyMethod().AllowAnyHeader());  //配置跨域
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
        endpoints.MapHub<ChatHub>("/chatHub");   //配置前端请求接口
    });
}

 

后端的服务配置基本按这个模式就配置完成了。

 

接下来是前端代码:

前端代码可以在同一个程序里面做一个页面,如果是mvc就直接在在view里面做,api就在wwwroot里面新建一个html就行。

可以选择网络的js包:

<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.js"></script>

也可以在对应地址下载js包到本地:

microsoft-signalr - 库 - cdnjs - #1 免费开源 CDN,旨在让开发人员的生活更轻松

之后在前端页面加上对应的js代码来连接服务端和做数据收发:

<script src="js/signalr/dist/browser/signalr.js"></script>
<script>
$(document).ready(function () {
​
    var connection = new signalR.HubConnectionBuilder().withUrl("http://localhost:8900/chatHub").build();
​
    connection.on("ReceiveMessage", function (user, message) {
        console.log(`${user} 发送消息 ${message}`);
    });
​
    connection.on("ConnectMessage", function (message) {
        console.log(`${message}`);
    });
​
    connection.on("NoticeMessage", function (message) {
        console.log(`${message}`);
        $("#message-container").html(`<div>服务端消息: ${message}</div>`);
    });
​
    connection.start().then(function () {
    }).catch(function (err) {
        return console.error(err.toString());
    });
        
</script>

 

上面的代码能实现最基础的signalr代码接收。

现在来做一个简单的触发下发:

在程序里面加一个控制器:

ChatSendController.cs

[ApiController]
[Route("[controller]")]
public class ChatSendController : ControllerBase
{
    private readonly ILogger<ChatSendController> _logger;
    private readonly IHubContext<ChatHub> _hubContext;
    public ChatSendController(IHubContext<ChatHub> hubContext, ILogger<ChatSendController> logger)
    {
        _logger = logger;
        _hubContext = hubContext;
    }
    /// <summary>
    /// 给指定用户发送消息
    /// </summary>
    /// <param name="user"></param>
    /// <returns></returns>
    [HttpGet]
    public async Task<JsonResult> Get(string user)
    {
        if (!string.IsNullOrEmpty(user))
        {
            await _hubContext.Clients.Client(user).SendAsync("NoticeMessage", "指定用户消息");
        }
        return new JsonResult("发送信息成功");
    }
    
    /// <summary>
    /// 给所有用户发送消息
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    public async Task<JsonResult> Getall()
    {
        await _hubContext.Clients.All.SendAsync("NoticeMessage", "所有用户消息");
        return new JsonResult("发送信息成功");
    }
}

然后启动项目打开对应的前端页面,然后钓鱼getall接口即可控制signalr下发小心给所有用户。

这个暂时就先写到这里吧,公司的需求暂时就只需要做下发,不需要做接收客户端消息,但是依照我写socket和mqtt的经验,这个的功能应该是挺强大的,后面看看能不能做个简单的聊天室功能出来。

 
posted @ 2025-04-26 23:12  温凉半生  阅读(32)  评论(0)    收藏  举报