本篇文章主要介紹了Asp.net SignalR來做即時Web聊天實例程式碼,具有一定的參考價值,有興趣的小夥伴們可以參考一下
本章和大家分享的內容是使用Signal R框架創建個簡易的群聊功能,主要講解如何在.Net的MVC中使用這個框架,由於這個專案有官方文件(當然全英文),後面也不打算寫分享篇了,主要目的是讓朋友們在需要使用Web即時通訊的時候有更多一種解決方案,畢竟這是微軟主推的一種解決方案之一。
SignalR網路簡介
#ASP.NET SignalR 是為ASP.NET 開發人員提供的一個函式庫,可以簡化開發人員將即時Web功能添加到應用程式的過程。即時 Web 功能是指這樣一種功能:當所連接的客戶端變得可用時伺服器程式碼可以立即向其推送內容,而不是讓伺服器等待客戶端請求新的資料。
SignalR當然也提供了非常簡單易用的高階API,讓伺服器端可以單一或批次呼叫客戶端上的JavaScript函數,並且非常方便地進行連線管理,例如客戶端連接到伺服器端,或斷開連接,客戶端分組,以及客戶端授權,使用SignalR都非常容易實現。
SignalR將與客戶端進行即時通訊帶給了ASP .NET 。當然這樣既好用,而且也有足夠的擴充性。以前用戶需要刷新頁面或使用Ajax輪詢才能實現的即時顯示數據,現在只要使用SignalR,就可以簡單實現了。最重要的是您無需重新建立項目,使用現有ASP .NET項目即可無縫使用SignalR。
群組聊天實例效果
咋們先來看看測試案例的效果吧,效果圖:
#介面及其簡單,樣式這裡就不考慮了,主要是展示其用法,這裡涉及到的功能有:
1. 統計在線人數量
2. 顯示線上人數的暱稱和連接方式(本測試案例支援webSockets和longPolling(長連接))
3. 群聊資訊
MVC中如何使用SignalR
首先,我們平常做法建立MVC的Web項目,然後透過Nuget控制台指令: Install-package Microsoft.AspNet.SignalR 加入SignalR的依賴,自動新增的套件如下:
<package id="Microsoft.AspNet.SignalR" version="2.2.2" targetFramework="net45" /> <package id="Microsoft.AspNet.SignalR.Core" version="2.2.2" targetFramework="net45" /> <package id="Microsoft.AspNet.SignalR.JS" version="2.2.2" targetFramework="net45" /> <package id="Microsoft.AspNet.SignalR.SystemWeb" version="2.2.2" targetFramework="net45" />
並且自動往MVC專案中的Script資料夾中增加相關的js檔案:
jquery.signalR-2.2. 2.min.js
jquery.signalR-2.2.2.js
然後,我們需要在專案一級目錄建立一個檔案名稱為Startup.cs的類,裡面主要內容補全不下:
[assembly: OwinStartup(typeof(Stage.Api.Extend.Startup))] namespace Stage.Api.Extend { public class Startup { public void Configuration(IAppBuilder app) { app.MapSignalR("/chat", new Microsoft.AspNet.SignalR.HubConfiguration { EnableDetailedErrors = true, EnableJavaScriptProxies = true }); } } }
首先我們從上往下分析注意點:
#1. OwinStartup(Type)
建構子傳遞的是一個類型,而這個類型對應的就是我們所建立的Startup類,這個透過這個建構函式類別標記起始位置;
2. namespace Stage.Api.Extend
是我專案的命名空間,這個可以隨便定義;
3. public void Configuration(IAppBuilder app)
函數是固定必須的,這裡程式會先進入這個方法執行裡面的邏輯程式碼;
4. app.MapSignalR
是擴充的IAppBuilder介面方法,他有多種表現形式,這裡我選擇的是public <a href="http://www.php.cn/wiki/188.html" target="_blank">static</a> IAppBuilder MapSignalR(this IAppBuilder builder, <a href="http://www.php.cn/wiki/57.html" target="_blank">string</a> path, HubConfiguration configuration);
,這裡看有點類似咋們MVC的路由,這裡主要注意的是這個Path參數,在前端頁面的時候需要用到這個路徑;
到這裡後台需要的配置其實已經就完成了,下面是具體需要操作的業務邏輯處理類,新建一個類別(這裡我取名為ChatHub)並繼承Hub(這是SignalR框架提供),然後裡面的邏輯程式碼如下:
public class ChatHub : Hub { // public int Num = 10001; public static List<MoHubUser> _Users = new List<MoHubUser>(); /// <summary> /// 添加聊天人 /// </summary> /// <param name="user"></param> public void AddUser(MoHubUser user) { user.Id = Context.ConnectionId; if (!_Users.Any(b => b.Id == user.Id)) { _Users.Add(user); } //Clients.All.newUser(user); Clients.All.userList(_Users); } /// <summary> /// 发送信息 /// </summary> /// <param name="user"></param> /// <param name="message"></param> public void SendAll(MoHubUser user, string message) { Clients.All.addNewMessageToPage(user, message); } /// <summary> /// 某个聊天人退出是,通知所有在线人重新加载聊天人数 /// </summary> /// <param name="stopCalled"></param> /// <returns></returns> public override System.Threading.Tasks.Task OnDisconnected(bool stopCalled) { var user = _Users.SingleOrDefault(x => x.Id == Context.ConnectionId); if (user != null) { _Users.Remove(user); Clients.All.userList(_Users); } return base.OnDisconnected(stopCalled); } }
上面的3个方法分别做了:添加聊天人到List,发送信息到客户端,某个连接失效后通知各个有效的聊天人重新加载信息;这里值得关注的是通过重新 public override System.Threading.Tasks.Task OnDisconnected(bool stopCalled)
方法来实现捕获并移除失效连接(这里是聊天人的信息);整篇测试用例通过Context.ConnectionId来保证连接的唯一性,只要有新的链接请求,那么服务端就会分配一个唯一串给这个链接,当客户端关闭页面或关闭链接这个是有就能在OnDisconnected方法中捕获到这个失效的链接的ConnectionId,这样就达到了移除失效聊天人的要求了;为了代码的方便性,这里创建了一个聊天人信息类:
public class MoHubUser { public string Id { get; set; } public string NickName { get; set; } public string TransportMethod { get; set; } }
到这里后台的聊天室代码就完成了就是这么简单;我们再来看试图中如何来写代码,这里我先给出我的html布局代码:
@{ ViewBag.Title = "神牛聊天室 - SignalR"; } <style type="text/css"> .p_left { width: 70%; float: left; } .p_right { width: 28%; float: left; } .ul { list-style: none; border: 1px solid #ccc; height: 500px; overflow-y: scroll; color: black; } .ul li { padding-top: 5px; padding-right: 25px; } .ul_user { list-style: none; } .ul_user li { padding-top: 5px; } .send { position: relative; background: #eae7e7; border-radius: 5px; /* 圆角 */ padding-top: 4px; padding-left: 5px; margin-top: 13px; } .send .arrow { position: absolute; top: -16px; font-size: 0; border: solid 8px; border-color: #fff #fff #eae7e7 #fff; } </style> <h3>@ViewBag.Title 在线人数:<span id="sapnUserTotal">0</span>人</h3> <p class="container text-left"> <p class="p_left"> <ul class="ul" id="discussion"></ul> <textarea rows="5" class="form-control" id="message" maxlength="500" placeholder="开始聊天. . ." style="max-width: 100%"></textarea><br /> <input type="button" id="sendmessage" value="发 送" class="btn btn-default" /> <input type="hidden" id="displayname" /> </p> <p class="p_right"> <ul id="users" class="ul_user"></ul> </p> </p> <script src="~/Scripts/jquery.signalR-2.2.2.min.js"></script> @*<script src="~/chat/hubs"></script>*@
客户端这里写法大致有两种选择,一种直接使用生成的代理来操作,一种不用自动生成的代理采用手动创建代理的方式;为了内容的简洁性,这里只简单讲解下自动生成的代理方式,而更多的是分享手动创建代理链接的方式;
使用生成的代理获取链接
首先,我们需要通过Script标签来引用一下自动生成代理的路径: <script src="~/chat/hubs"></script>
,注意啦这里的路径/chat对应的就是咋们在前面Startup.cs文件中配置 app.MapSignalR("/chat"
路径,而后面/hubs固定的写法(至少目前我还没更多的试验过),只有先应用了这个~/chat/hubs,才能在后面使用生成的代理,先上段代码:
var chat = $.connection.chatHub; chat.client.newUser = function (user) { $("#users").append(' <li><strong>' + user.NickName + '[' + user.TransportMethod + ']</strong></li>'); }; chat.client.userList = function (users) { console.log(users); $("#sapnUserTotal").html(users.length); $.each(users, function (i, item) { $("#users").append(' <li>[' + item.TransportMethod + '] <strong>' + item.NickName + '</strong>(' + item.Id + ')</li>'); }); }; chat.client.addNewMessageToPage = function (user, message) { console.log(user); $("#discussion").append(' <li ><span><strong>' + user.NickName + '[' + user.TransportMethod + ']</strong>:</span><p class="send">' + message + '<p class="arrow"></p></p></li>'); }; //connection.hub.start({ transport: ['webSockets', 'longPolling'] }).done(function () { // my.TransportMethod = connection.hub.transport.name; // chat.server.addUser(my); // $('#sendmessage').click(function () { // //console.log(chat); // var content = $.trim($('#message').val()); // if (content.length <= 0) { $('#message').val('').focus(); return; } // chat.server.sendAll(my, content); // $('#message').val('').focus(); // }); //}).fail(function () { // alert("链接聊天室失败,请重新刷新页面。"); //});
咋们逐步来解析下代码注意点:
1. var chat = $.connection.chatHub;
这里的chatHub对应的就是咋们创建的并继承Hub的ChatHub类,由于js变量开头都是小写,所以这里是chatHub,这句活就表示链接到咋们后端了ChatHub类了,然后就可以使用里面的方法啦(这种有点类似于早期的webform中某种ajax请求的写法)
2. 通过chat.client.xxx来接收服务端通知的消息,而这个xxx对应的方法名称和咋们后端的Clients.All.xxx,本章实例对应的是:chat.client.userList = function (users){}对应Clients.All.userList(_Users);这样后端就能直接通知客户端的对应方法了;注意这里我后端Clients.All用的是通知全部客户端的意思,如果您需要通知指定的链接需要用到的是: T Client(string connectionId);
方法
3. chat.client是后端调用客户端,那相对的chat.server就是客户端请求服务端,和上面一样通过chat.server.xxx来指定请求的服务端方法,注意这个时候服务端方法指的是继承类Hub的子类的公共方法(本篇对应的是:chat.server.sendAll(my, content)对应ChatHub.cs文件中的 public void SendAll(MoHubUser user, string message)
函数)
4. connection.hub.start({ transport: ['webSockets', 'longPolling'] })
来指定运行的交互协议
以上就是使用生成代理的方式,按照上面的注意点来写应该不是问题;
使用手动创建反向代理来链接
不采用生成的代码的方式,我们只需要修改前端就行了,后台代码不用变或设置不用变动;首先我们把上面说的Script引用自动代理的代码去掉,因为这个时候不需要了,我们还是先上代码:
var my = { NickName: "神牛001", TransportMethod: "" }; var connection = $.hubConnection("/chat/hubs"); var chat = connection.createHubProxy('chatHub'); chat.on("userList", function (users) { console.log(users); $("#sapnUserTotal").html(users.length); $("#users").html(""); $.each(users, function (i, item) { $("#users").append(' <li>[' + item.TransportMethod + '] <strong>' + item.NickName + '</strong>(' + item.Id + ')</li>'); }); }); chat.on("addNewMessageToPage", function (user, message) { console.log(user); $("#discussion").append(' <li ><span><strong>' + user.NickName + '[' + user.TransportMethod + ']</strong>:</span><p class="send">' + message + '<p class="arrow"></p></p></li>'); var p = document.getElementById('discussion'); //p.scrollTop = p.scrollHeight; p.scrollTop = 999999; }); var nickName = prompt("请输入一个昵称:", my.NickName); my.NickName = nickName.length > 0 ? nickName : my.NickName; $('#displayname').val(nickName); $('#message').focus(); connection.start({ transport: ['webSockets', 'longPolling'] }).done(function () { my.TransportMethod = connection.transport.name; //console.log(my.TransportMethod); chat.invoke("addUser", my); $('#sendmessage').click(function () { var content = $.trim($('#message').val()); if (content.length <= 0) { $('#message').val('').focus(); return; } chat.invoke("sendAll", my, content); $('#message').val('').focus(); }); });
同样列出如下注意点:
1. var connection = $.hubConnection("/chat/hubs");
创建链接,这里的path同样对应后端的 app.MapSignalR("/chat" ,路径保持一致
2. var chat = connection.createHubProxy('chatHub');
来创建反向代理链接,这里的name:chatHub对应的是后端的ChatHub类名称
3. 通过on("xxx",function(){})函数来绑定服务端通知客户端的事件,xxx对应Clients.All.xxx中的xxx
4. connection.start({ transport: ['webSockets', 'longPolling'] })
来设置允许的链接方式,并开始链接
5. connection.transport.name来获取链接的方式名称,默认start不设置的话有这么几种:webSockets,foreverFrame,serverSentEvents,longPolling
6. chat.invoke("xxx", param1, param2)透過invoke來映射繼承類別Hub的類別的公共方法,這裡實例對應的是: chat.invoke("sendAll", my, content )
對應public void SendAll(MoHubUser user, string message)
以上是SignalR實現即時Web聊天的實例程式碼的詳細內容。更多資訊請關注PHP中文網其他相關文章!