SignalR代理對象異常:Uncaught TypeError: Cannot read property 'client' of undefined 推出的結論 SignalR 簡單示例 經過三個

SignalR代理對象異常:Uncaught TypeError: Cannot read property 'client' of undefined 推出的結論

 

 異常彙總:http://www.cnblogs.com/dunitian/p/4523006.html#signalRjavascript

後臺建立了一個DntHub的集線器css

前臺在調用的時候出現了問題(經檢查是代理對象建立失敗)html

因而到StackOverflow上面找了下:java

http://stackoverflow.com/questions/14146913/signalr-cannot-read-property-client-of-undefinednode

上面說改爲小寫就ok了,不少人也解決成功了jquery

逆天改爲小寫後也解決了,var chat = $.connection.dntHubgit

也許不少人就直接忽略了~~~~but,我爲何這樣就解決了呢?C#的命名規則就是首字母大寫啊?github

逆天喜歡深究一下,因而打開其動態生成的js,發現了這麼一句web

 

so,原來默認生成了的就是小寫開頭的,,,,,,,,(⊙o⊙)… 不少人說結束了? NONONO編程

程序猿須要什麼?想象力和反常規的想象力!

那麼我就大膽設想,咱們是否是能夠指定名字呢?

上網搜了下,原來經過 HubName("xxx")能夠設置名字

 

擴展一下,經過這個能夠設置任意名字,不見得和類名相同

 

那麼再試試?

 

看看動態生成的js,

嘿嘿,爽!

 

結論:

  若是不本身設置HubName,那麼SignalR會自動幫咱們生成一個和類名相同而且以小寫開頭的HubName

  這個問題有兩種解決方法,一種js中用首字母小寫的HubName,另外一種本身指定。(前臺建議都是小寫)

 

 

 

SignalR 簡單示例

 

1、什麼是 SignalR

  ASP.NET SignalR is a library for ASP.NET developers that simplifies the process of adding real-time web functionality to applications. Real-time web functionality is the ability to have server code push content to connected clients instantly as it becomes available, rather than having the server wait for a client to request new data.

 

2、簡單示例

  新建項目 SignalRChat

 

  選擇 Empty 模板

 

  安裝 SignalR

 

  添加完查看 Scripts 文件夾

 

  添加 Signal Hub Class (V2)

 

  代碼以下

複製代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.SignalR;

namespace SignalRChat
{
    public class ChatHub : Hub
    {
        public void Send(string name, string message)
        {
            Clients.All.broadcastMessage(name, message);
        }
    }
}
複製代碼

 

  添加 OWIN Startup class

 

  代碼以下

複製代碼
using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(SignalRChat.Startup))]

namespace SignalRChat
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=316888
            app.MapSignalR();
        }
    }
}
複製代碼

 

  添加 HTML 頁面

 

  代碼以下

複製代碼
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<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 references. -->
    <!--Reference the jQuery library. -->
    <script src="Scripts/jquery-1.6.4.min.js"></script>
    <!--Reference the SignalR library. -->
    <script src="Scripts/jquery.signalR-2.2.0.min.js"></script>
    <!--Reference the autogenerated SignalR hub script. -->
    <script src="signalr/hubs"></script>
    <!--Add script to update the page and send messages.-->
    <script type="text/javascript">
        $(function () {
            //To enable logging for your hub's events in a browser, add the following command to your client application
            $.connection.hub.logging = true;
            // Declare a proxy to reference the hub.
            var chat = $.connection.chatHub;
            // Create a function that the hub can call to broadcast messages.
            chat.client.broadcastMessage = function (name, message) {
                // Html encode display name and message.
                var encodedName = $('<div />').text(name).html();
                var encodedMsg = $('<div />').text(message).html();
                // Add the message to the page.
                $('#discussion').append('<li><strong>' + encodedName + '</strong>:&nbsp;&nbsp;' + encodedMsg + '</li>');
            };
            // Get the user name and store it to prepend to messages.
            $('#displayname').val(prompt('Enter your name:', ''));
            // Set initial focus to message input box.
            $('#message').focus();
            // Start the connection.
            $.connection.hub.start().done(function () {
                $('#sendmessage').click(function () {
                    // Call the Send method on the hub.
                    chat.server.send($('#displayname').val(), $('#message').val());
                    // Clear text box and reset focus for next comment.
                    $('#message').val('').focus();
                });
            });
        });
    </script>
</body>
</html>
複製代碼

 

  F5 運行,複製 URL 再打開一個新瀏覽器窗口同時運行,分別輸入 NameOne 和 NameTwo

 

  若是是 IE 瀏覽器,打開 Soultion Explorer,能夠看到 hubs 文件

 

  F12 打開控制檯,發現使用的是 ForeverFrame (若是想在控制檯查看日誌,代碼中必須包含 $.connection.hub.logging = true;)

 

  Chrome 效果以下

 

參考文章:http://www.asp.net/signalr/overview/getting-started/tutorial-getting-started-with-signalr

代碼下載

SignalRChat

做者: 舍長
 
 
 

經過三個DEMO學會SignalR的三種實現方式

 

1、理解SignalR

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

2、SignalR的三種實現方式

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

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

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客戶端:

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)+自動生成代理模式

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使用方式整理):

 

 

SignalR是.net中一個不錯的推送框架。它包含長鏈接跟集線器兩種方式,我須要的是兩個項目之間的通信,也就是A控制檯項目,向B web項目發送數據,而後B就推送到各個用戶頁面。 
下面的代碼是A控制檯項目代碼

class getData
    {
        //日誌輸出 private static ILog log = log4net.LogManager.GetLogger(typeof(getData)); //鏈接web public static Connection connection =new Connection("http://localhost:4669/webConnections"); static getData() //靜態構造初始化長鏈接參數 { //添加頭文件,這樣B就知道是A傳輸數據而不是頁面客戶端,還可使用connection.CookieContainer來區分 connection.Headers.Add("where", "windowserver"); try { connection.Received+=Console.WriteLine;//控制檯輸出 connection.Start().Wait(); //wait()方法十分重要,要否則就異步執行後面的語句了 } catch (System.Exception ex) { log.Error("沒法鏈接web應用,Start方法不能使用" + ex.ToString()); throw ex; } } public void QueryDataAndToWeb(object sta) { string datajson = connection.JsonSerializeObject(sta);//序列化成JSON傳送到web try { connection.Send(datajson).Wait(); } catch (System.Exception ex) { Console.Write(ex); log.Error("沒法鏈接web應用,Send方法不能使用" + ex.ToString()); } } }
  • 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

在上述代碼中,我只是簡單的寫出使用方法,一些異常處理,還沒處理完。 
下面貼出B web項目 
建立一個Startup.cs

[assembly: OwinStartup(typeof(Startup))] namespace signalr { public class Startup { public void Configuration(IAppBuilder app) { // 有關如何配置應用程序的詳細信息,請訪問 http://go.microsoft.com/fwlink/?LinkID=316888 // 配置上文實現的ChatConnections app.MapSignalR<ChatConnection>("/webConnections"); } } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

ChatConnection類:

public class ChatConnection : PersistentConnection { private static string _WinServiceID = "";//爲空字符串表示斷線,用於從內部觸發推送 protected override Task OnConnected(IRequest request, string connectionId) { string where = request.Headers.Get("where"); if (where !=null&&where.Equals("windowserver")) _WinServiceID = connectionId; return Connection.Send(connectionId, "welcome!"); } protected override Task OnReceived(IRequest request, string connectionId, string data) { string where = request.Headers.Get("where"); if (where !=null&&where.Equals("windowserver")) Connection.Broadcast(Newtonsoft.Json.JsonConvert.DeserializeObject(data), connectionId); else {//說明頁面須要一些請求好比添加刪除修改之類東西,而且須要發送到A項目 Connection.Send(_WinServiceID, data); } } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

這個時候有個問題就是A項目接受B項目發送的數據,好比B項目客戶端頁面刪除,增長,修改A項目的東西,那就不能用控制檯輸出,這時候須要對傳送的數據進行分析是增長仍是刪除仍是修改。

connection.Received += data => { connection_Received(data); };//用這個替換上面代碼 connection.Received+=Console.WriteLine; public void connection_Received(string obj) { //在這裏面對web發送的數據進行解析而後進行操做 switch (obj) { case "delete": break; default: //包含添加跟更新兩種命令 break; } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

頁面javaScript的客戶端鏈接我就不貼出代碼了,那些百度搜索資料不少也能夠看下面給出的連接地址。 
這樣就完成一個簡單的兩個項目之間的通信,二者之間能夠相互利用JSON通信。

SignalR參考連接:

https://www.asp.net/signalr/overview/getting-started/tutorial-getting-started-with-signalr#run 
http://www.cnblogs.com/Wddpct/p/5650015.html#2.1

SignalR 集線器簡單實例2

1.路由配置

 

  1.  
    //註冊集線器路由
  2.  
    app.MapSignalR( "/realtime", new HubConfiguration() { });

 

2.服務端處理

 

  1.  
    /// <summary>
  2.  
    /// 集線器定義
  3.  
    /// </summary>
  4.  
    public class EchoHub : Hub
  5.  
    {
  6.  
    private static int _count = 0;
  7.  
    /// <summary>
  8.  
    /// 鏈接建立成功
  9.  
    /// </summary>
  10.  
    /// <returns></returns>
  11.  
    public override async Task OnConnected()
  12.  
    {
  13.  
    Interlocked.Increment( ref _count);
  14.  
    //通知其餘鏈接
  15.  
    await Clients.Others.Message("新鏈接建立:" + Context.ConnectionId + ",已鏈接數量:" + _count);
  16.  
    //通知當前鏈接
  17.  
    await Clients.Caller.Message("Hey,welcome!");
  18.  
    }
  19.  
    /// <summary>
  20.  
    /// 鏈接斷開
  21.  
    /// </summary>
  22.  
    /// <param name="stopCalled"></param>
  23.  
    /// <returns></returns>
  24.  
    public override Task OnDisconnected(bool stopCalled)
  25.  
    {
  26.  
    Interlocked.Decrement( ref _count);
  27.  
    //通知全部鏈接
  28.  
    return Clients.All.Message(Context.ConnectionId + " 鏈接關閉,剩餘鏈接數:" + _count);
  29.  
    }
  30.  
    /// <summary>
  31.  
    /// 廣播消息
  32.  
    /// </summary>
  33.  
    public Task Broadcast(string message)
  34.  
    {
  35.  
    //通知全部鏈接
  36.  
    return Clients.All.Message(Context.ConnectionId + "> " + message);
  37.  
    }
  38.  
    /// <summary>
  39.  
    /// 獲取當前在線用戶的鏈接ID
  40.  
    /// </summary>
  41.  
    /// <returns></returns>
  42.  
    public string GetCurrentID()
  43.  
    {
  44.  
    return Context.ConnectionId;
  45.  
    }
  46.  
    /// <summary>
  47.  
    /// 獲取當前請求的cookie列表
  48.  
    /// </summary>
  49.  
    /// <returns></returns>
  50.  
    public object GetCurrentCookie()
  51.  
    {
  52.  
    return Context.RequestCookies.ToArray();
  53.  
    }
  54.  
    /// <summary>
  55.  
    /// 獲取全部在的鏈接
  56.  
    /// </summary>
  57.  
    /// <returns></returns>
  58.  
    public object GetAllID()
  59.  
    {
  60.  
    // GlobalHost.ConnectionManager.GetHubContext<>();
  61.  
    return Clients.All;
  62.  
    }
  63.  
     
  64.  
     
  65.  
    }

 

3.客戶端處理

 

  1.  
    <h1>Echo hub</h1>
  2.  
    <div>
  3.  
    <input type="text" id="text" />
  4.  
    <button id="send">Send</button>
  5.  
    <button id="GetCurrentID">獲取當前在線用戶的ID</button>
  6.  
    <button id="GetAllID">獲取全部在線用戶的ID</button>
  7.  
    <button id="GetCookies">獲取當前請求的cookie</button>
  8.  
    </div>
  9.  
    @*與服務器端集線器
  10.  
    創建鏈接*@
  11.  
    <script src="/realtime/js"></script>
  12.  
    <script type="text/javascript">
  13.  
    $( function () {
  14.  
    //服務器端對應的集線器
  15.  
    var hub = $.connection.echoHub;
  16.  
    $.connection.loggint = true;
  17.  
    $.connection.url = '/realtime';
  18.  
    //客戶端接收消息
  19.  
    hub.client.message = function (text) {
  20.  
    $( "body").append(text + "<br />");
  21.  
    }
  22.  
    //啓動鏈接成功
  23.  
    $.connection.hub.start().done( function () {
  24.  
    //廣播消息
  25.  
    $( '#send').click(function () {
  26.  
    var text = $('#text').val();
  27.  
    //調用服務器端,廣播消息
  28.  
    hub.server.broadcast(text)
  29.  
    });
  30.  
    //獲取在線用戶的ID
  31.  
    $( '#GetCurrentID').click(function () {
  32.  
    var deferred = hub.server.getCurrentID();
  33.  
    deferred.done( function (data) {
  34.  
    console.info(data);
  35.  
    alert(data);
  36.  
    });
  37.  
    });
  38.  
    //獲取全部用戶在線ID,失敗
  39.  
    $( '#GetAllID').click(function () {
  40.  
    var deferred = hub.server.getAllID();
  41.  
    deferred.done( function (data) {
  42.  
    alert(data);
  43.  
    console.info(data);
  44.  
    });
  45.  
    deferred.fail( function (err) {
  46.  
    console.info(err)
  47.  
    alert(err.message);
  48.  
    });
  49.  
    });
  50.  
    //獲取當前請求的cookie
  51.  
    $( '#GetCookies').click(function () {
  52.  
    var deferred = hub.server.getCurrentCookie();
  53.  
    deferred.done( function (data) {
  54.  
    console.info(data);
  55.  
    console.info(JSON.stringify(data));
  56.  
    alert(data);
  57.  
    }).fail( function (err) {
  58.  
    console.info(err);
  59.  
    alert(err.message);
  60.  
    });
  61.  
    });
  62.  
    });
  63.  
    });
  64.  
    </script>

 

獲取當前鏈接的ID

獲取當前鏈接上下文中的cookie


獲取全部在線鏈接的ID失敗

 

 

 

 

 

 

用SignalR建立實時永久長鏈接異步網絡應用程序

2012-11-06 13:44 by Zhuang miao, 7181 閱讀, 0 評論, 收藏編輯

原文發表地址: Asynchronous scalable web applications with real-time persistent long-running connections with SignalR

原文發表時間: 2011-08-29 09:29

 

SignalRSample.zip

我最近在研究異步和衡量的問題。你可能看過我以前寫的博文:我研究的 node.js和iisnode在Windows上運行

每一個應用程序都有不一樣的要求,「衡量」的規則不是對每一種應用程序都適用的。衡量一個獲取數據和循環的網絡應用和那些召集深度潛在的主框架應用,保持服務器永久鏈接的應用是不同的。

古語說「當你手上只有榔頭的時候,看什麼都像是釘子」,這個在編程和網絡世界裏的確是真理。工具越多而且掌握使用它們的技能那麼效果就會越好。那也是爲何我不只僅宣揚多種語言編程,還但願你們深刻研究本身的主要語言。好比當你真正學會了LINQ,而且很擅長使用dynamic,C#就變成了一種更加有趣和富有表現力的語言。

更新是用榔頭錘釘子的常見例子。想作一個聊天程序?每隔5秒更新一次。處理時間很長?那就丟掉動畫圖片,不停地更新,我親愛的朋友!

間隔長一段時間來更新是另外一種方法。簡單來講就是打開一個鏈接而後保持打開狀態,強制客戶端(瀏覽器)等待,僞裝須要很長時間才能返回結果。若是你的服務器端程序模型上有足夠多的控件,這就能容許你按照你指望的來返回數據在打開的鏈接上。若是鏈接斷開了,鏈接會無縫地被從新打開,斷開信息會在兩個端口都隱藏掉。在WebSockets將會是另外一種解決這類問題的方法。

ASP.NET中的永恆鏈接

在聊天應用程序或者股票應用程序中用ASP.NET作這樣的永久鏈接不是很容易。在服務器或客戶庫中尚未一個合適的概念來討論它。

SignalR是爲ASP.NET而設的一個異步信號庫。咱們團隊正在研究這個項目,但願能建立實時的多用戶網絡應用。

這不就是Socket.IO或者nowjs麼?

Socket.IO是一個客戶端JavaScript庫,能與node.js進行交流。Nowjs是能讓你從服務器端調用客戶的類庫。這些和Signalr都很類似並且相關,只是同一個概念中的不一樣方面。這些JavaScript庫都但願在服務器端有特定的東西和協定,這樣就有可能讓服務器端的顯示如同客戶但願看到的那樣。

SignalR是一個徹底基於客戶及服務器端解決方案,它是以JS做爲客戶端和ASP.NET做爲服務端來建立這類的應用。你能夠去GitHub獲取。

我能用12行代碼建立一個聊天應用嗎?

我想說

「在代碼的世界中,簡潔明瞭永遠不是神話。」

換句話說,我但願我能這麼說,固然能夠!

 1: Chat.DoItBaby()

可是那多是一個謊話,下面是SignalR中的一個真實的聊天應用程序例子:

客戶:

 1: var chat = $.connection.chat;
 2: chat.name = prompt("What's your name?", "");
 3:  
 4: chat.receive = function(name, message){
 5: $("#messages").append(""+name+": "+message);
 6: }
 7:  
 8: $("#send-button").click(function(){
 9: chat.distribute($("#text-input").val());
 10: });

服務器:

 1: public class Chat : Hub {
 2: public void Distribute(string message) {
 3: Clients.receive(Caller.name, message);
 4: }
 5: }

那也許是12行,其實能夠縮到9行的,若是你願意的話。

有關SignalR的更多細節

SignalR在NuGet上被分紅了幾個包:

· SignalR – 主要的包,包括SignalR.Server和SignalR.Js(你應該安裝這個)

· SignalR.Server – 服務器端組件用以建立SignalR端點

· SignalR.Js – SignalR的Javascript客戶端

· SignalR.Client – SignalR的.NET客戶端

· SignalR.Ninject - SignalR 的Ninject 相關解決方案

若是你只是想了解一下玩一玩,那就從Visual Studio 2010開始。

首先,建立一個空白的ASP.NET應用程序,用NuGet安裝SignalR,用NuGet的UI或者Package Console均可以。

其次,建立一個新的default.aspx頁面,添加一個按鈕,一個文本框,用如下腳原本引用jQuery和jQuery.signalR。

 1: <html >
 2: <head runat="server">
 3: <script src="Scripts/jquery-1.6.2.min.js" type="text/javascript"></script>
 4: <script src="Scripts/jquery.signalR.min.js" type="text/javascript"></script>
 5: </head>
 6: <body>
 7: <form id="form1" runat="server">
 8: <div>
 9: <script type="text/javascript">
 10: $(function () {
 11: var connection = $.connection('echo');
 12: connection.received(function (data) {
 13: $('#messages').append('<li>' + data + '</li>');
 14: });
 15: connection.start();
 16: $("#broadcast").click(function () {
 17: connection.send($('#msg').val());
 18: });
 19: }); 
 20: </script>
 21: <input type="text" id="msg" />
 22: <input type="button" id="broadcast" />
 23: <ul id="messages"></ul>
 24: </div>
 25: </form>
 26: </body>
 27: </html>

底層鏈接

注意咱們是從客戶端調用/echo嗎?這個在Global.asax中有所介紹:

 1: RouteTable.Routes.MapConnection<MyConnection>("echo", "echo/{*operation}");

如今,咱們有兩個SignalR模型來選擇。咱們先來看看底層的這個。

 1: using SignalR;
 2: using System.Threading.Tasks;
 3:  
 4: public class MyConnection : PersistentConnection
 5: {
 6: protected override Task OnReceivedAsync(string clientId, string data)
 7: {
 8: // Broadcast data to all clients
 9: return Connection.Broadcast(data);
 10: }
 11: }

咱們繼承PersistentConnection這個類,基本上能夠在這層中作咱們想作的任何事,這有不少個選擇:

 1: public abstract class PersistentConnection : HttpTaskAsyncHandler, IGroupManager
 2: {
 3: protected ITransport _transport;
 4:  
 5: protected PersistentConnection();
 6: protected PersistentConnection(Signaler signaler, IMessageStore store, IJsonStringifier jsonStringifier);
 7:  
 8: public IConnection Connection { get; }
 9: public override bool IsReusable { get; }
 10:  
 11: public void AddToGroup(string clientId, string groupName);
 12: protected virtual IConnection CreateConnection(string clientId, IEnumerable<string> groups, HttpContextBase context);
 13: protected virtual void OnConnected(HttpContextBase context, string clientId);
 14: protected virtual Task OnConnectedAsync(HttpContextBase context, string clientId);
 15: protected virtual void OnDisconnect(string clientId);
 16: protected virtual Task OnDisconnectAsync(string clientId);
 17: protected virtual void OnError(Exception e); 
 18: protected virtual Task OnErrorAsync(Exception e);
 19: protected virtual void OnReceived(string clientId, string data);
 20: protected virtual Task OnReceivedAsync(string clientId, string data);
 21: public override Task ProcessRequestAsync(HttpContext context);
 22: public void RemoveFromGroup(string clientId, string groupName);
 23: public void Send(object value);
 24: public void Send(string clientId, object value);
 25: public void SendToGroup(string groupName, object value);
 26: }

高端中轉站

或者咱們能夠提升一級,在添加

<script src="http://blogs.msdn.com/signalr/hubs" type="text/javascript"></script>

至頁面後爲咱們的聊天客戶作這個:

 1: $(function () {
 2: // Proxy created on the fly
 3: var chat = $.connection.chat;
 4:  
 5: // Declare a function on the chat hub so the server can invoke it
 6: chat.addMessage = function (message) {
 7: $('#messages').append('<li>' + message + '</li>');
 8: }; 
 9:  
 10: $("#broadcast").click(function () {
 11: // Call the chat method on the server
 12: chat.send($('#msg').val());
 13: });
 14:  
 15: // Start the connection
 16: $.connection.hub.start();
 17: });

而後就沒有跟蹤的必要了,鏈接聊天會映射到服務器上,而後服務器就能回調客戶端了。

 1: public class Chat : Hub
 2: {
 3: public void Send(string message)
 4: {
 5: // Call the addMessage method on all clients
 6: Clients.addMessage(message);
 7: }
 8: }

我想你的腦子到如今應該要炸了。這是C#,服務器端代碼,咱們告訴全部的客戶調用addMessage() JavaScript函數。咱們經過永久鏈接,發送客戶函數名稱以得到服務器端迴應,從而回調客戶端。這和NowJS很像,可是沒有不少人熟悉這個技術。

SignalR會處理全部客戶端和服務器端的鏈接,確保它保持鏈接,運行正常。它會爲你的瀏覽器選擇正確的鏈接方式,用異步衡量async,await技術(就像我在node.js博文中展現過的asp.net上的可衡量異步事件 I/O )。

想親眼看看這個樣本的運行?

咱們在http://chatapp.apphb.com上有一個小小的聊天應用程序,快去看看吧。那裏有aspnet的同仁。試試粘貼到你的YouTube或相冊上吧!

image

這是早期的,不過仍是一個頗有趣的.NET新組件,之前也從沒出現過。你能夠隨意去GitHub看看,和SignalR的做者David Fowler和Damian Edwards交流下。但願大家喜歡。

相關文章
相關標籤/搜索