请选择 进入手机版 | 继续访问电脑版

马上加入IBC程序猿 各种源码随意下,各种教程随便看! 注册 每日签到 加入编程讨论群

C#教程 ASP.NET教程 C#视频教程程序源码享受不尽 C#技术求助 ASP.NET技术求助

【源码下载】 社群合作 申请版主 程序开发 【远程协助】 每天乐一乐 每日签到 【承接外包项目】 面试-葵花宝典下载

官方一群:

官方二群:

基于SignalR的服务端和客户端通讯处理

[复制链接]
查看3944 | 回复2 | 2019-11-8 09:56:09 | 显示全部楼层 |阅读模式

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类里的方法.

095626mn2ib6gif2ipvkub.png

SignalR可以将参数序列化和反序列化. 这些参数被序列化的格式叫做Hub 协议, 以是Hub协议就是一种用来序列化和反序列化的格式.

Hub协议的默认协议是JSON, 还支持别的一个协议是MessagePack。MessagePack是二进制格式的, 它比JSON更紧凑, 而且处理起来更简朴快速, 由于它是二进制的.

别的, SignalR也可以扩展利用别的协议。

2、基于SignalR构建的Winform服务端和客户端案例

服务单界面结果如下所示,重要功能为启动服务、制止服务,广播消息和检察毗连客户端信息。

095626pwzf7popxwf7nfsw.png

客户端重要就是实时获取在线用户列表,以及发送、应答消息,消息可以群发,也可以针对特定的客户端举行消息一对一发送。

客户端1:

095627ssagv0519yaaw9gm.png

客户端2:

095627y89ia27r7kw7raca.png

构建的项目工程,包罗服务端、客户端和两个之间的通讯对象类,如下所示。

095628b178gs6hbhv06y6m.png

服务端引用

095628bo33888q3q8cs1zo.png

客户端引用

095629wep7seaudgpsiip2.png

服务端启动代码,想要界说一个Startup类,用来承载SignalR的入口处理。

  1. [assembly: OwinStartup(typeof(SignalRServer.Startup))]
  2. namespace SignalRServer
  3. {
  4. public class Startup
  5. {
  6. public void Configuration(IAppBuilder app)
  7. {
  8. var config = new HubConfiguration();
  9. config.EnableDetailedErrors = true;
  10. //设置可以跨域访问
  11. app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
  12. //映射到默认的管理
  13. app.MapSignalR(config);
  14. }
  15. }
  16. }
复制代码

我们前面先容过,服务端利用Winform步伐来处理它的启动,制止的,如下所示。

095629hdxzza7shdax5fhz.png

因此界面上通过按钮变乱举行启动,启动服务的代码如下所示。

  1. private void btnStart_Click(object sender, EventArgs e)
  2. {
  3. this.btnStart.Enabled = false;
  4. WriteToInfo("正在毗连中....");
  5. Task.Run(() =>
  6. {
  7. ServerStart();
  8. });
  9. }
复制代码

这里通过启动别的一个线程的处理,通过WebApp.Start启动入口类,并传入设置好的端口毗连地点。

  1. /// <summary>
  2. /// 开启服务
  3. /// </summary>
  4. private void ServerStart()
  5. {
  6. try
  7. {
  8. //开启服务
  9. signalR = WebApp.Start<Startup>(serverUrl);
  10. InitControlState(true);
  11. }
  12. catch (Exception ex)
  13. {
  14. //服务失败时的处理
  15. WriteToInfo("服务开启失败,缘故因由:" + ex.Message);
  16. InitControlState(false);
  17. return;
  18. }
  19. WriteToInfo("服务开启乐成 : " + serverUrl);
  20. }
复制代码

毗连地点我们设置在xml文件内里,此中的 serverUrl 就是指向下面的键url, 设置的url如下所示:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <configuration>
  3. <startup>
  4. <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/>
  5. </startup>
  6. <appSettings>
  7. <add key="url" value="http://localhost:17284"/>
  8. </appSettings>
复制代码

制止服务代码如下所示,通过一个异步操纵制止服务。

  1. /// <summary>
  2. /// 制止服务
  3. /// </summary>
  4. /// <returns></returns>
  5. private async Task StopServer()
  6. {
  7. if (signalR != null)
  8. {
  9. //向客户端广播消息
  10. hubContext = GlobalHost.ConnectionManager.GetHubContext<SignalRHub>();
  11. await hubContext.Clients.All.SendClose("服务端已关闭");
  12. //开释对象
  13. signalR.Dispose();
  14. signalR = null;
  15. WriteToInfo("服务端已关闭");
  16. }
  17. }
复制代码

服务端对SignalR客户端的管理是通过一个继承于Hub的类SignalRHub举行管理,这个就是整个SignalR的核心了,它重要有几个函数必要重写,如OnConnected、OnDisconnected、OnReconnected、以及一个通用的消息发送AddMessage函数。

095629u9fprbgwez12cgro.png

客户端有接入的时候,我们会通过参数获取毗连客户端的信息,并同一广播当前客户的状态信息,如下所示是服务端对于接入客户端的管理代码。

  1. /// <summary>
  2. /// 在毗连上时
  3. /// </summary>
  4. public override Task OnConnected()
  5. {
  6. var client = JsonConvert.DeserializeObject<ClientModel>(Context.QueryString.Get("Param"));
  7. if (client != null)
  8. {
  9. client.ConnId = Context.ConnectionId;
  10. //将客户端毗连加入列表
  11. if (!Portal.gc.ClientList.Exists(e => e.ConnId == client.ConnId))
  12. {
  13. Portal.gc.ClientList.Add(client);
  14. }
  15. Groups.Add(client.ConnId, "Client");
  16. //向服务端写入一些数据
  17. Portal.gc.MainForm.WriteToInfo("客户端毗连ID:" + Context.ConnectionId);
  18. Portal.gc.MainForm.WriteToInfo(string.Format("客户端 【{0}】接入: {1} , IP地点: {2} \n 客户端总数: {3}", client.Name, Context.ConnectionId, client.IPAddress, Portal.gc.ClientList.Count));
  19. //先全部毗连客户端广播毗连客户状态
  20. var imcp = new StateMessage()
  21. {
  22. Client = client,
  23. MsgType = MsgType.State,
  24. FromConnId = client.ConnId,
  25. Success = true
  26. };
  27. var jsonStr = JsonConvert.SerializeObject(imcp);
  28. Clients.Group("Client", new string[0]).addMessage(jsonStr);
  29. return base.OnConnected();
  30. }
  31. return Task.FromResult(0);
  32. }
复制代码

客户端的接入,必要对相应的HubConnection变乱举行处理,并初始化干系信息,如下代码所示。

  1. /// <summary>
  2. /// 初始化服务毗连
  3. /// </summary>
  4. private void InitHub()
  5. {
  6. 。。。。。。
  7. //毗连的时候转达参数Param
  8. var param = new Dictionary<string, string> {
  9. { "Param", JsonConvert.SerializeObject(client) }
  10. };
  11. //创建毗连对象,并实现干系变乱
  12. Connection = new HubConnection(serverUrl, param);
  13. 。。。。。。//实现干系变乱
  14. Connection.Closed += HubConnection_Closed;
  15. Connection.Received += HubConnection_Received;
  16. Connection.Reconnected += HubConnection_Succeed;
  17. Connection.TransportConnectTimeout = new TimeSpan(3000);
  18. //绑定一个集线器
  19. hubProxy = Connection.CreateHubProxy("SignalRHub");
  20. AddProtocal();
  21. }
复制代码
  1. private async Task StartConnect()
  2. {
  3. try
  4. {
  5. //开始毗连
  6. await Connection.Start();
  7. await hubProxy.Invoke<CommonResult>("CheckLogin", this.txtUser.Text);
  8. HubConnection_Succeed();//处理毗连后的初始化
  9. 。。。。。。
  10. }
  11. catch (Exception ex)
  12. {
  13. Console.WriteLine(ex.StackTrace);
  14. this.richTextBox.AppendText("服务器毗连失败:" + ex.Message);
  15. InitControlStatus(false);
  16. return;
  17. }
  18. }
复制代码

客户端根据收到的差别协议信息,举行差别的变乱处理,如下代码所示。

  1. /// <summary>
  2. /// 对各种协议的变乱举行处理
  3. /// </summary>
  4. private void AddProtocal()
  5. {
  6. //吸取实时信息
  7. hubProxy.On<string>("AddMessage", DealMessage);
  8. //毗连上触发connected处理
  9. hubProxy.On("logined", () =>
  10. this.Invoke((Action)(() =>
  11. {
  12. this.Text = string.Format("当前用户:{0}", this.txtUser.Text);
  13. richTextBox.AppendText(string.Format("以名称【" + this.txtUser.Text + "】毗连乐成!" + Environment.NewLine));
  14. InitControlStatus(true);
  15. }))
  16. );
  17. //服务端拒绝的处理
  18. hubProxy.On("rejected", () =>
  19. this.Invoke((Action)(() =>
  20. {
  21. richTextBox.AppendText(string.Format("无法利用名称【" + this.txtUser.Text + "】举行毗连!" + Environment.NewLine));
  22. InitControlStatus(false);
  23. CloseHub();
  24. }))
  25. );
  26. //客户端收到服务关闭消息
  27. hubProxy.On("SendClose", () =>
  28. {
  29. CloseHub();
  30. });
  31. }
复制代码

例如我们对收到的文本信息,如一对一的发送消息大概广播消息,同一举行展示处理。

  1. /// <summary>
  2. /// 处理文本消息
  3. /// </summary>
  4. /// <param name="data"></param>
  5. /// <param name="basemsg"></param>
  6. private void DealText(string data, BaseMessage basemsg)
  7. {
  8. //JSON转换为文本消息
  9. var msg = JsonConvert.DeserializeObject<TextMessage>(data);
  10. var ownerClient = ClientList.FirstOrDefault(f => f.ConnId == basemsg.FromConnId);
  11. var ownerName = ownerClient == null ? "体系广播" : ownerClient.Name;
  12. this.Invoke(new Action(() =>
  13. {
  14. richTextBox.AppendText(string.Format("{0} - {1}:\n {2}" + Environment.NewLine, DateTime.Now, ownerName, msg.Message));
  15. richTextBox.ScrollToCaret();
  16. }));
  17. }
复制代码

客户端对消息的处理界面

095630wh7ordkiiykartas.png

而客户端发送消息,则是同一通过调用Hub的AddMessage方法举行发送即可,如下代码所示。

  1. private void BtnSendMessage_Click(object sender, EventArgs e)
  2. {
  3. if (txtMessage.Text.Length == 0)
  4. return;
  5. var message = new TextMessage() {
  6. MsgType = MsgType.Text,
  7. FromConnId = client.ConnId,
  8. ToConnId = this.toId,
  9. Message = txtMessage.Text,
  10. Success = true };
  11. hubProxy.Invoke("AddMessage", JsonConvert.SerializeObject(message));
  12. txtMessage.Text = string.Empty;
  13. txtMessage.Focus();
  14. }
复制代码

此中的hubProxy是我们前面毗连服务端的时候,构造出的一个署理对象

  1. hubProxy = Connection.CreateHubProxy("SignalRHub");
复制代码

客户端关闭的时候,我们烧毁干系的对象即可。

  1. private void Form1_FormClosing(object sender, FormClosingEventArgs e)
  2. {
  3. if (Connection != null)
  4. {
  5. Connection.Stop();
  6. Connection.Dispose();
  7. }
  8. }
复制代码

以上就是SignalR的服务端和客户端的相互共同,相互通讯过程。

C#论坛 www.ibcibc.com IBC编程社区
C#
C#论坛
IBC编程社区
*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则