使用 SignalR 在 MVC 中构建聊天应用程序






4.08/5 (6投票s)
在这里,我将演示一个使用 SignalR 进行聊天和私聊的应用程序。在此之前,我们需要先了解 SignalR!SignalR 是一个开放且免费的库,可用于将实时功能集成到您的 Web 应用程序中。
引言
在这里,我将演示一个使用 SignalR 进行聊天和私聊的应用程序。
在此之前,我们需要先了解 SignalR!SignalR 是一个开放且免费的库,可用于将实时功能集成到您的 Web 应用程序中。SignalR 在许多方面都能派上用场,可以让您的应用程序更便捷、更集成,并且对最终用户更具响应性。实时意味着服务器在客户端发出请求时,尽快作出响应。
现在,例如,我们有一个需求,需要向用户显示文件上传的进度,即在服务器上已上传文件的百分比。或者我遇到过这样的场景,我们需要向最终用户显示,他正在上传一个包含“n”行数据的 CSV 文件,并且需要处理每一行以进行某些验证。最终用户会想知道后端正在做什么。那么,如果我们能让他看到已处理了多少行,还剩多少行,那不是很棒吗!这就像一个进度窗口。这时,SignalR 就派上用场了!
我们大多数人认为 SignalR 在制作聊天应用程序方面很有用,但事实并非如此!它的功能远不止聊天!我认为 SignalR 的开发者可能并没有想过用它来制作聊天应用程序!
现在故事讲得差不多了!让我们进入一些理论知识!
理论
我们将看下面的一个简单图片,并从中尝试获得一些关于流程的知识。
如今,每个应用程序都需要大量的实时服务器响应才能在市场上立足,因为用户期望值已经很高了。
远程过程调用 (RPC) 是 SignalR 内部执行的概念。SignalR 提供了一个 API,有助于在服务器和客户端之间进行 RPC。实际上,从客户端的角度来看,一旦建立了与服务器的连接,就可以使用 JavaScript 调用服务器端函数。SignalR API 还可以创建连接并在需要时管理它们。简单来说,SignalR 在服务器和客户端之间提供连接,允许服务器调用客户端的函数,并允许客户端调用服务器的函数。这某种程度上被称为“服务器推送”。
SignalR 从 HTTP 开始,然后如果连接可用,则升级到 WebSocket。摘自维基百科
“WebSocket 是一种协议,通过单个 TCP 连接提供全双工通信通道。”
使用 WebSocket 的一个优势是它同时被客户端和服务器应用程序使用。WebSocket 被认为是最高效和最一致的通信媒介,因为它能够妥善管理服务器内存,并且作为全双工通信,具有低延迟。这些是 SignalR 考虑到的因素,使其更加高效。
SignalR 根据浏览器决定传输方式,即浏览器是否支持 SignalR 所需的传输类型。接下来我们将讨论传输类型。
HTML 5 传输
- WebSocket 正如我们已经讨论过的。如果浏览器支持,则这种传输被认为是真正的持久化,在客户端和服务器之间创建双向连接。
- 服务器发送事件 也称为 Event Source,除 IE 外,所有浏览器都支持。
Comet 传输
Comet 通常是一种 Web 应用程序模型,其中一个长时间保持的 HTTP 请求允许服务器将数据推送到客户端(浏览器)。
- Forever frame 仅由 Internet Explorer 支持。它会创建一个隐藏的框架,向服务器上的端点发出请求。服务器会持续 ping 客户端或将脚本发送到客户端,从而提供单向连接,并在每次请求时创建一个新连接。
- Ajax Polling 这实际上是长轮询,它永远不是持久化的。它会轮询请求,并一直等到收到服务器的响应。这会引入延迟,并且连接会重置。
实践
我们将创建一个聊天应用程序来解释 SignalR 的流程。我们安装 SignalR,创建一个客户端将与之交互的中心 (hub),调用服务器方法,然后服务器进行响应并与客户端交互。
您可以在 VS 中直接为 SignalR 添加一个新项目,或者创建一个 MVC 项目并从 Nuget 安装 SignalR 包/库。
PM > Install-Package Microsoft.AspNet.SignalR
这将下载 SignalR 所需的所有依赖项。
成功安装后,上述 dll 或包将安装到您的项目中。
需要将一个类文件添加到项目根目录,它看起来会是这样
using Owin;
using Microsoft.Owin;
[assembly: OwinStartup(typeof(SignalRChat.Startup))]
namespace SignalRChat
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
// Any connection or hub wire up and configuration should go here
app.MapSignalR();
}
}
}
这是一个基于 OWIN 的应用程序。每个 OWIN 应用程序都有一个 startup.cs 类,其中添加了应用程序管道的组件。指定属性类型的 OWIN 属性,指定项目的启动和配置方法,会设置 App 的 SignalR 映射。
当我们安装 SignalR 的包时,还会添加另外两个脚本文件。
为了激活 SignalR,这些脚本文件必须加载到 .cshtml 页面上。
我们直接来看代码吧。
我们需要在 Hub 文件夹中添加一个新的 hub 类。我们将其命名为 LetsChatHub.cs,它看起来会是这样
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.SignalR;
namespace LetsChatApplication.Hubs
{
public class LetsChatHub : Hub
{
public void Send(string name, string message,string connId)
{
Clients.Client(connId).appendNewMessage(name, message);
}
}
}
上面的 send 方法接受参数 name(您在导航到 url 时会提供)、message(用户将从 UI 发送的消息)。另一个参数是 connId,它将帮助我们进行私聊,而不发送给浏览该网站的所有用户。如果您希望所有用户都能接收和发送发送到 URL 的消息。要允许所有用户访问,请将代码更改为如下所示
namespace LetsChatApplication.Hubs
{
public class LetsChatHub : Hub
{
public void Send(string name, string message)
{
Clients.All.appendNewMessage(name, message);
}
}
}
Send 方法是从客户端请求的,带有参数,然后在客户端建立连接后,服务器接收到请求,进行处理,并使用 appendNewMessage 将响应发送回客户端。这个 appendNewMessage 方法被添加到客户端以接收响应并在客户端的 UI 上显示。
您需要添加一个控制器,假设是:“LetsChat”,并带有一个名为“LetsChat”的操作,为它添加一个视图,代码如下。
客户端代码看起来如下
@{
ViewBag.Title = "LetsChat";
}
<h2>Lets Chat</h2>
<link href="~/Content/sweetalert.css" rel="stylesheet" />
<div class="form-group col-xl-12">
<label class="control-label">Your connection Id</label><br />
<input type="text" class="col-lg-12 text-primary" id="frndConnId" placeholder="Paste your friend's connection Id" /><br /><br />
<label class="control-label">Your Message</label><br />
<textarea type="text" class="col-lg-10 text-primary" id="message"></textarea>
<input type="button" class="btn btn-primary" id="sendmessage" value="Send" /><br /><br />
<img src="~/Content/smile.jpg" width="20" height="20" id="smile" style="cursor:pointer"/>
<img src="~/Content/uff.jpg" width="20" height="20" id="ufff" style="cursor:pointer" />
<div class="container chatArea">
<input type="hidden" id="displayname" />
<ul id="discussion"></ul>
</div>
</div>
<br />
<input type="hidden" id="connId" />
<!--Reference the autogenerated SignalR hub script. -->
@section scripts {
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Content/sweetalert.min.js"></script>
<script src="~/Scripts/jquery.signalR-2.2.0.min.js"></script>
<script src="~/signalr/hubs"></script>
<script>
//var userName = "";
//var sessionVal = '';
$(function () {
// Reference the auto-generated proxy for the hub.
var chat = $.connection.letsChatHub;
debugger;
// Create a function that the hub can call back to display messages.
chat.client.addNewMessageToPage = function (name, message) {
// Add the message to the page.
$('#discussion').append('<li><strong>' + htmlEncode(name)
+ '</strong>: ' + htmlEncode(message) + '</li>');
};
// Get the user name and store it to prepend to messages.
swal({
title: "Lets Chat!",
text: "<span style='color:#f8bb86;font-weight:700;'>Enter your name:</span>",
type: "input",
html: true,
showCancelButton: true,
closeOnConfirm: true,
animation: "slide-from-top",
inputPlaceholder: "Your Name"
},
function (inputValue) {
userName = inputValue;
if (inputValue === false) return false;
if (inputValue === "") {
swal.showInputError("You need to type your name!");
return false;
}
$('#displayname').val(inputValue);
});
// Set initial focus to message input box.
$('#message').focus();
$('#message').keypress(function (e) {
if (e.which == 13) {//Enter key pressed
$('#sendmessage').trigger('click');//Trigger search button click event
}
});
$("#smile").click(function () {
});
// Start the connection.
$.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
// Call the Send method on the hub.
var connId = $("#connId").val();
var frndConnId = $("#frndConnId").val();
var finalConnId = frndConnId == "" ? $.connection.hub.id : frndConnId;
chat.server.send($('#displayname').val(), $('#message').val(), finalConnId);
$("#connId").val($.connection.hub.id);
if (frndConnId == "") {
swal("You connection Id", $.connection.hub.id, "success");
}
// Clear text box and reset focus for next comment.
$('#discussion').append('<li><strong>' + htmlEncode($('#displayname').val())
+ '</strong>: ' + htmlEncode($('#message').val()) + '</li>');
$('#message').val('').focus();
});
});
});
// This optional function html-encodes messages for display in the page.
function htmlEncode(value) {
var encodedValue = $('<div />').text(value).html();
return encodedValue;
}
</script>
}
Lets Chat
我们有一个普通的 UI,用于添加消息和发送按钮来调用服务器方法。
让我们逐部分理解上面的代码。
var chat = $.connection.letsChatHub;
在这里,我们设置了到 Hub 类的连接。正如您在这里注意到的,letsChatHub 是我们在设置服务器时添加的同一个 hub 类文件名。惯例如下,方法或类名的首字母都从小写开始。从这里,我们使用 chat 来访问 Send 方法。
$.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
// Calls the Send method on the hub.
chat.server.send($('#displayname').val(), $('#message').val(), finalConnId);
chat.server.send,不言而喻,它设置了 chat 连接,以便在连接建立和启动后调用服务器的 Send 方法。
chat.client.appendNewMessage = function (name, message) {
//
}
当服务器收到请求并调用客户端上的方法时,就会触发此操作。
示例如何工作!
下载提供的示例将包含一些说明。
- 当您导航到 LetsChat/LetsChat 路由时,会弹出一个提示框,询问您要使用的聊天名称。Sweet Alert !!
- 输入名称后,您会看到一个文本框,询问“您朋友的连接 ID”,但由于您正在开始聊天,只需发送消息,然后会弹出另一个提示框,显示您的连接 ID,您需要与朋友分享,以便分享您的消息。请记住,只有拥有您的 ID 并在使用它聊天的人才能看到您并向您发送消息。
- 当您的朋友导航过来时,他必须生成他的连接 ID 并与您分享,以便完全建立您的连接。因此,您需要拥有 connID 才能发送给谁,反之亦然,与您的朋友一样。然后就可以聊天了!!
如果您想向所有人发送消息,并使其成为公共的,则使用 Clients.All 代码片段向所有人发送。
另一个有趣的事情是,@section scripts{} 的使用,它可以让 SignalR 脚本在您的页面上渲染和激活,并且使用 @section scripts 还能让您的代码看起来更整洁。
使用 SignalR 分享和发送文件!!
哎呀!!一个不错的问题,对吧!
理想情况下,不建议,或者我不会推荐使用 Signal R 发送或共享文件。总有更好的方法可以实现这一点。这种想法是使用 API,您可以有一个上传区域,并使用 SignalR 显示进度,上传完成后,向用户更新完成信息,并在 UI 上为用户生成下载链接以下载和查看文件。
这并不总是最好的方法,但只是另一种想法。
结论
这只是一个简单的聊天应用程序,如果您将其托管在 Azure 或其他域上,就可以与朋友聊天。但是,SignalR 的功能不止于此。SignalR 还有很多其他有效的用途。我将在接下来的几篇文章中分享更多 SignalR 的实用性。