记录一次SignalR服务端实现过程
前言:最近手上一个项目需要后端实时推送数据到前端,第一个想到的就是微软的SignalR,由于之前都是平时没事写的Demo,没有用到实际项目中,这次恰好用到了,因此记录下来整个实现过程(网上也有很多类似的教程,写的不好,请指正出来)
本文源码下载:https://download.csdn.net/download/baidu_24578765/10490052
一、项目准备
1、新建一个MVC空项目(过程不在赘述)
2、添加SignalR引用,通过Nuget安装SignalR包。右键引用-》选择管理Nuget程序包-》在出现的窗口中输入SignalR来找到SignalR包进行安装
3、安装SignalR成功后,SignalR库的脚本将被添加进Scripts文件夹下。具体如下图所示:

4、安装Microsoft.Owin以及Microsoft.Owin.Cors,安装方式同前
二、项目构建
首先在Models文件夹下面新建一个SignalR集线器(v2)并命名为ChatsHub

然后继续新建接口IChatClient以及IChatService,在ChatsHub.cs中添加以下代码:
[HubName("ChatsHub")]
public class ChatsHub : Hub<IChatClient>, IChatService
{
#region 基础信息
/// <summary>
/// 前端自定义参数集合
/// </summary>
public INameValueCollection ClientQueryString
{
get { return Context.QueryString; }
}
/// <summary>
/// Cookie
/// </summary>
public IDictionary<string, Cookie> ClientCookies
{
get { return Context.RequestCookies; }
}
/// <summary>
/// 用户信息
/// </summary>
public IPrincipal ClientContextUser
{
get { return Context.User; }
}
/// <summary>
/// SignalR上下文
/// </summary>
public HttpContext HttpContext
{
get { return HttpContext.Current; }
}
#endregion
#region 测试代码
/// <summary>
/// 向所有客户端发送消息
/// </summary>
/// <param name="message"></param>
public async Task Send(string message)
{
try
{
//当前发送消息的用户ID,前端自定义
//string userId = ClientQueryString["userId"];
//当前连接ID
string connId = Context.ConnectionId;
//调用所有客户端的SendMessage方法
ChatMessageDTO msg = new ChatMessageDTO
{
SendId = connId,
SendUserName = "",
Content = message,
CreateDate = DateTime.Now
};
await Clients.All.SendMessage(msg);
}
catch (Exception e)
{
throw new HubException("发送消息发生异常.", new { userName = ClientContextUser.Identity.Name, message = e.Message });
}
}
#endregion
#region 默认事件
/// <summary>
/// 客户端连接的时候调用
/// </summary>
/// <returns></returns>
public override Task OnConnected()
{
//string userId = ClientQueryString["userId"];
Trace.WriteLine("客户端连接成功,连接ID是: " + Context.ConnectionId);
return base.OnConnected();
}
/// <summary>
/// 客户端断开连接的时候调用
/// </summary>
/// <param name="stopCalled"></param>
/// <returns></returns>
public override Task OnDisconnected(bool stopCalled)
{
Trace.WriteLine($"客户端[{Context.ConnectionId}]断开连接");
return base.OnDisconnected(true);
}
/// <summary>
/// 客户端重新连接的时候调用
/// </summary>
/// <returns></returns>
public override Task OnReconnected()
{
Trace.WriteLine($"客户端[{Context.ConnectionId}]正在重新连接");
return base.OnReconnected();
}
#endregion
}
IChatClient.cs代码:
/// <summary>
/// 客户端方法
/// </summary>
public interface IChatClient
{
#region 用于测试
/// <summary>
/// 测试方法
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
Task SendMessage(ChatMessageDTO message);
#endregion }
IChatService.cs代码
/// <summary>
/// 服务端方法
/// </summary>
public interface IChatService
{
#region 基础信息
/// <summary>
/// 前端自定义参数集合
/// </summary>
INameValueCollection ClientQueryString { get; }
/// <summary>
/// Cookie
/// </summary>
IDictionary<string, Cookie> ClientCookies { get; }
/// <summary>
/// 用户信息
/// </summary>
IPrincipal ClientContextUser { get; }
/// <summary>
/// SignalR上下文
/// </summary>
HttpContext HttpContext { get; }
#endregion #region 用于测试
/// <summary>
/// 发送消息,测试方法
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
Task Send(string message);
#endregion
}
至此,一个基础的Hub就建立好了,然后继续在App_Start中新建Startup.cs,如下图:

在Startup中添加以下代码:
[assembly: OwinStartup(typeof(SignalRService.Startup))]
namespace SignalRService
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
//异常处理
GlobalHost.HubPipeline.AddModule(new ErrorHandlingPipelineModule()); //自定义管道
GlobalHost.HubPipeline.AddModule(new LoggingPipelineModule()); //允许跨域推送
app.UseCors(CorsOptions.AllowAll); app.MapSignalR();
}
}
}
添加自定义异常处理机制,新建一个ErrorHandlingPipelineModule类,这里只做了简单的实现,具体的可以根据你自己的需求进行处理:
/// <summary>
/// 自定义异常处理
/// </summary>
public class ErrorHandlingPipelineModule : HubPipelineModule
{
protected override void OnIncomingError(ExceptionContext exceptionContext, IHubIncomingInvokerContext invokerContext)
{
Trace.WriteLine("=> Exception " + exceptionContext.Error.Message);
if (exceptionContext.Error.InnerException != null)
{
Trace.WriteLine("=> Inner Exception " + exceptionContext.Error.InnerException.Message);
}
base.OnIncomingError(exceptionContext, invokerContext);
}
}
新增日志处理管道LoggingPipelineModule类:
public class LoggingPipelineModule : HubPipelineModule
{
protected override bool OnBeforeIncoming(IHubIncomingInvokerContext context)
{
Trace.WriteLine("=> Invoking " + context.MethodDescriptor.Name + " on hub " + context.MethodDescriptor.Hub.Name);
return base.OnBeforeIncoming(context);
}
protected override bool OnBeforeOutgoing(IHubOutgoingInvokerContext context)
{
Trace.WriteLine("<= Invoking " + context.Invocation.Method + " on client hub " + context.Invocation.Hub);
return base.OnBeforeOutgoing(context);
}
}
上述类建立好了以后,结构如下:

实体类ChatMessageDTO.cs代码:
public class ChatMessageDTO
{
/// <summary>
/// 发送人ID
/// </summary>
public string SendId { get; set; }
/// <summary>
/// 发送方姓名
/// </summary>
public string SendUserName { get; set; }
/// <summary>
/// 内容
/// </summary>
public string Content { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreateDate { get; set; }
}
至此,一个简单的SignalR服务端就搭建好了。接下来就是前端调用服务测试,同样,新建一个空的MVC项目,把刚刚安装SignalR时自动添加的jquery.signalR-2.2.3.min.js拷贝到现在项目中,前端实现代码:
添加一个输入框:
<div class="container">
<input type="text" id="message" />
<input type="button" id="sendmessage" value="发送消息" />
<ul id="discussion"></ul>
</div>
JS代码:
<!--引用SignalR库. -->
<script src="~/Scripts/jquery.signalR-2.2.3.min.js"></script>
<!--引用自动生成的SignalR 集线器(Hub)脚本.在运行的时候在浏览器的Source下可看到 -->
<script src="http://你的地址/signalr/hubs"></script>
<script>
$(function () {
// 引用自动生成的集线器代理
var chat = $.connection.ChatsHub;
$.connection.hub.url = 'http://你的地址/signalr/hubs';
//自定义用户ID
$.connection.hub.qs = { "userId": "110" }; //启用浏览器端输出日志
//$.connection.hub.logging = true; // 定义服务器端调用的客户端SendMessage来显示新消息
chat.client.SendMessage = function (req) {
// 向页面添加消息
$('#discussion').append('<li><strong>' + htmlEncode(req.SendId) + '</strong>: ' + htmlEncode(req.Content) + '</li>');
};
// 设置焦点到输入框
$('#message').focus(); // Start the connection.
$.connection.hub.starting(function () {
console.log('SignalR启动中...')
});
$.connection.hub.received(function (e) {
//console.log(e)
console.log('SignalR收到消息:\n')
var msgBody = e.A;
//console.log(msgBody)
if (msgBody)
console.log("用户ID:" + msgBody[0].SendId + ",消息内容:" + msgBody[0].Content)
});
$.connection.hub.connectionSlow(function () {
console.log('connectionSlow.')
});
$.connection.hub.reconnecting(function () {
console.log('SignalR正在重新连接中...')
});
$.connection.hub.reconnected(function () {
console.log('SignalR重新连接成功...')
});
$.connection.hub.stateChanged(function (o, n) {
//console.log(o)
console.log('SignalR状态改变,newState:' + o.newState + ",oldState:" + o.oldState + "," + n)
});
$.connection.hub.disconnected(function () {
console.log('SignalR断开连接...');
setTimeout(function () {
$.connection.hub.start();
}, 1000); // 1秒后重新连接
});
//错误
$.connection.hub.error(function (err) {
console.log("SignalR出现错误. \n" + "Error: " + err.message);
}); // 开始连接服务器
$.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
var msg = $('#message').val();
// 调用服务器端集线器的Send方法
chat.server.send(msg).fail(function (e) {
if (e.source === 'HubException') {
console.log("异常信息:" + e.message + ",用户名:" + e.data.userName + ",错误描述:" + e.data.message);
}
});
// 清空输入框信息并获取焦点
$('#message').val('').focus();
});
});
}); // 为显示的消息进行Html编码
function htmlEncode(value) {
var encodedValue = $('<div />').text(value).html();
return encodedValue;
}
</script>
在运行前,先启动服务端,然后修改js代码中服务器地址,再运行客户端,效果图如下:

三、后序
参考文章:https://docs.microsoft.com/en-us/aspnet/signalr/overview/guide-to-the-api/hubs-api-guide-server
https://www.cnblogs.com/aaaaq/p/5929104.html
特别提醒:服务端signalR版本必须与客户端版本一致。
记录一次SignalR服务端实现过程的更多相关文章
- SignalR入门之多平台SignalR服务端
之前创建SignalR服务端是基于Web应用程序而言的.那么能不能把SignalR服务端做成控制台应用程序.Winform或windows服务呢? 答案是肯定的. 之前尽管看起来好像是IIS和ASP. ...
- 创建自托管的SignalR服务端
微软官方例子地址:http://www.asp.net/signalr/overview/deployment/tutorial-signalr-self-host 1.说明: SignalR服务端可 ...
- Android BLE与终端通信(三)——客户端与服务端通信过程以及实现数据通信
Android BLE与终端通信(三)--客户端与服务端通信过程以及实现数据通信 前面的终究只是小知识点,上不了台面,也只能算是起到一个科普的作用,而同步到实际的开发上去,今天就来延续前两篇实现蓝牙主 ...
- Netty 服务端启动过程
在 Netty 中创建 1 个 NioServerSocketChannel 在指定的端口监听客户端连接,这个过程主要有以下 个步骤: 创建 NioServerSocketChannel 初始化并注 ...
- Centos 6.5 pptpd服务端搭建过程
首先检测有没有启用ppp和tun cat /dev/ppp cat /dev/net/tun 如果显示是这样的 cat: /dev/ppp: No such device or address cat ...
- Android BLE与终端通信(三)——client与服务端通信过程以及实现数据通信
Android BLE与终端通信(三)--client与服务端通信过程以及实现数据通信 前面的终究仅仅是小知识点.上不了台面,也仅仅能算是起到一个科普的作用.而同步到实际的开发上去,今天就来延续前两篇 ...
- Netty源码解析 -- 服务端启动过程
本文通过阅读Netty源码,解析Netty服务端启动过程. 源码分析基于Netty 4.1 Netty是一个高性能的网络通信框架,支持NIO,OIO等多种IO模式.通常,我们都是使用NIO模式,该系列 ...
- .net core signalR 服务端强制中断用户连接
.net core signalR 服务端断开连接 { } { } *:first-child { } *:last-child { } { } { } { } { } { } { } { } { } ...
- zookeeper源码分析之一服务端启动过程
zookeeper简介 zookeeper是为分布式应用提供分布式协作服务的开源软件.它提供了一组简单的原子操作,分布式应用可以基于这些原子操作来实现更高层次的同步服务,配置维护,组管理和命名.zoo ...
随机推荐
- [2019牛客多校第三场][G. Removing Stones]
题目链接:https://ac.nowcoder.com/acm/contest/883/G 题目大意:有\(n\)堆石头,每堆有\(a_i\)个,每次可以选其中两堆非零的石堆,各取走一个石子,当所有 ...
- MySQL8 设置大小写 正确步骤
在安装完成之后,初始化数据库之前,修改 my.cnf 打开mysql配置文件vim /etc/my.cnf 在尾部追加一行lower_case_table_names=1并保存,然后再初始化数据库. ...
- 网络_05 STP HSRP
一.案例1:STP的基本配置 将S1配置成vlan1的主根,将S2配置成vlan2的次根 查看生成树的配置:show spanning-tree 查看某个vlan生成树的详细信息:show spann ...
- Public model for matrix
以下是可以加减乘除(就是乘逆矩阵啦)以及求若干次幂.行列式和逆的矩阵模板. 欢迎大家指正其中可能存在的错误(只验证了求逆的正确性). 顺便提一下这种复杂度低于定义式求逆的方法,来自于我的高等代数书,思 ...
- Luogu [P3622] [APIO2007]动物园
题目链接 比较费脑子的一道题 先说题目核心思想 : 状压dp 环的处理我们先不管. 我们设 dp[j][s] 表示 到达动物 j 且 [ j , j+5) 这五个动物状态为s时 最多能使多少小朋友开心 ...
- 7月清北学堂培训 Day 1
今天是林永迪老师的讲授~ 基础算法 1. 模拟算法 面向测试算法 模拟算法的关键就是将人类语言翻译成机器语言. 要做到以下两点: 1.优秀的读题能力: 2.优秀的代码能力: 程序流程图: 读入,循环处 ...
- bash基础之三配置文件
一.shell的两种登录方式: 1.交互式登录:(1)直接通过终端输入账号密码登录(2)使用“su - UserName” 或“su -l Username”切换的用户执行顺序:/etc/profil ...
- spring-boot-starter-test
1.约定 单元测试代码写在src/test/java目录下 单元测试类命名为*Test,前缀为要测试的类名 2. 使用mock方式单元测试 Spring测试框架提供MockMvc对象,可以在不需要客户 ...
- mybatis中foreach参数过多效率很慢的优化
foreach 后面in 传入的参数有1万条,#和$是有效率区别的,$的效率远高于#,上篇文章做了比较. 但没达到我的理想结果. 1. 更改方式,把foreach 去掉,改成拼装方式, 参数直接拼装成 ...
- abd shell pm list packages
abd shell pm list packages ####查看当前连接设备或者虚拟机的所有包 adb shell pm list packages -d #####只输出禁用的包. adb she ...