一:什么是signalR

Asp.net SignalR是微软为实现实时通信的一个类库。一般情况下,signalR会使用JavaScript的长轮询(long polling)的方式来实现客户端和服务器通信,随着Html5中WebSockets出现,SignalR也支持WebSockets通信。另外SignalR开发的程序不仅仅限制于宿主在IIS中,也可以宿主在任何应用程序,包括控制台,客户端程序和Windows服务等,另外还支持Mono,这意味着它可以实现跨平台部署在Linux环境下。

SignalR将整个信息的交换封装起来,客户端和服务器都是使用JSON来沟通的,在服务端声明的所有Hub信息,都会生成JavaScript输出到客户端,.NET则依赖Proxy来生成代理对象,而Proxy的内部则是将JSON转换成对象。

客户端和服务端的具体交互情况如下图所示:

1、SignalR传输方式

1、HTML5 传输

这种传输方式当然是需要对HTML5兼容支持。如果客户端不支持HTML5 标准,那么你可以使用其他方式。

  • WebSocket: (前提是服务器和客户端都可以支持WebSocket )。WebSocket 是唯一的能够支持服务器和客户端建立真正持久的双向连接的传输方式,但是就是WebSocket要求的条件比较苛刻。WebSocket真正能够支持只是在最新的IE 浏览器、Chrome、Firefox和一些其他的浏览器比如Opera 和 Safari。

  • 服务端发送事件 :就是我们常说的EventSource (除了IE的其他浏览器都支持支持服务器发送事件)。

2、Comet  传输

下面的传输方式都是建立在Comet Web应用模型上。这种传输方式会在浏览器或者其他客户端保持一个长连接的HTTP请求,服务器可以利用这个请求将数据推送到客户端,而不用等客户端单独去请求数据。

  • Forever Frame:(只有IE浏览器支持)Forever Frame 会创建一个隐藏的IFrame ,这个IFrame 会向服务器发起一个端点请求,但是这个请求不会结束。服务器会不停的发送脚本到客户端马上执行,提供一个从服务器端到客户端的单向实时连接。服务端到客户端和客户端到服务端分别是两个不同的连接,就像一个标准的HTTP请求,一旦有数据需要发送就会创建一个新的连接。

  • Ajax 长轮询:长轮询不会创建一个持久的连接,它是会保持一个客户端与服务器的连接,直到服务器做出应答就会立即关闭,然后创建一个新的连接。当连接重置的时候这会导致一些延迟。

2、SignalR连接模式

  1. Http Persisten Connection(持久连接)对象:用来解决长时间连接的功能。还可以由客户端主动向服务器要求数据,而服务器端不需要实现太多细节,只需要处理PersistentConnection 内所提供的五个事件:OnConnected, OnReconnected, OnReceived, OnError 和 OnDisconnect 即可。
  2. Hub(集线器)对象:用来解决实时(realtime)信息交换的功能,服务端可以利用URL来注册一个或多个Hub,只要连接到这个Hub,就能与所有的客户端共享发送到服务器上的信息,同时服务端可以调用客户端的脚本。SignalR将整个信息的交换封装起来,客户端和服务器都是使用JSON来沟通的,在服务端声明的所有Hub信息,都会生成JavaScript输出到客户端,.NET则依赖Proxy来生成代理对象,而Proxy的内部则是将JSON转换成对象。

下面的图展示了Hub和持久连接的关系,让你对底层的传输技术一目了然。

二、服务端

原文参考:教程:通过 SignalR 2 进行实时聊天

使用vs2012创建一个新的ASP.NET Web Application项目。


1、添加服务端Hub

解决方案中添加SignalR Hub类选项,然后创建文件ChatHub.cs。此步骤将创建ChatHub.cs类文件并添加一组脚本文件和 SignalR 支持到项目的程序集引用。

注意:可以使用NuGet命令 install-package Microsoft.AspNet.SignalR 来引用SignalR到项目中

然后在ChatHub类中输入下面这段代码。

using Microsoft.AspNet.SignalR;

namespace WebApplication2
{
public class ChatHub : Hub
{
//定义可以被网页脚本访问的公共方法
public void Send(string name, string message) //在客户端可以使用hub.server.send()方法调用。
{
// 客户端通过调用broadcastMessage来获取服务端数据
Clients.All.broadcastMessage(name, message);//在客户端通过hub.client.broadcastMessage=function(){…}定义这个回调函数。
}
}
}

ChatHub类中,可以看到它继承自Microsoft.AspNet.SignalR.Hub类。从Hub类派生出类是构建SignalR应用程序的有用方式。在这个类(ChatHub)中定义的公共方法可以被网页内的脚本访问。

在这段代码中(ChatHub类),客户端通过调用ChatHub.Send方法,并把名称和消息内容做为参数传递给ChatHub。而ChatHub则通过Clients.All.broadcastMessage方法把该消息广播给所有客户端。

Send方法演示了几个Hub概念:

  • 在hub上声明一些公共方法,以便客户端可以调用它们。
  • 使用Microsoft.AspNet.SignalR.Hub。客户端动态属性用于与连接到此中心的所有客户端进行通信。
  • 调用客户机上的函数(如broadcastMessage函数)来更新客户机。

2、添加OWIN 启动类

Solution Explorer中,右击项目,然后添加一个名为StartupOWIN 启动类,然后输入下面这段代码。

using Microsoft.Owin;
using Owin; [assembly: OwinStartup(typeof(WebApplication2.Startup))]
namespace WebApplication2
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
}

三、客户端

在项目中添加一个静态网页文件,并命名为index.html,然后右击该网页文件,并选择Set As Start Page,在Index.html中输入下面的代码,注意引用正确的文件名。

<!DOCTYPE html>
<html>
<head>
<title>SignalR Simple Chat</title>
<style type="text/css">
.container {
background-color: #99CCFF;
border: thick solid #808080;
padding: 20px;
margin: 20px;
}
</style>
</head>
<body>
<div class="container">
<input type="text" id="message" />
<input type="button" id="sendmessage" value="Send" /> <input type="hidden" id="displayname" />
<ul id="discussion"></ul>
</div> <!--Reference the jQuery library. -->
<script src="Scripts/jquery-1.12.4.js"></script>
<!--Reference the SignalR library. -->
<script src="Scripts/jquery.signalR-2.4.1.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="signalr/hubs"></script>
<!--Add script to update the page and send messages.-->
<script type="text/javascript">
$(function () {
// 1、声明一个代理,用于引用服务端hub
var chat = $.connection.chatHub;
            // 2、创建一个回调函数,让Hub调用它来广播消息。
            chat.client.broadcastMessage = function (name, message) {
// Html编码形式显示名称和消息
                var encodedName = $('<div />').text(name).html();
var encodedMsg = $('<div />').text(message).html();;
// 将消息添加到页面。
                $('#discussion').append('<li><strong>' + encodedName
+ '</strong>:&nbsp;&nbsp;' + encodedMsg + '</li>');
};
// 获取用户名
            $('#displayname').val(prompt('Enter your name:', ''));
// 将初始焦点设置为消息输入框。
            $('#message').focus();
            // 3、开始连接和发送消息
            $.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
// 在Hub上调用Send方法.
chat.server.send($('#displayname').val(), $('#message').val());
// 清除文本框并为下一个注释重置焦点。
                    $('#message').val('').focus();
});
});
});
</script>
</body>
</html>

客户端通过jquery.signalR.jssignalr/hubs来与服务器进行通信,首先它要声明一个代理来引用集线器。

var chat = $.connection.chatHub

请注意:在JavaScript中,对服务器类及其成员的引用必须是camelCase形式。本例中将C#服务端的ChatHub类在JavaScript中的引用为chatHub。

然后它要再定义一个回调函数,这个回调函数主要是为了让服务器进行调用,从而将数据推送到客户端。

chat.client.broadcastMessage = function (name, message) {
//TODO:接收服务器推送的消息
};

而下面的代码则是为了确保在将消息发送到服务器之前已经与服务器建立了连接。.done 函数表示连接成功后为发送的按钮绑定一个单击事件。

// 与服务器建立连接后才能发送消息到服务器
$.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
// 调用服务器端的ChatHub.Send方法,把消息发送到服务器
chat.server.send("name", "message");
});
});

保存项目,并按F5运行,就可以实现B/S模式下的即时通讯。

四、运行结果

项目运行起来后,同时用多个浏览器打开的,输入各自的姓名之后,就能够实现即时通讯了。回到我们的vs,还能够看到自动生成的hubs脚本文件,如下图所示。

三个不同的浏览器中的运行方式。 当 Tom、 Anand 和 Susan 发送消息时,所有浏览器实时更新:

在ServerHub重写一个 OnConnected 方法来监控客户端的连接情况,程序运行的时候web页面就使用$.connection.hub.start() 与signalR服务开始建立连接了,在调试的时候可以在输入中看到 "客户端连接成功!"

public override Task OnConnected()
{
System.Diagnostics.Trace.WriteLine("客户端连接成功!");
return base.OnConnected();
}

五、一对一聊天实例

Clients.Client(connectionId).addMessage() 此方法的作用就是客户端注册addMessage方法,向指定连接Id的客户端发送消息。一对一的聊天发送的消息也必须回发给自己,所以连接的Id可以通过Context.ConnectionId来获取。当然不用Client.Client(Context.ConnectionId) ,也可以使用Client.Caller()方法直接发送。

Client.Clients(IList<string> connectionIds) 这个方法的意思就是想一组string 的几个ConnectionId发送消息。类似于QQ上@好友的那种功能。

using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks; namespace WebApplication2
{
[HubName("UserHub")]
public class UserHub : Hub
{
#region 字段 public static List<User> users = new List<User>(); #endregion 字段 #region 方法 //获取所有用户在线列表
private void GetUsers()
{
var list = users.Select(s => new { s.Name, s.ConnectionID }).ToList();
string jsonList = JsonConvert.SerializeObject(list);
Clients.All.getUsers(jsonList);
}

        //登记名字
public void LoginIn(string name)
{
//查询用户
var user = users.SingleOrDefault(u => u.ConnectionID == Context.ConnectionId);
if (user != null)
{
user.Name = name;//登记名字
Clients.Client(Context.ConnectionId).showId(Context.ConnectionId);
}
GetUsers();
} //发送消息
[HubMethodName("sendMessage")]
public void SendMessage(string connectionId, string message)
{
Clients.All.hello();
var user = users.Where(s => s.ConnectionID == connectionId).FirstOrDefault();
if (user != null)
{
Clients.Client(connectionId).addMessage(message + "" + DateTime.Now, Context.ConnectionId);
//给自己发送,把用户的ID传给自己
Clients.Client(Context.ConnectionId).addMessage(message + "" + DateTime.Now, connectionId);
}
else
{
Clients.Client(Context.ConnectionId).showMessage("该用户已离线");
}
} /// <summary>
/// 重写连接事件
/// </summary>
/// <returns></returns>
public override Task OnConnected()
{
//查询用户
var user = users.Where(w => w.ConnectionID == Context.ConnectionId).SingleOrDefault();
//判断用户是否存在,否则添加集合
if (user == null)
{
user = new User("", Context.ConnectionId);
users.Add(user);
}
return base.OnConnected();
} public override Task OnDisconnected(bool stopCalled)
{
var user = users.Where(p => p.ConnectionID == Context.ConnectionId).FirstOrDefault();
//判断用户是否存在,存在则删除
if (user != null)
{
//删除用户
users.Remove(user);
}
GetUsers();//获取所有用户的列表
return base.OnDisconnected(stopCalled);
} #endregion 方法
} public class User
{
#region 构造函数 public User(string name, string connectionId)
{
this.Name = name;
this.ConnectionID = connectionId;
} #endregion 构造函数 #region 属性 [Key]
public string ConnectionID { get; set; } public string Name { get; set; } #endregion 属性
}
}

聊天室具体实例: signalr中Group 分组群发消息的简单使用

MVC中使用SignalR打造酷炫实用的即时通讯功能附源码

SignalR入门一、通过 SignalR 2 进行实时聊天的更多相关文章

  1. 使用signalr实现网页和微信公众号实时聊天(上)

    最近项目中需要实现客户在公众号中和客服(客服使用后台网站系统)进行实时聊天的功能.折腾了一段时间,实现了这个功能.现在将过程记录下,以便有相同需求的同行可以参考,也是自己做个总结.这篇是上,用手机编辑 ...

  2. ASP.Net Core 3.1 使用实时应用SignalR入门

    参考文章:微软官方文档:https://docs.microsoft.com/zh-cn/aspnet/core/signalr/introduction?view=aspnetcore-3.1 和 ...

  3. [置顶] MVC中使用signalR入门教程

    一.前言:每次写总要说一点最近的感想 进入工作快半年了,昨天是最郁闷的一天,我怀疑我是不是得了"星期一综合征",每个星期一很没有状态.全身都有点酸痛,这个可能一个星期只有周末才打一 ...

  4. SignalR 入门 .netCore实现聊天室

    SignalR 入门 .netCore实现聊天室 本文根据微软SignalR 简介 | Microsoft Docs 和 ASP.NET Core SignalR 简介 | Microsoft Doc ...

  5. SignalR实现网页实时聊天功能

    SignalR是利用html5 sokit方式实现网页的实时性,在客户端不支持html5的情况下通过轮询实现 实现原理是客户端发送的消息先去服务器,然后服务器根据需要将消息广播到需要接收信息的客户群. ...

  6. 使用SignalR+Asp.net创建实时聊天应用程序

    一.概述: 使用 ASP.NET 那么 SignalR 2 创建一个实时聊天应用程序.将 SignalR 添加 MVC 5 应用程序中,并创建聊天视图发送并显示消息. 在Demo中,将学习Signal ...

  7. 利用SignalR实现实时聊天

    2018/10/10:博主第一次写原创博文而且还是关于C#的(博主是从前端转过来的),菜鸟一枚,如果有什么写的不对,理解错误,还望各位轻喷.,从SignalR开始! 首先先介绍一下关于SignalR的 ...

  8. SignalR入门之多平台SignalR服务端

    之前创建SignalR服务端是基于Web应用程序而言的.那么能不能把SignalR服务端做成控制台应用程序.Winform或windows服务呢? 答案是肯定的. 之前尽管看起来好像是IIS和ASP. ...

  9. SignalR系列教程:SignalR快速入门

    ---恢复内容开始--- 本篇是SignalR系列教程的第一篇,本篇内容介绍了如何创建SignalR应用,如何利用SignalR搭建简易的聊天室等,本篇内容参考自:http://www.asp.net ...

随机推荐

  1. spring框架是怎么样通过properties来获得对象的?

    首先我们要知道java获得对象的方式有四种: 1.通过new语句实例化一个对象. 2.通过反射机制创建对象. 3.通过clone()方法创建对象 3.通过反序列化的方式创建对象 在spring框架中, ...

  2. vscode 前端常用插件推荐

    1.  vscode 简介vscode是微软开发的的一款代码编辑器,就如官网上说的一样,vscode重新定义(redefined)了代码编辑器.当前市面上常用的轻型代码编辑器主要是:sublime,n ...

  3. react-navigation 的抽屉效果 createDrawerNavigator (DrawerNavigator)

    一.前言: react-navigation  3.x 版本中, 使用createDrawerNavigator 替换 原先的DrawerNavigator 方法: 那么,当前createBottom ...

  4. 引用和自包含令牌(Reference Tokens and Introspection)

    访问令牌可以有两种形式:自包含的和引用的. 自包含令牌(Self-contained tokens): 使用受保护的.有时间限制的数据结构,该结构包含元数据,并声明通过网络传递用户或客户机的身份.一种 ...

  5. Spring-Cloud之Zuul路由网关-6

    一.为什么需要Zuul? Zuul 作为微服务系统的网关组件,用于构建边界服务( Edge Service ),致力于动态路由.过滤.监控.弹性伸缩和安全.Zuul 作为路由网关组件,在微服务架构中有 ...

  6. C# vb .net实现透明特效滤镜

    在.net中,如何简单快捷地实现Photoshop滤镜组中的透明效果呢?答案是调用SharpImage!专业图像特效滤镜和合成类库.下面开始演示关键代码,您也可以在文末下载全部源码: 设置授权 第一步 ...

  7. pandas.to_datetime() 只保留【年-月-日】

    Outline pandas.to_datetime()  生成的日期会默认带有 [2019-07-03 00:00:00]的分钟精度:但有时并不需要这些分钟精度: 去掉分钟精度 可以通过pandas ...

  8. AWS--Lamdba

    分享一个Lambda相关的连接 https://blog.csdn.net/m0_37204491/article/details/72829477

  9. Python进阶(十)----软件开发规范, time模块, datatime模块,random模块,collection模块(python额外数据类型)

    Python进阶(十)----软件开发规范, time模块, datatime模块,random模块,collection模块(python额外数据类型) 一丶软件开发规范 六个目录: #### 对某 ...

  10. 两种方法实现在HTML页面加载完毕后运行JS

    JS默认方法: <script type=”text/javascript”> window.onload=function (){ /*代码区域*/ } </script> ...