一、前言
SignalR是微軟推出的開源實時通信框架。其內部使用Web Socket, Server Sent Events 和 Long Polling作為底層傳輸方式,SignalR會根據客戶端和服務端的支持情況,采用回落機制來選擇一種傳輸方式,Web Socket是首選的。在web開發中,SignalR可以很好的解決傳統ajax輪詢的問題,真正做到實時通信。
二、編碼
- 首先創建2個項目,一個控制台項目,一個web項目。控制台項目作為SignalR服務端,web項目作為客戶端。
- 先從服務端開始:
安裝NuGet包
控制台程序作為宿主實現自托管,需要安裝:Microsoft.AspNet.SignalR.SelfHost
添加跨域支持:Microsoft.Owin.Cors
- 服務端SignalRServer.Program代碼:
using Microsoft.AspNet.SignalR;
using Microsoft.Owin.Cors;
using Microsoft.Owin.Hosting;
using Owin;
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
namespace SignalRServer
{
class Program
{
static void Main(string[] args)
{
var url = "http://localhost:8019";
using (WebApp.Start(url))
{
Console.WriteLine("SignalR運行:" + url);
Console.WriteLine("輸入要發送的消息,用戶與消息之間用空格隔開:");
var hub = GlobalHost.ConnectionManager.GetHubContext<CustomHub>();
while (true)
{
var str = Console.ReadLine();
hub.Clients.Client(CustomHub.OnLineUsers[str.Split(' ')[0]]).refreshData(str.Split(' ')[1]);
}
}
}
}
public class CustomHub : Hub
{
//在線用戶
public static ConcurrentDictionary<string, string> OnLineUsers = new ConcurrentDictionary<string, string>();
public override Task OnConnected()
{
string clientName = Context.QueryString["clientName"].ToString();
OnLineUsers.AddOrUpdate(clientName, Context.ConnectionId, (key, value) =>
{
return Context.ConnectionId;
});
Console.WriteLine($"{clientName}:{Context.ConnectionId}已連接。");
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
string clientName = Context.QueryString["clientName"].ToString();
string client;
OnLineUsers.TryRemove(clientName, out client);
Console.WriteLine($"{clientName}:{Context.ConnectionId}已斷開。");
return base.OnDisconnected(stopCalled);
}
public override Task OnReconnected()
{
string clientName = Context.QueryString["clientName"].ToString();
OnLineUsers.AddOrUpdate(clientName, Context.ConnectionId, (key, value) =>
{
return Context.ConnectionId;
});
Console.WriteLine($"{clientName}:{Context.ConnectionId}已重連。");
return base.OnReconnected();
}
}
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR();
}
}
}
以上代碼大致就是指定一個url啟動SignalR服務,CustomHub中維護了一個在線客戶端集合,控制台根據輸入的信息發送到對應的客戶端...
- 下面是客戶端
同樣先安裝NuGet包
安裝Microsoft.AspNet.SignalR.JS
- 創建2個視圖User1,User2,用來代表2個不同用戶
User1.cshtml代碼,支持斷線重連:
@{
ViewBag.Title = "Index";
Layout = null;
}
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.signalR-2.4.1.min.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script id="signalr_script" src="http://localhost:8019/signalr/hubs"></script>
<script type="text/javascript">
$(function () {
//連接Signalr服務器
signalrInit();
});
//signalr初始化配置
function signalrInit() {
try {
//Set the hubs URL for the connection
$.connection.hub.url = 'http://localhost:8019/signalr';
$.connection.hub.qs = { "clientName": "user1" };
// Declare a proxy to reference the hub.
var chat = $.connection.customHub;
// Create a function that the hub can call to broadcast messages.
chat.client.refreshData = function (message) {
//TODO:收到服務端數據
appendMessage(message);
};
signalrConnection();
$.connection.hub.disconnected(function () {
appendMessage('SignalR連接斷開!');
//重連
setTimeout(signalrConnection, 10000);
});
//$.connection.hub.stateChanged(function (change) { console.log(change); });
} catch (e) {
appendMessage("SignalR連接異常" + e);
//重新加載hubs
$.getScript('http://localhost:8019/signalr/hubs');
//重新初始化 斷線重連
setTimeout(signalrInit, 10000);
}
}
//signalr連接
function signalrConnection() {
$.connection.hub.start()
.done(function () { appendMessage('SignalR建立連接成功'); })
.fail(function () { appendMessage('SignalR建立連接失敗'); });
}
function appendMessage(message) {
$("#box").append("<p>" + message + "</p>");
}
</script>
<h1>User1</h1>
<div id="box">
</div>
User2.cshtml代碼:
@{
ViewBag.Title = "Index";
Layout = null;
}
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.signalR-2.4.1.min.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script id="signalr_script" src="http://localhost:8019/signalr/hubs"></script>
<script type="text/javascript">
$(function () {
//連接Signalr服務器
signalrInit();
});
//signalr初始化配置
function signalrInit() {
try {
//Set the hubs URL for the connection
$.connection.hub.url = 'http://localhost:8019/signalr';
$.connection.hub.qs = { "clientName": "user2" };
// Declare a proxy to reference the hub.
var chat = $.connection.customHub;
// Create a function that the hub can call to broadcast messages.
chat.client.refreshData = function (message) {
//TODO:收到服務端數據
appendMessage(message);
};
signalrConnection();
$.connection.hub.disconnected(function () {
appendMessage('SignalR連接斷開!');
//重連
setTimeout(signalrConnection, 10000);
});
//$.connection.hub.stateChanged(function (change) { console.log(change); });
} catch (e) {
appendMessage("SignalR連接異常" + e);
//重新加載hubs
$.getScript('http://localhost:8019/signalr/hubs');
//重新初始化 斷線重連
setTimeout(signalrInit, 10000);
}
}
//signalr連接
function signalrConnection() {
$.connection.hub.start()
.done(function () { appendMessage('SignalR建立連接成功'); })
.fail(function () { appendMessage('SignalR建立連接失敗'); });
}
function appendMessage(message) {
$("#box").append("<p>" + message + "</p>");
}
</script>
<h1>User2</h1>
<div id="box">
</div>
三、效果
- 服務端和客戶端的代碼已完成,現在先運行一下服務端控制台程序。右鍵編譯好的SignalRServer.exe,以管理員身份運行(最好是管理員身份運行,不然可能會報錯)。
- 運行客戶端,瀏覽器分別打開User1,User2頁面
客戶端已經顯示連接成功
服務端也顯示2個已連接 - 向User1發送消息:
- 向User2發送消息:
四、總結
以上就是SignalR的基本使用。
有一些可能的坑:
1.服務端控制台啟動報錯:嘗試用管理員身份啟動
2.服務端啟動正常,客戶端卻無法連接,報錯Cannot read property 'client' of undefined:有時候可能是端口的問題,換一個端口試一下
3.程序放到服務器,通過外網IP無法連接:WebApp.Start()嘗試使用*號格式:http://*:8019