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}"); } } } }