SignalR可以借助Owin擺脫對IIS的依賴,實作Self-Host,使得SignalR有了部署在非Windows平台的可能。
什麼是Owin
Owin的英文全稱是Open Web Interface for .NET, 他定義了Web應用程式和Web伺服器之間的接口。他的作用就是解除了Web應用程式與Web伺服器之間的耦合,進而使Web應用程式不再依賴于具體的Web伺服器,已ASP.NET應用程式為例,以前需要依賴于IIS, 引入Owin之後,他隻依賴與Owin提供的接口,是以所有實作Owin接口的Web伺服器都可以替換掉IIS。
如何在控制台程式中實作SignalR Self-Host
建立一個空的控制台程式

引入SignalR SelfHost包
打開Package Manager Console面闆,輸入以下指令安裝SignalR Self Host包。
Install-package Microsoft.AspNet.SignalR.SelfHost
其他可能需要引入的包
因為如果使用Self-Host, 通常會指定一個獨立的端口或者獨立ip,這樣就會出現跨域的問題,如果出現跨域問題,請引入Owin的CORS包
Install-package Microsoft.Owin.Cors
使用Owin啟動一個Web伺服器
class Program
{
static void Main(string[] args)
{
string url = "http://localhost:9021";
using (WebApp.Start(url))
{
Console.WriteLine("Server running on {0}", url);
Console.ReadKey();
}
}
}
添加Owin啟動類
class Startup
{
public void Configuration(IAppBuilder app)
{
//允許所有域名跨域通路
app.UseCors(CorsOptions.AllowAll);
//啟動SignalR
app.MapSignalR();
}
}
添加Hub代碼
這裡我們可以直接把學習筆記(一)中的Hub代碼直接Copy過來,最終的的Self Host代碼如下
using Microsoft.AspNet.SignalR;
using Microsoft.Owin.Cors;
using Microsoft.Owin.Hosting;
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
namespace SignalRSelfHost
{
class Program
{
static void Main(string[] args)
{
string url = "http://localhost:9021";
using (WebApp.Start(url))
{
Console.WriteLine("Server running on {0}", url);
Console.ReadKey();
}
}
}
class Startup
{
public void Configuration(IAppBuilder app)
{
//允許所有域名跨域通路
app.UseCors(CorsOptions.AllowAll);
//啟動SignalR
app.MapSignalR();
}
}
public class ChatRoomHub : Hub
{
private static Dictionary<string, string> _nickNames = new Dictionary<string, string>();
public void SetNickName(string nickName)
{
//當Hub啟動完畢,每個連接配接到這個Hub的用戶端都會自動配置設定一個唯一的ConnectionId。
//當SignalR向指定用戶端推送消息的時候,需要指定ConnectionId, 是以這裡需要記錄一下每個昵稱對應的用戶端ConnectionId
_nickNames.Add(Context.ConnectionId, nickName);
//當使用者設定昵稱之後,需要發送歡迎資訊到所有的使用者用戶端,調用用戶端receiveWelcomeMessage方法顯示歡迎資訊
Clients.All.ReceiveWelcomeMessage($"{nickName}進入聊天室。");
}
public void Send(string nickName, string message)
{
if (string.IsNullOrWhiteSpace(nickName) || string.IsNullOrWhiteSpace(message))
{
//如果使用者昵稱或者消息不存在,就不做任何操作
return;
}
if (message.StartsWith("to") && message.Split(' ').Length == 3)
{
//私聊消息
var toUserName = message.Split(' ')[1];
if (_nickNames.ContainsValue(toUserName))
{
var connectionId = _nickNames.First(p => p.Value == toUserName).Key;
if (!string.IsNullOrWhiteSpace(connectionId) && connectionId != Context.ConnectionId)
{
Clients.Client(connectionId).ReceivePrivateMessage(nickName, message.Split(' ')[2]);
}
}
}
else
{
//普通廣播消息
if (_nickNames.ContainsValue(nickName))
{
Clients.All.ReceiveBroadcastMessage(nickName, message);
}
}
}
}
}
運作程式
按Ctrl+F5啟動控制台程式, 顯示Server running on xxxxx.表明伺服器啟動成功。
添加聊天室網頁
這裡需要重新建立一個空的Web程式
添加所需SignalR庫
因為SignalR伺服器已經移到了控制台程式當中,是以這裡不需要應用Microsft.AspNet.SignalR庫了
這裡僅需要引入SignalR的用戶端腳本
Install-package Microsoft.AspNet.SignalR.JS
添加聊天網頁
這裡我們将學習筆記(一)的代碼直接copy過來,稍作修改。
- 原先我們引用<script src="signalr/hubs"></script>需要替換為<script src="http://localhost:9021/signalr/hubs"></script>
- 在建立Hub代理之前,需要設定SignalR伺服器的位址
$.connection.hub.url = 'http://localhost:9021/signalr';
最終網頁代碼
<!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>
<script src="Scripts/jquery-1.6.4.min.js"></script>
<script src="Scripts/jquery.signalR-2.2.2.min.js"></script>
<!--<script src="signalr/hubs"></script>-->
<script src="http://localhost:9021/signalr/hubs"></script>
<script type="text/javascript">
$(function () {
$.connection.hub.url = 'http://localhost:9021/signalr';
//使用代理模式, 建立用戶端的hub代理
var chat = $.connection.chatRoomHub;
//伺服器Hub中, 調用ReceiveWelcomeMessage方法時, 會執行用戶端的chat.client.receiveBroadcastMessage方法
chat.client.receiveBroadcastMessage = function (name, message) {
//防JS輸入
var encodedName = $('<div />').text(name).html();
var encodedMsg = $('<div />').text(message).html();
$('#discussion').append('<li><strong>' + encodedName
+ '</strong>: ' + encodedMsg + '</li>');
};
//伺服器Hub中, 調用ReceiveWelcomeMessage方法時, 會執行用戶端的chat.client.receiveWelcomeMessage方法
chat.client.receiveWelcomeMessage = function (message) {
var encodedMsg = $('<div />').text(message).html();
$('#discussion').append('<li><strong style="color:blue">' + encodedMsg
+ '</strong></li>');
};
//伺服器Hub中, 調用ReceivePrivateMessage方法時, 會執行用戶端的chat.client.receivePrivateMessage方法
chat.client.receivePrivateMessage = function (name, message) {
var encodedName = $('<div />').text(name).html();
var encodedMsg = $('<div />').text(message).html();
$('#discussion').append('<li><strong style="color: green">' + encodedName
+ '偷偷的跟你說</strong>: ' + encodedMsg + '</li>');
};
//通過代理連接配接到伺服器Hub
$.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
chat.server.send($('#displayname').val(), $('#message').val());
$('#message').val('').focus();
});
//連接配接成功後, 需要使用者立刻輸入昵稱
$('#displayname').val(prompt('Enter your name:', ''));
chat.server.setNickName($('#displayname').val());
$('#message').focus();
});
});
</script>
</body>
</html>
啟動程式,效果如下
酷炫功能
使用Self-Host之後,我們可以實作一個很酷炫的功能,在控制台程式中監控使用者的輸入
public class ChatRoomHub : Hub
{
private static Dictionary<string, string> _nickNames = new Dictionary<string, string>();
public void SetNickName(string nickName)
{
//當Hub啟動完畢,每個連接配接到這個Hub的用戶端都會自動配置設定一個唯一的ConnectionId。
//當SignalR向指定用戶端推送消息的時候,需要指定ConnectionId, 是以這裡需要記錄一下每個昵稱對應的用戶端ConnectionId
_nickNames.Add(Context.ConnectionId, nickName);
//當使用者設定昵稱之後,需要發送歡迎資訊到所有的使用者用戶端,調用用戶端receiveWelcomeMessage方法顯示歡迎資訊
Clients.All.ReceiveWelcomeMessage($"{nickName}進入聊天室。");
Console.WriteLine($"{nickName}進入聊天室。");
}
public void Send(string nickName, string message)
{
if (string.IsNullOrWhiteSpace(nickName) || string.IsNullOrWhiteSpace(message))
{
//如果使用者昵稱或者消息不存在,就不做任何操作
return;
}
if (message.StartsWith("to") && message.Split(' ').Length == 3)
{
//私聊消息
var toUserName = message.Split(' ')[1];
if (_nickNames.ContainsValue(toUserName))
{
var connectionId = _nickNames.First(p => p.Value == toUserName).Key;
if (!string.IsNullOrWhiteSpace(connectionId) && connectionId != Context.ConnectionId)
{
Clients.Client(connectionId).ReceivePrivateMessage(nickName, message.Split(' ')[2]);
Console.WriteLine($"{nickName}偷偷對{toUserName}說:{message}");
}
}
}
else
{
//普通廣播消息
if (_nickNames.ContainsValue(nickName))
{
Clients.All.ReceiveBroadcastMessage(nickName, message);
Console.WriteLine($"{nickName}對大家說:{message}");
}
}
}
}