WPF+ASP.NET SignalR实现简易在线聊天功能的示例代码

目录
  • 涉及知识点
  • 什么是ASP.NET SignalR
  • 在线聊天整体架构
  • ASP.NET SignalR在线聊天服务端
    • 1. 创建ASP.NET Web API项目
    • 2. 创建消息通知中心Hub
    • 3. 注册服务和路由
    • 4. ASP.NET SignalR中心对象生存周期
  • SignalR客户端
    • 1. 安装SignalR客户端依赖库
    • 2. 客户端消息接收发送
  • 运行示例

在实际业务中,当后台数据发生变化,客户端能够实时的收到通知,而不是由用户主动的进行页面刷新才能查看,这将是一个非常人性化的设计。有没有那么一种场景,后台数据明明已经发生变化了,前台却因为没有及时刷新,而导致页面显示的数据与实际存在差异,从而造成错误的判断。那么如何才能在后台数据变更时及时通知客户端呢?本文以一个简单的聊天示例,简述如何通过WPF+ASP.NET SignalR实现消息后台通知,仅供学习分享使用,如有不足之处,还请指正。

涉及知识点

在本示例中,涉及知识点如下所示:

开发工具:Visual Studio 2022 目标框架:.NET6.0

ASP.NET SignalR,一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信,目前新版已支持.NET6.0及以上版本。在本示例中,作为消息通知的服务端。

WPF,是微软推出的基于Windows 的用户界面框架,主要用于开发客户端程序。

什么是ASP.NET SignalR

ASP.NET SignalR 是一个面向 ASP.NET 开发人员的库,可简化将实时 web 功能添加到应用程序的过程。 实时 web 功能是让服务器代码将内容推送到连接的客户端立即可用,而不是让服务器等待客户端请求新数据的能力。

SignalR 提供了一个简单的 API,用于创建服务器到客户端远程过程调用 (RPC) ,该调用客户端浏览器 (和其他客户端平台中的 JavaScript 函数) 服务器端 .NET 代码。 SignalR 还包括用于连接管理的 API (,例如连接和断开连接事件) ,以及分组连接。

虽然聊天通常被用作示例,但你可以做更多的事情。每当用户刷新网页以查看新数据时,或者该网页实施 Ajax 长轮询以检索新数据时,它都是使用 SignalR 的候选者。SignalR 还支持需要从服务器进行高频更新的全新类型的应用,例如实时游戏。

在线聊天整体架构

在线聊天示例,主要分为服务端(ASP.NET Web API)和客户端(WPF可执行程序)。具体如下所示:

ASP.NET SignalR在线聊天服务端

服务端主要实现消息的接收,转发等功能,具体步骤如下所示:

1. 创建ASP.NET Web API项目

首先创建ASP.NET Web API项目,默认情况下SignalR已经作为项目框架的一部分而存在,所以不需要安装,直接使用即可。通过项目--依赖性--框架--Microsoft.AspNetCore.App可以查看,如下所示

2. 创建消息通知中心Hub

在项目中新建Chat文件夹,然后创建ChatHub类,并继承Hub基类。主要包括登录(Login),聊天(Chat)等功能。如下所示:

using Microsoft.AspNetCore.SignalR;

namespace SignalRChat.Chat
{
    public class ChatHub:Hub
    {
        private static Dictionary<string,string> dictUsers = new Dictionary<string,string>();

        public override Task OnConnectedAsync()
        {
            Console.WriteLine($"ID:{Context.ConnectionId} 已连接");
            return base.OnConnectedAsync();
        }

        public override Task OnDisconnectedAsync(Exception? exception)
        {
            Console.WriteLine($"ID:{Context.ConnectionId} 已断开");
            return base.OnDisconnectedAsync(exception);
        }

        /// <summary>
        /// 向客户端发送信息
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        public Task Send(string msg) {
            return Clients.Caller.SendAsync("SendMessage",msg);
        }

        /// <summary>
        /// 登录功能,将用户ID和ConntectionId关联起来
        /// </summary>
        /// <param name="userId"></param>
        public void Login(string userId) {
            if (!dictUsers.ContainsKey(userId)) {
                dictUsers[userId] = Context.ConnectionId;
            }
            Console.WriteLine($"{userId}登录成功,ConnectionId={Context.ConnectionId}");
            //向所有用户发送当前在线的用户列表
            Clients.All.SendAsync("Users", dictUsers.Keys.ToList());
        }

        /// <summary>
        /// 一对一聊天
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="targetUserId"></param>
        /// <param name="msg"></param>
        public void Chat(string userId, string targetUserId, string msg)
        {
            string newMsg = $"{userId}|{msg}";//组装后的消息体
            //如果当前用户在线
            if (dictUsers.ContainsKey(targetUserId))
            {
                Clients.Client(dictUsers[targetUserId]).SendAsync("ChatInfo",newMsg);
            }
            else {
                //如果当前用户不在线,正常是保存数据库,等上线时加载,暂时不做处理
            }
        }

        /// <summary>
        /// 退出功能,当客户端退出时调用
        /// </summary>
        /// <param name="userId"></param>
        public void Logout(string userId)
        {
            if (dictUsers.ContainsKey(userId))
            {
                dictUsers.Remove(userId);
            }
            Console.WriteLine($"{userId}退出成功,ConnectionId={Context.ConnectionId}");
        }
    }
}

3. 注册服务和路由

聊天类创建成功后,需要配置服务注入和路由,在Program中,添加代码,如下所示:

using SignalRChat.Chat;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
//1.添加SignalR服务
builder.Services.AddSignalR();
var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
app.UseRouting();
app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();
//2.映射路由
app.UseEndpoints(endpoints => {
    endpoints.MapHub<ChatHub>("/chat");
});

app.Run();

4. ASP.NET SignalR中心对象生存周期

你不会实例化 Hub 类或从服务器上自己的代码调用其方法;由 SignalR Hubs 管道为你完成的所有操作。 SignalR 每次需要处理中心操作(例如客户端连接、断开连接或向服务器发出方法调用时)时,SignalR 都会创建 Hub 类的新实例。

由于 Hub 类的实例是暂时性的,因此无法使用它们来维护从一个方法调用到下一个方法的状态。 每当服务器从客户端收到方法调用时,中心类的新实例都会处理消息。 若要通过多个连接和方法调用来维护状态,请使用一些其他方法(例如数据库)或 Hub 类上的静态变量,或者不派生自 Hub的其他类。 如果在内存中保留数据,请使用 Hub 类上的静态变量等方法,则应用域回收时数据将丢失。

如果要从在 Hub 类外部运行的代码将消息发送到客户端,则无法通过实例化 Hub 类实例来执行此操作,但可以通过获取对 Hub 类的 SignalR 上下文对象的引用来执行此操作。

注意:ChatHub每次调用都是一个新的实例,所以不可以有私有属性或变量,不可以保存对像的值,所以如果需要记录一些持久保存的值,则可以采用静态变量,或者中心以外的对象。

SignalR客户端

1. 安装SignalR客户端依赖库

客户端如果要调用SignalR的值,需要通过NuGet包管理器,安装SignalR客户端,如下所示:

2. 客户端消息接收发送

在客户端实现消息的接收和发送,主要通过HubConntection实现,核心代码,如下所示:

namespace SignalRClient
{
    public class ChatViewModel:ObservableObject
    {
        #region 属性及构造函数

        private string targetUserName;

        public string TargetUserName
        {
            get { return targetUserName; }
            set { SetProperty(ref targetUserName , value); }
        }

        private string userName;

        public string UserName
        {
            get { return userName; }
            set
            {
                SetProperty(ref userName, value);
                Welcome = $"欢迎 {value} 来到聊天室";
            }
        }

        private string welcome;

        public string Welcome
        {
            get { return welcome; }
            set { SetProperty(ref welcome , value); }
        }

        private List<string> users;

        public List<string> Users
        {
            get { return users; }
            set {SetProperty(ref users , value); }
        }

        private RichTextBox richTextBox;

        private HubConnection hubConnection;

        public ChatViewModel() {

        }

        #endregion

        #region 命令

        private ICommand loadedCommand;

        public ICommand LoadedCommand
        {
            get
            {
                if (loadedCommand == null)
                {
                    loadedCommand = new RelayCommand<object>(Loaded);
                }
                return loadedCommand;
            }
        }

        private void Loaded(object obj)
        {
            //1.初始化
            InitInfo();
            //2.监听
            Listen();
            //3.连接
            Link();
            //4.登录
            Login();
            //
            if (obj != null) {
                var eventArgs = obj as RoutedEventArgs;

                var window= eventArgs.OriginalSource as ChatWindow;
                this.richTextBox = window.richTextBox;
            }
        }

        private IRelayCommand<string> sendCommand;

        public IRelayCommand<string> SendCommand
        {
            get {
                if (sendCommand == null) {
                    sendCommand = new RelayCommand<string>(Send);
                }
                return sendCommand; }
        }

        private void Send(string msg)
        {
            if (string.IsNullOrEmpty(msg)) {
                MessageBox.Show("发送的消息为空");
                return;
            }
            if (string.IsNullOrEmpty(this.TargetUserName)) {
                MessageBox.Show("发送的目标用户为空");
                return ;
            }
            hubConnection.InvokeAsync("Chat",this.UserName,this.TargetUserName,msg);
            if (this.richTextBox != null)
            {
                Run run = new Run();
                Run run1 = new Run();
                Paragraph paragraph = new Paragraph();
                Paragraph paragraph1 = new Paragraph();
                run.Foreground = Brushes.Blue;
                run.Text = this.UserName;
                run1.Foreground= Brushes.Black;
                run1.Text = msg;
                paragraph.Inlines.Add(run);
                paragraph1.Inlines.Add(run1);
                paragraph.LineHeight = 1;
                paragraph.TextAlignment = TextAlignment.Right;
                paragraph1.LineHeight = 1;
                paragraph1.TextAlignment = TextAlignment.Right;
                this.richTextBox.Document.Blocks.Add(paragraph);
                this.richTextBox.Document.Blocks.Add(paragraph1);
                this.richTextBox.ScrollToEnd();
            }
        }

        #endregion

        /// <summary>
        /// 初始化Connection对象
        /// </summary>
        private void InitInfo() {
            hubConnection = new HubConnectionBuilder().WithUrl("https://localhost:7149/chat").WithAutomaticReconnect().Build();
            hubConnection.KeepAliveInterval =TimeSpan.FromSeconds(5);
        }

        /// <summary>
        /// 监听
        /// </summary>
        private void Listen() {
            hubConnection.On<List<string>>("Users", RefreshUsers);
            hubConnection.On<string>("ChatInfo",ReceiveInfos);
        }

        /// <summary>
        /// 连接
        /// </summary>
        private async void Link() {
            try
            {
               await hubConnection.StartAsync();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void Login()
        {
            hubConnection.InvokeAsync("Login", this.UserName);
        }

        private void ReceiveInfos(string msg)
        {
            if (string.IsNullOrEmpty(msg)) {
                return;
            }
            if (this.richTextBox != null)
            {
                Run run = new Run();
                Run run1 = new Run();
                Paragraph paragraph = new Paragraph();
                Paragraph paragraph1 = new Paragraph();
                run.Foreground = Brushes.Red;
                run.Text = msg.Split("|")[0];
                run1.Foreground = Brushes.Black;
                run1.Text = msg.Split("|")[1];
                paragraph.Inlines.Add(run);
                paragraph1.Inlines.Add(run1);
                paragraph.LineHeight = 1;
                paragraph.TextAlignment = TextAlignment.Left;
                paragraph1.LineHeight = 1;
                paragraph1.TextAlignment = TextAlignment.Left;
                this.richTextBox.Document.Blocks.Add(paragraph);
                this.richTextBox.Document.Blocks.Add(paragraph1);
                this.richTextBox.ScrollToEnd();
            }
        }

        private void RefreshUsers(List<string> users) {
            this.Users = users;
        }
    }
}

运行示例

在示例中,需要同时启动服务端和客户端,所以以多项目方式启动,如下所示:

运行成功后,服务端以ASP.NET Web API的方式呈现,如下所示:

客户端需要同时运行两个,所以在调试运行启动一个客户端后,还要在Debug目录下,手动双击客户端,再打开一个,并进行登录,如下所示:

系统运行时,后台日志输出如下所示:

到此这篇关于WPF+ASP.NET SignalR实现简易在线聊天功能的示例代码的文章就介绍到这了,更多相关WPF在线聊天功能内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Asp.net MVC SignalR来做实时Web聊天实例代码

    本章和大家分享的内容是使用Signal R框架创建个简易的群聊功能,主要讲解如何在.Net的MVC中使用这个框架,由于这个项目有官方文档(当然全英文),后面也不打算写分享篇了,主要目的是让朋友们在需要使用Web实时通信的时候有更多一种解决方案,毕竟这是微软主推的一种解决方案之一. SignalR网上简介 ASP.NET SignalR 是为 ASP.NET 开发人员提供的一个库,可以简化开发人员将实时 Web 功能添加到应用程序的过程.实时 Web 功能是指这样一种功能:当所连接的客户端变得可用

  • Asp.net使用SignalR实现聊天室的功能

    一.引言 在前一篇文章<Asp.net使用SignalR实现酷炫端对端聊天功能>中,我向大家介绍了如何实现实现端对端聊天的功能的,在这一篇文章中将像大家如何使用SignalR实现群聊这样的功能. 二.实现思路 要想实现群聊的功能,首先我们需要创建一个房间,然后每个在线用户可以加入这个房间里面进行群聊,我们可以为房间设置一个唯一的名字来作为标识.那SignalR类库里面是否有这样现有的方法呢?答案是肯定的. // IGroupManager接口提供如下方法 // 作用:将连接ID加入某个组 //

  • WPF微信聊天和通讯录按钮样式代码分享

    一.先用Path画一下轮廓 <Path Stroke="Red" StrokeThickness="1" Margin="10" StrokeDashCap="Round"> <Path.Data> <GeometryGroup> <PathGeometry Figures="M 4,40 A 16,13 0 1 1 10,45 L 3,48 Z" /> &l

  • Asp.net通过SignalR2进行实时聊天

    目录 一:什么是signalR 1.SignalR传输方式 1.HTML5 传输 2.Comet 传输 2.SignalR连接模式 二.服务端 1.添加服务端Hub 2.添加OWIN 启动类 三.客户端 四.运行结果 五.一对一聊天实例 一:什么是signalR Asp.net SignalR是微软为实现实时通信的一个类库. 一般情况下,signalR会使用JavaScript的长轮询(long polling)的方式来实现客户端和服务器通信,随着Html5中WebSockets出现,Signa

  • WPF+ASP.NET SignalR实现简易在线聊天功能的示例代码

    目录 涉及知识点 什么是ASP.NET SignalR 在线聊天整体架构 ASP.NET SignalR在线聊天服务端 1. 创建ASP.NET Web API项目 2. 创建消息通知中心Hub 3. 注册服务和路由 4. ASP.NET SignalR中心对象生存周期 SignalR客户端 1. 安装SignalR客户端依赖库 2. 客户端消息接收发送 运行示例 在实际业务中,当后台数据发生变化,客户端能够实时的收到通知,而不是由用户主动的进行页面刷新才能查看,这将是一个非常人性化的设计.有没

  • 基于Java Socket实现一个简易在线聊天功能(一)

    最近做了一个项目,其中有一个在线网页交流的需求,好久没写代码了,手都生疏了,于是先写demo练练手,分享到我们平台,以此做个记录,方便自己和大家使用. 先给大家说下实现步骤分这样几大步: 1.使用awt组件和socket实现简单的单客户端向服务端持续发送消息: 2.结合线程,实现多客户端连接服务端发送消息: 3.实现服务端转发客户端消息至所有客户端,同时在客户端显示: 4.把awt组件生成的窗口界面改成前端jsp或者html展示的界面,java socket实现的客户端改为前端技术实现. 这里首

  • workerman结合laravel开发在线聊天应用的示例代码

    项目背景: 最近由于公司的业务需求,需要用到聊天功能.而且有比较多的个性化需求需要定制.之前使用别人的聊天组件是基于微擎的.如果要移植到普通的H5在逻辑修改还有定制上存在比较多的困难.为此只能克服困难,自己搭建一个吧 什么是Workerman? Workerman是一款 开源 高性能异步 PHP socket即时通讯框架 .支持高并发,超高稳定性,被广泛的用于手机app.移动通讯,微信小程序,手游服务端.网络游戏.PHP聊天室.硬件通讯.智能家居.车联网.物联网等领域的开发. 支持TCP长连接,

  • Android在类微信程序中实现蓝牙聊天功能的示例代码

    项目要求 1.初次打开程序时右上角标题栏显示"无连接",点击旁边的按钮选择"我的好友",进入配对界面: 2.选择好友之后,返回主界面,标题栏会显示已连接的手机型号: 3.两部手机间可通过蓝牙聊天 效果展示 项目结构 主要代码 1.在清单文件中注册权限 <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission and

  • 微信小程序聊天功能的示例代码

    效果 初始化滚动条高度 var keyHeight = 0; 数据格式 const CHAT_DATA=[ { type:0,//0客服1用户 content:'欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎', headImg:'../../assets/common/images/headHortrait.jpeg',//头像 creatTime:'2019-01-01',//创建时间 contentType:'text' }

  • java实现简易局域网聊天功能

    本文实例为大家分享了java使用UDP模式编写聊天程序的具体代码,供大家参考,具体内容如下 Java代码: /* 使用UDP模式,编写一个聊天程序 有发送和接收数据2部分, 一个线程接收,一个线程发送 由于发送和接收动作是不一致的,所以要使用2个run方法 而且这两个方法要封装到不同的类中 本程序忽略了部分异常的处理,也未加入UI组件 这样比较简洁 发送端口9998 接受端口9999 用的是局域网广播地址,所以自己发的消息自己也收到了 [示例]:简易控制台聊天程序 */ import java.

  • Java实现在线聊天功能

    本文实例为大家分享了Java实现在线聊天功能的具体代码,供大家参考,具体内容如下 效果 关键代码 创建Client.java import java.io.IOException; import java.net.Socket; import java.net.UnknownHostException; /**  * @author Lete  * @乐特的程序永无BUG  * @createDate 2020- 07-04 22:13  *  * 1. SendThread 发送消息线程  *

  • WPF+ASP.NET SignalR实现后台通知功能的示例代码

    目录 涉及知识点 前提条件 服务端 客户端 运行示例 在实际业务中,当后台数据发生变化,客户端能够实时的收到通知,而不是由用户主动的进行页面刷新才能查看,这将是一个非常人性化的设计.比如数字化大屏,并没有人工的干预,而是自动的刷新数据,那如何才能实现数据的实时刷新呢?本文以一个简单示例,简述如何通过WPF+ASP.NET SignalR实现消息后台通知以及数据的实时刷新,仅供学习分享使用,如有不足之处,还请指正. 通过上一篇文章的学习,了解了如何通过SignalR实现在线聊天功能,在示例中,我们

  • Java中使用websocket实现在线聊天功能

    很早以前为了快速达到效果,使用轮询实现了在线聊天功能,后来无意接触了socket,关于socket我的理解是进程间通信,首先要有服务器跟客户端,服务的启动监听某ip端口定位该进程,客户端开启socket分配ip端口连接服务端ip端口,于是两个进程间便可以通信了.下面简单画个图理解. but,今天还是准备分享websocket的使用,先上效果,再贴代码. 第一步启动socket服务. 然后连接客户端连接服务器,加入聊天室,分别使用googel(白玉京,沈浪),火狐(楚留香),ie(李寻欢)进行测试

  • ASP.NET MVC4异步聊天室的示例代码

    本文介绍了ASP.NET MVC4异步聊天室的示例代码,分享给大家,具体如下: 类图: Domain层 IChatRoom.cs using System; using System.Collections.Generic; namespace MvcAsyncChat.Domain { public interface IChatRoom { void AddMessage(string message); void AddParticipant(string name); void GetM

随机推荐