主頁 > .NET開發 > 基于SignalR的服務端和客戶端通訊處理

基于SignalR的服務端和客戶端通訊處理

2020-09-10 09:53:04 .NET開發

SignalR是一個.NET Core/.NET Framework的實時通訊的框架,一般應用在ASP.NET上,當然也可以應用在Winform上實作服務端和客戶端的訊息通訊,本篇隨筆主要基于SignalR的構建一個基于Winform的服務端和客戶端的通訊處理案例,介紹其中的處理程序,

1、SignalR基礎知識

SignalR是一個.NET Core/.NET Framework的開源實時框架. SignalR的可使用Web Socket, Server Sent Events 和 Long Polling作為底層傳輸方式,

SignalR基于這三種技術構建, 抽象于它們之上, 它讓你更好的關注業務問題而不是底層傳輸技術問題,

SignalR將整個資訊的交換封裝起來,客戶端和服務器都是使用JSON來溝通的,在服務端宣告的所有Hub資訊,都會生成JavaScript輸出到客戶端,.NET則依賴Proxy來生成代理物件,而Proxy的內部則是將JSON轉換成物件,

RPC

RPC (Remote Procedure Call). 它的優點就是可以像呼叫本地方法一樣呼叫遠程服務.

SignalR采用RPC范式來進行客戶端與服務器端之間的通信.

SignalR利用底層傳輸來讓服務器可以呼叫客戶端的方法, 反之亦然, 這些方法可以帶引數, 引數也可以是復雜物件, SignalR負責序列化和反序列化.

 

Hub

Hub是SignalR的一個組件, 它運行在ASP.NET Core應用里. 所以它是服務器端的一個類.

Hub使用RPC接受從客戶端發來的訊息, 也能把訊息發送給客戶端. 所以它就是一個通信用的Hub.

在ASP.NET Core里, 自己創建的Hub類需要繼承于基類Hub,在Hub類里面, 我們就可以呼叫所有客戶端上的方法了. 同樣客戶端也可以呼叫Hub類里的方法.

 

SignalR可以將引數序列化和反序列化. 這些引數被序列化的格式叫做Hub 協議, 所以Hub協議就是一種用來序列化和反序列化的格式.

Hub協議的默認協議是JSON, 還支持另外一個協議是MessagePack,MessagePack是二進制格式的, 它比JSON更緊湊, 而且處理起來更簡單快速, 因為它是二進制的.

此外, SignalR也可以擴展使用其它協議,

 

2、基于SignalR構建的Winform服務端和客戶端案例

服務單界面效果如下所示,主要功能為啟動服務、停止服務,廣播訊息和查看連接客戶端資訊,

 客戶端主要就是實時獲取在線用戶串列,以及發送、應答訊息,訊息可以群發,也可以針對特定的客戶端進行訊息一對一發送,

 客戶端1:

客戶端2:

構建的專案工程,包括服務端、客戶端和兩個之間的通訊物件類,如下所示,

服務端參考

客戶端參考

服務端啟動代碼,想要定義一個Startup類,用來承載SignalR的入口處理,

[assembly: OwinStartup(typeof(SignalRServer.Startup))]namespace SignalRServer{    public class Startup    {        public void Configuration(IAppBuilder app)        {            var config = new HubConfiguration();            config.EnableDetailedErrors = true;            //設定可以跨域訪問            app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);            //映射到默認的管理            app.MapSignalR(config);        }    }}

 我們前面介紹過,服務端使用Winform程式來處理它的啟動,停止的,如下所示,

因此界面上通過按鈕事件進行啟動,啟動服務的代碼如下所示,

        private void btnStart_Click(object sender, EventArgs e)        {            this.btnStart.Enabled = false;            WriteToInfo("正在連接中....");            Task.Run(() =>            {                ServerStart();            });        }

 這里通過啟動另外一個執行緒的處理,通過WebApp.Start啟動入口類,并傳入配置好的埠連接地址,

        /// <summary>        /// 開啟服務        /// </summary>        private void ServerStart()        {            try            {                //開啟服務                signalR = WebApp.Start<Startup>(serverUrl);                InitControlState(true);            }            catch (Exception ex)            {                //服務失敗時的處理                WriteToInfo("服務開啟失敗,原因:" + ex.Message);                InitControlState(false);                return;            }            WriteToInfo("服務開啟成功 : " + serverUrl);        }

連接地址我們配置在xml檔案里面,其中的 serverUrl 就是指向下面的鍵url, 配置的url如下所示:

<?xml version="1.0" encoding="utf-8"?><configuration>    <startup>         <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/>    </startup>  <appSettings>    <add key="url" value="http://localhost:17284"/>  </appSettings>

停止服務代碼如下所示,通過一個異步操作停止服務,

        /// <summary>        /// 停止服務        /// </summary>        /// <returns></returns>        private async Task StopServer()        {            if (signalR != null)            {                //向客戶端廣播訊息                hubContext = GlobalHost.ConnectionManager.GetHubContext<SignalRHub>();                await hubContext.Clients.All.SendClose("服務端已關閉");                //釋放物件                signalR.Dispose();                signalR = null;                WriteToInfo("服務端已關閉");            }        }

服務端對SignalR客戶端的管理是通過一個繼承于Hub的類SignalRHub進行管理,這個就是整個SignalR的核心了,它主要有幾個函式需要重寫,如OnConnected、OnDisconnected、OnReconnected、以及一個通用的訊息發送AddMessage函式,

 

 客戶端有接入的時候,我們會通過引數獲取連接客戶端的資訊,并統一廣播當前客戶的狀態資訊,如下所示是服務端對于接入客戶端的管理代碼,

        /// <summary>        /// 在連接上時        /// </summary>        public override Task OnConnected()        {            var client = JsonConvert.DeserializeObject<ClientModel>(Context.QueryString.Get("Param"));            if (client != null)            {                client.ConnId = Context.ConnectionId;                //將客戶端連接加入串列                if (!Portal.gc.ClientList.Exists(e => e.ConnId == client.ConnId))                {                    Portal.gc.ClientList.Add(client);                }                Groups.Add(client.ConnId, "Client");                //向服務端寫入一些資料                Portal.gc.MainForm.WriteToInfo("客戶端連接ID:" + Context.ConnectionId);                Portal.gc.MainForm.WriteToInfo(string.Format("客戶端 【{0}】接入: {1} ,  IP地址: {2} \n 客戶端總數: {3}", client.Name, Context.ConnectionId, client.IPAddress, Portal.gc.ClientList.Count));                //先所有連接客戶端廣播連接客戶狀態                var imcp = new StateMessage()                {                    Client = client,                    MsgType = MsgType.State,                    FromConnId = client.ConnId,                    Success = true                };                var jsonStr = JsonConvert.SerializeObject(imcp);                Clients.Group("Client", new string[0]).addMessage(jsonStr);                return base.OnConnected();            }            return Task.FromResult(0);        }

客戶端的接入,需要對相應的HubConnection事件進行處理,并初始化相關資訊,如下代碼所示,

        /// <summary>        /// 初始化服務連接        /// </summary>        private void InitHub()        {            ,,,,,,            //連接的時候傳遞引數Param            var param = new Dictionary<string, string> {                { "Param", JsonConvert.SerializeObject(client) }            };            //創建連接物件,并實作相關事件            Connection = new HubConnection(serverUrl, param);            ,,,,,,//實作相關事件            Connection.Closed += HubConnection_Closed;            Connection.Received += HubConnection_Received;            Connection.Reconnected += HubConnection_Succeed;            Connection.TransportConnectTimeout = new TimeSpan(3000);            //系結一個集線器            hubProxy = Connection.CreateHubProxy("SignalRHub");            AddProtocal();        }
        private async Task StartConnect()        {            try            {                //開始連接                await Connection.Start();                await hubProxy.Invoke<CommonResult>("CheckLogin", this.txtUser.Text);                HubConnection_Succeed();//處理連接后的初始化                ,,,,,,            }            catch (Exception ex)            {                Console.WriteLine(ex.StackTrace);                this.richTextBox.AppendText("服務器連接失敗:" + ex.Message);                InitControlStatus(false);                return;            }        }

客戶端根據收到的不同協議資訊,進行不同的事件處理,如下代碼所示,

        /// <summary>        /// 對各種協議的事件進行處理        /// </summary>        private void AddProtocal()        {            //接收實時資訊            hubProxy.On<string>("AddMessage", DealMessage);            //連接上觸發connected處理            hubProxy.On("logined", () =>                this.Invoke((Action)(() =>                {                    this.Text = string.Format("當前用戶:{0}", this.txtUser.Text);                    richTextBox.AppendText(string.Format("以名稱【" + this.txtUser.Text + "】連接成功!" + Environment.NewLine));                    InitControlStatus(true);                }))            );            //服務端拒絕的處理            hubProxy.On("rejected", () =>                this.Invoke((Action)(() =>                {                    richTextBox.AppendText(string.Format("無法使用名稱【" + this.txtUser.Text + "】進行連接!" + Environment.NewLine));                    InitControlStatus(false);                    CloseHub();                }))            );            //客戶端收到服務關閉訊息            hubProxy.On("SendClose", () =>            {                CloseHub();            });        }

例如我們對收到的文本資訊,如一對一的發送訊息或者廣播訊息,統一進行展示處理,

        /// <summary>        /// 處理文本訊息        /// </summary>        /// <param name="data"></param>        /// <param name="basemsg"></param>        private void DealText(string data, BaseMessage basemsg)        {            //JSON轉換為文本訊息            var msg = JsonConvert.DeserializeObject<TextMessage>(data);            var ownerClient = ClientList.FirstOrDefault(f => f.ConnId == basemsg.FromConnId);            var ownerName = ownerClient == null ? "系統廣播" : ownerClient.Name;            this.Invoke(new Action(() =>            {                richTextBox.AppendText(string.Format("{0} - {1}:\n {2}" + Environment.NewLine, DateTime.Now, ownerName, msg.Message));                richTextBox.ScrollToCaret();            }));        }

客戶端對訊息的處理界面

而客戶端發送訊息,則是統一通過呼叫Hub的AddMessage方法進行發送即可,如下代碼所示,

        private void BtnSendMessage_Click(object sender, EventArgs e)        {            if (txtMessage.Text.Length == 0)                return;            var message = new TextMessage() {                MsgType = MsgType.Text,                FromConnId = client.ConnId,                ToConnId = this.toId,                Message = txtMessage.Text,                Success = true };            hubProxy.Invoke("AddMessage", JsonConvert.SerializeObject(message));            txtMessage.Text = string.Empty;            txtMessage.Focus();        }

其中的hubProxy是我們前面連接服務端的時候,構造出的一個代理物件

hubProxy = Connection.CreateHubProxy("SignalRHub");

客戶端關閉的時候,我們銷毀相關的物件即可,

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)        {            if (Connection != null)            {                Connection.Stop();                Connection.Dispose();            }        }

以上就是SignalR的服務端和客戶端的相互配合,相互通訊程序,

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/1714.html

標籤:WinForm

上一篇:Winform中怎樣獲取專案圖片資源并轉換為Image物件

下一篇:收藏收藏:作業用了很久的自主開發的Sql Server代碼生成器,我開源了(.NET Winform)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more