最近項目中需要使用SignalR,在此記錄自己初次使用時候的一些問題,避免以后再踩。
主要測試三種模式,BS:客戶端及服務端均在web中來實現。CS 客戶端與服務器端均采用winform的形式。bs和cs混合,服務器端采用winform,客戶端采用web和winrorm兩種模式。
一、B\S
S:創建一個類mvcfhub,繼承Hub。當然,此時需要先在NuGet中獲取SignalR,如下圖:
public class mvcfhub : Hub
{
//將服務端方法Hello重新命名為sendone,
[HubMethodName("sendone")] public void Hello(string message, string connectionid) { if (connectionid != null) { //調用客戶端方法 Clients.Client(connectionid).SendMessage("ID:" + connectionid, message + " 時間:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); } else { Clients.All.SendMessage("ID:" + connectionid, message + " 時間:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); } }
}
安裝Mictosoft.ASP.NET.SignalR,使用的是2.4.0版本。

在安裝此控件的同時會自動添加 用於web端的js文件
。當然此時也會添加owin及其他依賴項,這些都會在安裝上面的dll的時候自動安裝。


待上述dll安裝完畢后,再創建 owin startup 類,(這個需要再研究...)

代碼如下:
using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(typeof(MVCF.Startup))]
namespace MVCF
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
}
至此,服務器端的簡單Hub類創建完成。
web客戶端:
首先需要引用signalR 的js文件,我使用的是MVC bundles經行加載。
//signalR bundles.Add(new ScriptBundle("~/bundles/signalR/js").Include("~/Scripts/jquery.signalR-2.4.0.js"));
關鍵步驟:需要額外再引用自動生成的代理js
<script src="~/signalr/hubs"></script>,后面在cs於bc混合里面,會再提到,和此處有些區別。
創建頁面signalR.cshtml
@{ ViewBag.Title = "signalR"; Layout = null; } <h2>signalR<label id="rstart"></label></h2> <div> 當前在線人數:<label id="users"></label> <select id="userslist"></select> <label id="messageBox"></label> <input type="text" id="message" /> <input type="button" id="sendmessage" value="發送" /> <input type="button" id="stopsignalr" value="斷開連接" /> <input type="button" id="startsignalr" value="重新連接" /> </div> <script> $(document).ready(function () { console.log("a") //引用自動生成的集線器代理 var chat = $.connection.mvcfhub; chat.client.status = function (status) { $("#rstart").text(''); if (status) $("#rstart").text('在線'); } chat.client.getusers = function (userslist) { var selectlist = ""; $("#users").text(''); $("#users").append(userslist.length); $.each(userslist, function (index, name) { selectlist += "<option value=" + userslist[index] + ">" + userslist[index] + "</option>"; }); $("#userslist").html(""); $("#userslist").append(selectlist); } //定義服務器調用的客戶端sendMessage來顯示新消息 chat.client.SendMessage = function (name, message) { //向頁面添加消息 $("#messageBox").append('<li><strong style="color:green">' + name + '</strong>:' + message + '</li>'); } $.connection.hub.connectionSlow(function () { console.log("連接出問題了!"); }); /*重新連接*/ //$.connection.hub.disconnected(function () { // setTimeout(function () { // $.connection.hub.start().done(function () { // console.log("重新連接成功!") // }); // }, 5000); // Restart connection after 5 seconds. //}); // 開始連接服務器 var hubid = ""; $.connection.hub.start().done(function () { hubid = $.connection.hub.id; chat.server.userlist(); $('#sendmessage').on('click', function () { //調用服務器端集線器的Send方法 chat.server.sendone($('#message').val(), $("#userslist").val()); //清空輸入框信息並獲取焦點 $("#message").val('').focus(); }); }); $("#stopsignalr").click(function () { $.connection.hub.stop(hubid); }); $("#startsignalr").click(function () { $.connection.hub.start(); }); }); //$.connection.hub.url = "http://localhost:8889/signalr"; //var chat = $.connection.myhub; //chat.client.addMessage = function (name, message) { // //向頁面添加消息 // $("#messageBox").append('<li><strong style="color:green">' + name + '</strong>:' + message + '</li>'); //} // // 開始連接服務器 // var hubid = ""; // $.connection.hub.start().done(function () { // hubid = $.connection.hub.id; // $('#sendmessage').on('click', function () { // //調用服務器端集線器的Send方法 // chat.server.send( hubid,$('#message').val()); // //清空輸入框信息並獲取焦點 // $("#message").val('').focus(); // }); // }); // $("#stopsignalr").click(function () { // $.connection.hub.stop(hubid); // }); // $("#startsignalr").click(function () { // $.connection.hub.start(); // }); </script>
測試結果:


二、C\S:
利用winform來做服務端,需要額外安裝如下dll,
Microsoft.Owin.Cors
Microsoft.Owin.Hosting
Microsoft.AspNet.SignalR.Client
創建 owin startup 類
using System; using System.Threading.Tasks; using Microsoft.Owin; using Owin; using Microsoft.Owin.Cors; [assembly: OwinStartup(typeof(SignalR_monitoring.Startup))] namespace SignalR_monitoring { public class Startup { public void Configuration(IAppBuilder app) { app.UseCors(CorsOptions.AllowAll); app.MapSignalR(); } } }
創建Hub類,參考BS:
public class myhub : Hub { private static List<Myc> userm; public void Send(string name, string message) { //客戶端調用的方法 Clients.All.addMessage(name, message); } //服務器端 public void testsend(string id, string message) { //客戶端調用方法 Clients.Client(id).mysend(message); } public void Send2(Myc mc) { mc.name = Context.ConnectionId; //調用前端代碼 // Clients.Client(Context.ConnectionId).sendmessage(Context.ConnectionId,message); Clients.All.sendmessage(mc); } /// <summary> /// 客戶端連接服務器成功后調用 /// </summary> /// <returns></returns> public override Task OnConnected() { if (userm == null) { userm = new List<Myc>(); } userm.Add(new Myc { id = Context.ConnectionId, status = true ,t=DateTime.Now}); Clients.All.onlineuser(userm.ToList()); // 在這添加你的代碼. // 例如:在一個聊天程序中,記錄當前連接的用戶ID和名稱,並標記用戶在線. // 在該方法中的代碼完成后,通知客戶端建立連接,客戶端代碼 // start().done(function(){//你的代碼}); return base.OnConnected(); } /// <summary> /// 客戶端斷開連接后調用 /// </summary> /// <param name="stopcalled"></param> /// <returns></returns> public override Task OnDisconnected(bool stopcalled) { if (userm == null) { userm = new List<Myc>(); } userm.Remove((from u in userm where u.id == Context.ConnectionId select u).ToList()[0]); Clients.All.onlineuser(userm.ToList()); // 在這添加你的代碼. // 例如: 標記用戶離線 // 刪除連接ID與用戶的關聯. return base.OnDisconnected(stopcalled); } }
public class Myc
{
public string id { get; set; }
public string name { get; set; }
public bool status { get; set; }
public DateTime t { get; set; }
}
創建一個winform,做為啟動服務的窗口
public partial class Form2 : Form { public Form2() { InitializeComponent(); } public IDisposable SignalR2 { get; set; } private const string ServerUri2 = "http://localhost:8889"; // SignalR服務地址,自定義 private void button1_Click(object sender, EventArgs e) { Task.Run(() => { StartServer(); }); // 異步啟動SignalR服務 label2.Text = "服務啟動成功" + ServerUri2; } private bool StartServer() { try { SignalR2 = WebApp.Start(ServerUri2);
/*下面代碼是為了獲取當前連接的客戶端信息*/ //獲取連接客戶端信息 HubConnection connection = new HubConnection(ServerUri2); IHubProxy rhub = connection.CreateHubProxy("myhub"); connection.Start();//連接服務器 rhub.On<List<Myc>>("onlineuser", onlienuser); } catch (Exception ex) { return false; } return true; } private bool StopServer() { try { SignalR2.Dispose(); } catch (Exception ex) { return false; } return true; } public void onlienuser(List<Myc> ou) { Thread viewthread = new Thread(viewincrease); viewthread.Start(ou); } public void viewincrease(object obj1) { List<Myc> obj = obj1 as List<Myc>; if (label1.InvokeRequired) { Action<string> label = (x) => { this.label1.Text = obj.Count.ToString(); }; label1.Invoke(label, obj.Count.ToString()); } if (listBox1.InvokeRequired) { Action<string> listb = (x) => { this.listBox1.Items.Clear(); }; listBox1.Invoke(listb, ""); foreach (Myc m in obj) { Action<string> listbox = (x) => { this.listBox1.Items.Add("id:" + m.id + " status:" + m.status+" T:"+m.t); }; listBox1.Invoke(listbox, "id:" + m.id + " status:" + m.status + " T:" + m.t); } } } private void button2_Click(object sender, EventArgs e) { label2.Text = "關閉"; Task.Run(() => { StopServer(); }); } }

其中 “onlineuser”方法名必須用hub類中的OnConnected 方法的 客戶端方法名一致。
下面我們來創建一個客戶端,向服務端發送消息

代碼如下:
public partial class Form1 : Form { public Form1() { InitializeComponent(); } HubConnection connection = null; IHubProxy rhub = null; private const string ServerUri = "http://localhost:8889"; private void button1_Click(object sender, EventArgs e) { connection = new HubConnection(ServerUri); //類名必須與服務端一致 //myHub = connection.CreateHubProxy("BroadcastHub"); rhub = connection.CreateHubProxy("myhub"); connection.Start();//連接服務器 label1.Text = "連接服務器成功!"; //注冊客戶端方法名稱"addMessage"與服務器端Send方法對應,對應的 callback方法 ReceiveMsg rhub.On<string, string>("addMessage", ReceiveMsg); } /// <summary> /// 對應的callback方法 /// </summary> /// <param name="name"></param> /// <param name="message"></param> private void ReceiveMsg(string name, string message) { Thread viewthread = new Thread(viewincrease); viewthread.Start("id:" + name + " M:" + message+" Date:"+DateTime.Now); } /// <summary> /// 發送消息 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button3_Click(object sender, EventArgs e) { string m = textBox1.Text; string id = connection.ConnectionId; //調用 hub中的方法 Send rhub.Invoke("Send", id,m).Wait(); } public void viewincrease(object obj) { string message = obj as string; if (listBox1.InvokeRequired) { Action<string> listbox = (x) => { this.listBox1.Items.Add(message); }; listBox1.Invoke(listbox, message); } } }
開始測試
啟動服務

顯示1個客戶端連接。
打開另外一個客戶端

測試完成。連接數為2,客戶端發送test,然后接收到test信息。
三、CS BS 混合
服務端保持不變,web端的js引用需要修改,帶上服務器地址信息,web端修改如下:

web端的引用改為服務端地址。
web頁面 js部分:
$.connection.hub.url = "http://localhost:8889/signalr"; var chat = $.connection.myhub; chat.client.addMessage = function (name, message) { //向頁面添加消息 $("#messageBox").append('<li><strong style="color:green">' + name + '</strong>:' + message + '</li>'); } // 開始連接服務器 var hubid = ""; $.connection.hub.start().done(function () { hubid = $.connection.hub.id; $('#sendmessage').on('click', function () { //調用服務器端集線器的Send方法 chat.server.send( hubid,$('#message').val()); $("#message").val('').focus(); }); }); $("#stopsignalr").click(function () { $.connection.hub.stop(hubid); }); $("#startsignalr").click(function () { $.connection.hub.start(); });
winform客戶端代碼保持不變,測試:

完畢。
