欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

WPF+ASP.NET?SignalR實現(xiàn)簡易在線聊天功能的示例代碼

 更新時間:2022年09月13日 08:36:59   作者:小六公子  
這篇文章將以一個簡單的聊天示例,簡述如何通過WPF+ASP.NET?SignalR實現(xiàn)消息后臺通知,僅供學習分享使用,如有不足之處,還請指正

在實際業(yè)務(wù)中,當后臺數(shù)據(jù)發(fā)生變化,客戶端能夠?qū)崟r的收到通知,而不是由用戶主動的進行頁面刷新才能查看,這將是一個非常人性化的設(shè)計。有沒有那么一種場景,后臺數(shù)據(jù)明明已經(jīng)發(fā)生變化了,前臺卻因為沒有及時刷新,而導致頁面顯示的數(shù)據(jù)與實際存在差異,從而造成錯誤的判斷。那么如何才能在后臺數(shù)據(jù)變更時及時通知客戶端呢?本文以一個簡單的聊天示例,簡述如何通過WPF+ASP.NET SignalR實現(xiàn)消息后臺通知,僅供學習分享使用,如有不足之處,還請指正。

涉及知識點

在本示例中,涉及知識點如下所示:

開發(fā)工具:Visual Studio 2022 目標框架:.NET6.0

ASP.NET SignalR,一個ASP .NET 下的類庫,可以在ASP .NET 的Web項目中實現(xiàn)實時通信,目前新版已支持.NET6.0及以上版本。在本示例中,作為消息通知的服務(wù)端。

WPF,是微軟推出的基于Windows 的用戶界面框架,主要用于開發(fā)客戶端程序。

什么是ASP.NET SignalR

ASP.NET SignalR 是一個面向 ASP.NET 開發(fā)人員的庫,可簡化將實時 web 功能添加到應(yīng)用程序的過程。 實時 web 功能是讓服務(wù)器代碼將內(nèi)容推送到連接的客戶端立即可用,而不是讓服務(wù)器等待客戶端請求新數(shù)據(jù)的能力。

SignalR 提供了一個簡單的 API,用于創(chuàng)建服務(wù)器到客戶端遠程過程調(diào)用 (RPC) ,該調(diào)用客戶端瀏覽器 (和其他客戶端平臺中的 JavaScript 函數(shù)) 服務(wù)器端 .NET 代碼。 SignalR 還包括用于連接管理的 API (,例如連接和斷開連接事件) ,以及分組連接。

雖然聊天通常被用作示例,但你可以做更多的事情。每當用戶刷新網(wǎng)頁以查看新數(shù)據(jù)時,或者該網(wǎng)頁實施 Ajax 長輪詢以檢索新數(shù)據(jù)時,它都是使用 SignalR 的候選者。SignalR 還支持需要從服務(wù)器進行高頻更新的全新類型的應(yīng)用,例如實時游戲。

在線聊天整體架構(gòu)

在線聊天示例,主要分為服務(wù)端(ASP.NET Web API)和客戶端(WPF可執(zhí)行程序)。具體如下所示:

ASP.NET SignalR在線聊天服務(wù)端

服務(wù)端主要實現(xiàn)消息的接收,轉(zhuǎn)發(fā)等功能,具體步驟如下所示:

1. 創(chuàng)建ASP.NET Web API項目

首先創(chuàng)建ASP.NET Web API項目,默認情況下SignalR已經(jīng)作為項目框架的一部分而存在,所以不需要安裝,直接使用即可。通過項目--依賴性--框架--Microsoft.AspNetCore.App可以查看,如下所示

2. 創(chuàng)建消息通知中心Hub

在項目中新建Chat文件夾,然后創(chuàng)建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>
        /// 向客戶端發(fā)送信息
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        public Task Send(string msg) {
            return Clients.Caller.SendAsync("SendMessage",msg);
        }

        /// <summary>
        /// 登錄功能,將用戶ID和ConntectionId關(guān)聯(lián)起來
        /// </summary>
        /// <param name="userId"></param>
        public void Login(string userId) {
            if (!dictUsers.ContainsKey(userId)) {
                dictUsers[userId] = Context.ConnectionId;
            }
            Console.WriteLine($"{userId}登錄成功,ConnectionId={Context.ConnectionId}");
            //向所有用戶發(fā)送當前在線的用戶列表
            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 {
                //如果當前用戶不在線,正常是保存數(shù)據(jù)庫,等上線時加載,暫時不做處理
            }
        }

        /// <summary>
        /// 退出功能,當客戶端退出時調(diào)用
        /// </summary>
        /// <param name="userId"></param>
        public void Logout(string userId)
        {
            if (dictUsers.ContainsKey(userId))
            {
                dictUsers.Remove(userId);
            }
            Console.WriteLine($"{userId}退出成功,ConnectionId={Context.ConnectionId}");
        }
    }
}

3. 注冊服務(wù)和路由

聊天類創(chuàng)建成功后,需要配置服務(wù)注入和路由,在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服務(wù)
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 類或從服務(wù)器上自己的代碼調(diào)用其方法;由 SignalR Hubs 管道為你完成的所有操作。 SignalR 每次需要處理中心操作(例如客戶端連接、斷開連接或向服務(wù)器發(fā)出方法調(diào)用時)時,SignalR 都會創(chuàng)建 Hub 類的新實例。

由于 Hub 類的實例是暫時性的,因此無法使用它們來維護從一個方法調(diào)用到下一個方法的狀態(tài)。 每當服務(wù)器從客戶端收到方法調(diào)用時,中心類的新實例都會處理消息。 若要通過多個連接和方法調(diào)用來維護狀態(tài),請使用一些其他方法(例如數(shù)據(jù)庫)或 Hub 類上的靜態(tài)變量,或者不派生自 Hub的其他類。 如果在內(nèi)存中保留數(shù)據(jù),請使用 Hub 類上的靜態(tài)變量等方法,則應(yīng)用域回收時數(shù)據(jù)將丟失。

如果要從在 Hub 類外部運行的代碼將消息發(fā)送到客戶端,則無法通過實例化 Hub 類實例來執(zhí)行此操作,但可以通過獲取對 Hub 類的 SignalR 上下文對象的引用來執(zhí)行此操作。

注意:ChatHub每次調(diào)用都是一個新的實例,所以不可以有私有屬性或變量,不可以保存對像的值,所以如果需要記錄一些持久保存的值,則可以采用靜態(tài)變量,或者中心以外的對象。

SignalR客戶端

1. 安裝SignalR客戶端依賴庫

客戶端如果要調(diào)用SignalR的值,需要通過NuGet包管理器,安裝SignalR客戶端,如下所示:

2. 客戶端消息接收發(fā)送

在客戶端實現(xiàn)消息的接收和發(fā)送,主要通過HubConntection實現(xiàn),核心代碼,如下所示:

namespace SignalRClient
{
    public class ChatViewModel:ObservableObject
    {
        #region 屬性及構(gòu)造函數(shù)

        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.監(jiān)聽
            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("發(fā)送的消息為空");
                return;
            }
            if (string.IsNullOrEmpty(this.TargetUserName)) {
                MessageBox.Show("發(fā)送的目標用戶為空");
                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>
        /// 監(jiān)聽
        /// </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;
        }
    }
}

運行示例

在示例中,需要同時啟動服務(wù)端和客戶端,所以以多項目方式啟動,如下所示:

運行成功后,服務(wù)端以ASP.NET Web API的方式呈現(xiàn),如下所示:

客戶端需要同時運行兩個,所以在調(diào)試運行啟動一個客戶端后,還要在Debug目錄下,手動雙擊客戶端,再打開一個,并進行登錄,如下所示:

系統(tǒng)運行時,后臺日志輸出如下所示:

到此這篇關(guān)于WPF+ASP.NET SignalR實現(xiàn)簡易在線聊天功能的示例代碼的文章就介紹到這了,更多相關(guān)WPF在線聊天功能內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論