SignalR的三個Demo

1、理解SignalRjavascript

ASP .NET SignalR 是一個ASP .NET 下的類庫,能夠在ASP .NET 的Web項目中實現實時通訊(即:客戶端(Web頁面)和服務器端能夠互相實時的通知消息及調用方法),SignalR有三種傳輸模式:LongLooping(長輪詢)、WebSocket(HTML5的WEB套接字)、Forever Frame(隱藏框架的長請求鏈接),能夠在WEB客戶端顯式指定一種或幾種,也能夠採起默認(推薦),若採起默認,SignalR會根據瀏覽器的環境自動選擇合適的傳輸方式。css

2、SignalR的三種實現方式html

第一種:採用集線器類(Hub)+非自動生成代理模式:服務端與客戶端分別定義的相對應的方法,客戶端經過代理對象調用服務端的方法,服務端經過IHubConnectionContext回調客戶端的方法,客戶端經過回調方法接收結果。java

以前我寫過一篇文章《分享一個基於長鏈接+長輪詢+原生的JS及AJAX實現的多人在線即時交流聊天室》,是經過長輪詢+長鏈接的方式來實現的在線多人聊天室功能,從代碼量來看就知道實現起來並不簡單,而現在有了SignalR,會簡單不少,我這裏使用SignalR再來寫一個簡單的在線多人聊天室示例,以便你們快速掌握SignalR。jquery

DEMO - 1 示例代碼以下:瀏覽器

服務端:服務器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
//Startup類文件
 
using  System;
using  System.Threading.Tasks;
using  Microsoft.Owin;
using  Owin;
using  Microsoft.AspNet.SignalR;
 
[assembly: OwinStartup( typeof (TestWebApp.Models.Startup))]
 
namespace  TestWebApp.Models
{
     public  class  Startup
     {
         public  void  Configuration(IAppBuilder app)
         {
             app.MapSignalR();
         }
     }
}
 
 
//ChatHub類文件
 
using  Microsoft.AspNet.SignalR;
using  Microsoft.AspNet.SignalR.Hubs;
using  System;
using  System.Collections.Concurrent;
using  System.Collections.Generic;
using  System.Linq;
using  System.Web;
 
namespace  TestWebApp.Models
{
     [HubName( "chat" )]
     public  class  ChatHub : Hub
     {
         public  static  ConcurrentDictionary< string string > OnLineUsers =  new  ConcurrentDictionary< string string >();
 
         [HubMethodName( "send" )]
         public  void  Send( string  message)
         {
             string  clientName = OnLineUsers[Context.ConnectionId];
             message = HttpUtility.HtmlEncode(message).Replace( "\r\n" "<br/>" ).Replace( "\n" "<br/>" );
             Clients.All.receiveMessage(DateTime.Now.ToString( "yyyy-MM-dd HH:mm:ss" ), clientName, message);
         }
 
         [HubMethodName( "sendOne" )]
         public  void  Send( string  toUserId,  string  message)
         {
             string  clientName = OnLineUsers[Context.ConnectionId];
             message = HttpUtility.HtmlEncode(message).Replace( "\r\n" "<br/>" ).Replace( "\n" "<br/>" );
             Clients.Caller.receiveMessage(DateTime.Now.ToString( "yyyy-MM-dd HH:mm:ss" ),  string .Format( "您對 {1}" , clientName, OnLineUsers[toUserId]), message);
             Clients.Client(toUserId).receiveMessage(DateTime.Now.ToString( "yyyy-MM-dd HH:mm:ss" ),  string .Format( "{0} 對您" , clientName), message);
         }
          
         public  override  System.Threading.Tasks.Task OnConnected()
         {
             string  clientName = Context.QueryString[ "clientName" ].ToString();
             OnLineUsers.AddOrUpdate(Context.ConnectionId, clientName, (key, value) => clientName);
             Clients.All.userChange(DateTime.Now.ToString( "yyyy-MM-dd HH:mm:ss" ),  string .Format( "{0} 加入了。" , clientName), OnLineUsers.ToArray());
             return  base .OnConnected();
         }
 
         public  override  System.Threading.Tasks.Task OnDisconnected( bool  stopCalled)
         {
             string  clientName = Context.QueryString[ "clientName" ].ToString();
             Clients.All.userChange(DateTime.Now.ToString( "yyyy-MM-dd HH:mm:ss" ),  string .Format( "{0} 離開了。" , clientName), OnLineUsers.ToArray());
             OnLineUsers.TryRemove(Context.ConnectionId,  out  clientName);
             return  base .OnDisconnected(stopCalled);
         }
 
     }
}

 

1
2
3
4
5
6
7
8
public  ActionResult Index()
{
     ViewBag.ClientName =  "聊客-"  + Guid.NewGuid().ToString( "N" );
     var  onLineUserList = ChatHub.OnLineUsers.Select(u =>  new  SelectListItem() { Text = u.Value, Value = u.Key }).ToList();
     onLineUserList.Insert(0,  new  SelectListItem() { Text =  "-全部人-" , Value =  ""  });
     ViewBag.OnLineUsers = onLineUserList;
     return  View();
}

 

WEB客戶端:app

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
<!DOCTYPE html>
 
<html>
<head>
     <meta name= "viewport"  content= "width=device-width"  />
     <meta charset= "utf-8"  />
     <title>聊天室</title>
     <script src= "~/Scripts/jquery-1.6.4.min.js"  type= "text/javascript" ></script>
     <script src= "~/Scripts/jquery.signalR-2.2.0.min.js"  type= "text/javascript" ></script>
     <style type= "text/css" >
         #chatbox {
             width: 100%;
             height: 500px;
             border: 2px solid blue;
             padding: 5px;
             margin: 5px 0px;
             overflow-x: hidden;
             overflow-y: auto;
         }
 
         .linfo {
         }
 
         .rinfo {
             text-align: right;
         }
     </style>
     <script type= "text/javascript" >
         $(function () {
 
             var  clientName = $( "#clientname" ).val();
             var  eChatBox = $( "#chatbox" );
             var  eUsers = $( "#users" );
 
             var  conn = $.hubConnection();
             conn.qs = {  "clientName" : clientName };
 
 
             conn.start().done(function () {
 
                 $( "#btnSend" ).click(function () {
                     var  toUserId = eUsers.val();
                     if  (toUserId !=  "" ) {
                         chat.invoke( "sendOne" , toUserId, $( "#message" ).val())
                         .done(function () {
                             //alert("發送成功!");
                             $( "#message" ).val( "" ).focus();
                         })
                         .fail(function (e) {
                             alert(e);
                             $( "#message" ).focus();
                         });
                     }
                     else  {
                         chat.invoke( "send" , $( "#message" ).val())
                         .done(function () {
                             //alert("發送成功!");
                             $( "#message" ).val( "" ).focus();
                         })
                         .fail(function (e) {
                             alert(e);
                             $( "#message" ).focus();
                         });
                     }
                 });
 
             });
 
             var  chat = conn.createHubProxy( "chat" );
 
             chat. on ( "receiveMessage" , function (dt, cn, msg) {
                 var  clsName =  "linfo" ;
                 if  (cn == clientName || cn.indexOf( "您對" ) >= 0) clsName =  "rinfo" ;
                 eChatBox.append( "<p class='"  + clsName +  "'>"  + dt +  " <strong>"  + cn +  "</strong> 說:<br/>"  + msg +  "</p>" );
                 eChatBox.scrollTop(eChatBox[0].scrollHeight);
             });
 
             chat. on ( "userChange" , function (dt, msg, users) {
                 eChatBox.append( "<p>"  + dt +  " "  + msg +  "</p>" );
                 eUsers.find( "option[value!='']" ).remove();
                 for  ( var  i = 0; i < users.length; i++) {
                     if  (users[i].Value == clientName)  continue ;
                     eUsers.append( "<option value='"  + users[i].Key +  "'>"  + users[i].Value +  "</option>" )
                 }
             });
 
 
 
         });
     </script>
</head>
<body>
     <h3>大衆聊天室</h3>
     <div id= "chatbox" >
     </div>
     <div>
         <span>聊天名稱:</span>
         @Html.TextBox( "clientname" , ViewBag.ClientName  as  string new  { @ readonly  "readonly" , style =  "width:300px;"  })
         <span>聊天對象:</span>
         @Html.DropDownList( "users" , ViewBag.OnLineUsers  as  IEnumerable<SelectListItem>)
     </div>
     <div>
         @Html.TextArea( "message" new  { rows = 5, style =  "width:500px;"  })
         <input type= "button"  value= "發送消息"  id= "btnSend"  />
     </div>
</body>
</html>

服務端與客戶端代碼都比較簡單,網上相關的說明也有,這裏就再也不解說了,只說一下這種方式JS端調用服務端方法採用:chat.invoke,而被服務端回調的方法則採用:chat.on (這裏的chat是createHubProxy建立得來的)框架

第二種:採用集線器類(Hub)+自動生成代理模式ide

DEMO - 2 示例代碼以下:

服務端與DEMO 1相同,無需改變

客戶端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
<! DOCTYPE  html>
 
< html >
< head >
     < meta  name="viewport" content="width=device-width" />
     < meta  charset="utf-8" />
     < title >聊天室</ title >
     < script  src="~/Scripts/jquery-1.6.4.min.js" type="text/javascript"></ script >
     < script  src="~/Scripts/jquery.signalR-2.2.0.min.js" type="text/javascript"></ script >
     < script  src="~/signalr/hubs" type="text/javascript"></ script >
     < style  type="text/css">
         #chatbox {
             width: 100%;
             height: 500px;
             border: 2px solid blue;
             padding: 5px;
             margin: 5px 0px;
             overflow-x: hidden;
             overflow-y: auto;
         }
 
         .linfo {
         }
 
         .rinfo {
             text-align: right;
         }
     </ style >
     < script  type="text/javascript">
         $(function () {
 
             var clientName = $("#clientname").val();
             var eChatBox = $("#chatbox");
             var eUsers = $("#users");
 
             var chat = $.connection.chat;
             $.connection.hub.qs = { "clientName": clientName };
             chat.state.test = "test";
 
             chat.client.receiveMessage = function (dt, cn, msg) {
                 var clsName = "linfo";
                 if (cn == clientName || cn.indexOf("您對")>=0) clsName = "rinfo";
                 eChatBox.append("< p  class='" + clsName + "'>" + dt + " < strong >" + cn + "</ strong > 說:< br />" + msg + "</ p >");
                 eChatBox.scrollTop(eChatBox[0].scrollHeight);
             }
 
             chat.client.userChange = function (dt, msg, users) {
                 eChatBox.append("< p >" + dt + " " + msg + "</ p >");
                 eUsers.find("option[value!='']").remove();
                 for (var i = 0; i <  users.length ; i++) {
                     if (users[i].Value == clientName) continue;
                     eUsers.append("<option value='" + users[i].Key + "'>" + users[i].Value + "</ option >")
                 }
             }
 
             $.connection.hub.start().done(function () {
 
                 $("#btnSend").click(function () {
                     var toUserId = eUsers.val();
                     if (toUserId != "") {
                         chat.server.sendOne(toUserId, $("#message").val())
                             .done(function () {
                                 //alert("發送成功!");
                                 $("#message").val("").focus();
                             })
                             .fail(function (e) {
                                 alert(e);
                                 $("#message").focus();
                             });
                     }
                     else {
                         chat.server.send($("#message").val())
                         .done(function () {
                             //alert("發送成功!");
                             $("#message").val("").focus();
                         })
                         .fail(function (e) {
                             alert(e);
                             $("#message").focus();
                         });
                     }
                 });
 
             });
 
         });
     </ script >
</ head >
< body >
     < h3 >大衆聊天室</ h3 >
     < div  id="chatbox">
     </ div >
     < div >
         < span >聊天名稱:</ span >
         @Html.TextBox("clientname", ViewBag.ClientName as string, new { @readonly = "readonly", style = "width:300px;" })
         < span >聊天對象:</ span >
         @Html.DropDownList("users", ViewBag.OnLineUsers as IEnumerable< SelectListItem >)
     </ div >
     < div >
         @Html.TextArea("message", new { rows = 5, style = "width:500px;" })
         < input  type="button" value="發送消息" id="btnSend" />
     </ div >
</ body >
</ html >

上述代碼中特別須要注意的是,須要引用一個「不存在的JS目錄」:<script src="~/signalr/hubs" type="text/javascript"></script>,爲何要打引號,是由於咱們在寫代碼的時候是不存在的,而當運行後就會自動生成signalr的代理腳本,這就是與非自動生成代理腳本最根本的區別,也正是由於這個自動生成的腳本,咱們能夠在JS中更加方便的調用服務端方法及定義回調方法,調用服務端方法採用:chat.server.XXX,而被服務端回調的客戶端方法則採用:chat.client.XXX

看一下上述兩種的運行效果截圖吧:

 

第三種:採用持久化鏈接類(PersistentConnection)

 DEMO - 3 示例代碼以下:

服務端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
//Startup類:
 
using  System;
using  System.Threading.Tasks;
using  Microsoft.Owin;
using  Owin;
using  Microsoft.AspNet.SignalR;
 
[assembly: OwinStartup( typeof (TestWebApp.Models.Startup))]
 
namespace  TestWebApp.Models
{
     public  class  Startup
     {
         public  void  Configuration(IAppBuilder app)
         {
             app.MapSignalR<MyConnection>( "/MyConnection" );
         }
     }
}
 
 
//MyConnection類:
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Threading.Tasks;
using  System.Web;
using  Microsoft.AspNet.SignalR;
 
namespace  TestWebApp.Models
{
     public  class  MyConnection : PersistentConnection
     {
         private  static  List< string > monitoringIdList =  new  List< string >();
         protected  override  Task OnConnected(IRequest request,  string  connectionId)
         {
             bool  IsMonitoring = (request.QueryString[ "Monitoring" ] ??  "" ).ToString() ==  "Y" ;
             if  (IsMonitoring)
             {
                 if  (!monitoringIdList.Contains(connectionId))
                 {
                     monitoringIdList.Add(connectionId);
                 }
                 return  Connection.Send(connectionId,  "ready" );
             }
             else
             {
                 if  (monitoringIdList.Count > 0)
                 {
                     return  Connection.Send(monitoringIdList,  "in_"  + connectionId);
                 }
                 else
                 {
                     return  Connection.Send(connectionId,  "nobody" );
                 }
             }
         }
 
         protected  override  Task OnReceived(IRequest request,  string  connectionId,  string  data)
         {
             if  (monitoringIdList.Contains(connectionId))
             {
                 return  Connection.Send(data,  "pass" );
             }
             return  null ;
         }
 
         protected  override  Task OnDisconnected(IRequest request,  string  connectionId,  bool  stopCalled)
         {
             if  (!monitoringIdList.Contains(connectionId))
             {
                 return  Connection.Send(monitoringIdList,  "out_"  + connectionId);
             }
             return  null ;
         }
     }
}

WEB客戶端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
<!-- MonitoringPage.cshtml 監控管理頁面-->
 
 
<! DOCTYPE  html>
 
< html >
< head >
     < meta  name="viewport" content="width=device-width" />
     < title >MonitoringPage</ title >
     < script  src="~/Scripts/jquery-1.6.4.min.js" type="text/javascript"></ script >
     < script  src="~/Scripts/jquery.signalR-2.2.0.min.js" type="text/javascript"></ script >
     < style  type="text/css">
         table {
             border:1px solid #808080;
             width:600px;
         }
         td {
             border:1px solid #808080;
             padding:3px;
         }
         .odd{ }
         .even{ }
         .non-temptr {
             display:none;
         }
     </ style >
     < script  type="text/javascript">
         $(function () {
             $("#userstable tbody tr:odd").addClass("odd");
             $("#userstable tbody tr:even").addClass("even");
 
             var conn = $.connection("/MyConnection", {"Monitoring":"Y"});
 
             conn.start().done(function () {
                 $("#userstable").delegate("button.pass", "click", function () {
                     var rid = $(this).parent("td").prev().attr("data-rid");
                     conn.send(rid);
                     var tr = $(this).parents("tr");
                     tr.remove();
                 });
                 
             }).fail(function (msg) {
                 alert(msg);
             });
 
             conn.received(function (msg) {
                 if (msg == "ready")
                 {
                     $("#spstatus").html("監控服務已就緒");
                     return;
                 }
                 else if (msg.indexOf("in_") == 0) {
                     var tr = $(".non-temptr").clone(true);
                     tr.removeClass("non-temptr");
                     var td = tr.children().first();
                     var rid = msg.toString().substr("in_".length);
                     td.html(rid + "進入被監控頁面,是否容許?");
                     td.attr("data-rid", rid);
                     $("#userstable tbody").append(tr);
                 }
                 else
                 {
                     var rid = msg.toString().substr("out_".length);
                     $("td[data-rid=" + rid + "]").parent("tr").remove();
                 }
             });
 
         });
     </ script >
</ head >
< body >
     < div >
         如下是實時監控到進入EnterPage頁面的用戶狀況:(服務情況:< strong >< span  id="spstatus"></ span ></ strong >)
     </ div >
     < table  id="userstable">
         < tr >
             < td >用戶進入消息</ td >
             < td >授 權</ td >
         </ tr >
         < tr  class="non-temptr">
             < td ></ td >
             < td  style="width:100px">< button  class="pass">容許</ button ></ td >
         </ tr >
     </ table >
</ body >
</ html >
 
 
<!-- EnterPage.cshtml 監控受限頁面-->
<! DOCTYPE  html>
 
< html >
< head >
     < meta  name="viewport" content="width=device-width" />
     < title >EnterPage</ title >
     < script  src="~/Scripts/jquery-1.6.4.min.js" type="text/javascript"></ script >
     < script  src="~/Scripts/jquery.signalR-2.2.0.min.js" type="text/javascript"></ script >
</ head >
< body >
     < script  type="text/javascript">
         $(function () {
             var conn = $.connection("/MyConnection");
 
             conn.start().fail(function (msg) {
                 alert(msg);
             });
 
             conn.received(function (data) {
                 if (data == "pass") {
                     $("#msg").html("管理員已審覈經過,能夠進入瀏覽詳情。");
                     setTimeout(function () {
                         self.location = "http://www.zuowenjun.cn";
                     }, 3000);
                 }
                 else
                 {
                     $("#msg").html("無管理員在線,請稍候再從新進入該頁面。");
                 }
             });
         });
     </ script >
     < div  id="msg">
         該頁面瀏覽受限,已自動將您的瀏覽請求發給管理員,請稍候。。。
     </ div >
</ body >
</ html >

上述代碼能夠看出與採用Hub(集線器類)的不一樣之處,一是:Startup.Configuration中是須要指定app.MapSignalR<MyConnection>("/MyConnection"),二是需實現繼承自PersistentConnection類的自定義的持久化鏈接類,在這個鏈接中能夠重寫:OnConnected、OnDisconnected、OnReceived、OnReconnected、ProcessRequest方法,同時有幾個重要的屬性成員Connection、Groups,服務端發消息給客戶端採用:Connection.Broadcast(廣播,全部客戶端均可以收到消息),Connection.Send(發送給指定的客戶端)

運行效果以下截圖示:

 

 

 

 

SignalR支持額外附加:QueryString、Cookie、State,具體的客戶端設置與服務端接收請見上面的代碼,同時也能夠參見以下其它博主總結的表格(SignalR的Javascript客戶端API使用方式整理):

相關文章
相關標籤/搜索