1. 程式人生 > >web服務端向客戶傳送提示資訊

web服務端向客戶傳送提示資訊

加密整理資訊:
之前有一個網站需要向客戶傳送某些資訊。構建了一點思路。


1、可以使用ajax定時請求:
讓瀏覽器隔個幾秒就傳送一次請求,詢問伺服器是否有新資訊。
或者:long poll 其實原理跟 ajax輪詢 差不多,都是採用輪詢的方式,不過採取的是阻塞模型(一直打電話,沒收到就不掛電話),也就是說,客戶端發起連線後,如果沒訊息,就一直不返回Response給客戶端。直到有訊息才返回,返回完之後,客戶端再次建立連線,周而復始
這種有點像下面這種場景
客戶端:啦啦啦,有沒有新資訊,沒有的話就等有了才返回給我吧(Request)
服務端:額。。 等待到有訊息的時候。。來 給你(Response)
客戶端:啦啦啦,有沒有新資訊,沒有的話就等有了才返回給我吧(Request) -loop
從上面可以看出其實這兩種方式,都是在不斷地建立HTTP連線,然後等待服務端處理,可以體現HTTP協議的另外一個特點,被動性。
何為被動性呢,其實就是,服務端不能主動聯絡客戶端,只能有客戶端發起。
簡單地說就是,伺服器是一個很懶的冰箱(這是個梗)(不會、不能主動發起連線),但是上司有命令,如果有客戶來,不管多麼累都要好好接待。
說完這個,我們再來說一說上面的缺陷(原諒我廢話這麼多吧OAQ)
從上面很容易看出來,不管怎麼樣,上面這兩種都是非常消耗資源的。
ajax輪詢 需要伺服器有很快的處理速度和資源。(速度)long poll 需要有很高的併發,也就是說同時接待客戶的能力。(場地大小)
所以 ajax輪詢 和 long poll 都有可能發生這種情況。
客戶端:啦啦啦啦,有新資訊麼?
服務端:月線正忙,請稍後再試(503 Server Unavailable)
客戶端:。。。。好吧,啦啦啦,有新資訊麼?
服務端:月線正忙,請稍後再試(503 Server Unavailable)
客戶端:然後服務端在一旁忙的要死:冰箱,我要更多的冰箱!更多。。更多。。(我錯了。。這又是梗。。)
2、下面就到WebSockets了
先簡單介紹下WebSockets:
通過上面這個例子,我們可以看出,這兩種方式都不是最好的方式,需要很多資源。
一種需要更快的速度,一種需要更多的’電話’。這兩種都會導致’電話’的需求越來越高。
哦對了,忘記說了HTTP還是一個狀態協議。
通俗的說就是,伺服器因為每天要接待太多客戶了,是個健忘鬼,你一掛電話,他就把你的東西全忘光了,把你的東西全丟掉了。你第二次還得再告訴伺服器一遍。
所以在這種情況下出現了,Websocket出現了。他解決了HTTP的這幾個難題。首先,被動性,當伺服器完成協議升級後(HTTP->Websocket),服務端就可以主動推送資訊給客戶端啦。所以上面的情景可以做如下修改。
客戶端:啦啦啦,我要建立Websocket協議,需要的服務:chat,Websocket協議版本:17(HTTP Request)
服務端:ok,確認,已升級為Websocket協議(HTTP Protocols Switched)
客戶端:麻煩你有資訊的時候推送給我噢。。
服務端:ok,有的時候會告訴你的。
服務端:balabalabalabala
服務端:balabalabalabala
服務端:哈哈哈哈哈啊哈哈哈哈
服務端:笑死我了哈哈哈哈哈哈哈
就變成了這樣,只需要經過一次HTTP請求,就可以做到源源不斷的資訊傳送了。(在程式設計中,這種設計叫做回撥,即:你有資訊了再來通知我,而不是我傻乎乎的每次跑來問你 )
這樣的協議解決了上面同步有延遲,而且還非常消耗資源的這種情況。那麼為什麼他會解決伺服器上消耗資源的問題呢?
其實我們所用的程式是要經過兩層代理的,即HTTP協議在Nginx等伺服器的解析下,然後再傳送給相應的Handler(PHP等)來處理。簡單地說,我們有一個非常快速的 接線員(Nginx) ,他負責把問題轉交給相應的 客服(Handler) 。
本身接線員基本上速度是足夠的,但是每次都卡在客服(Handler)了,老有客服處理速度太慢。,導致客服不夠。Websocket就解決了這樣一個難題,建立後,可以直接跟接線員建立持久連線,有資訊的時候客服想辦法通知接線員,然後接線員在統一轉交給客戶。
這樣就可以解決客服處理速度過慢的問題了。
同時,在傳統的方式上,要不斷的建立,關閉HTTP協議,由於HTTP是非狀態性的,每次都要重新傳輸 identity info (鑑別資訊),來告訴服務端你是誰。
雖然接線員很快速,但是每次都要聽這麼一堆,效率也會有所下降的,同時還得不斷把這些資訊轉交給客服,不但浪費客服的處理時間,而且還會在網路傳輸中消耗過多的流量/時間。
但是Websocket只需要一次HTTP握手,所以說整個通訊過程是建立在一次連線/狀態中,也就避免了HTTP的非狀態性,服務端會一直知道你的資訊,直到你關閉請求,這樣就解決了接線員要反覆解析HTTP協議,還要檢視identity info的資訊。
同時由客戶主動詢問,轉換為伺服器(推送)有資訊的時候就傳送(當然客戶端還是等主動傳送資訊過來的。。),沒有資訊的時候就交給接線員(Nginx),不需要佔用本身速度就慢的客服(Handler)了
3、好了現在實現一下(下面借鑑一下別人的demo,就不重寫了哈)

ASP .NET SignalR 是一個ASP .NET 下的類庫,可以在ASP .NET 的Web專案中實現實時通訊。什麼是實時通訊的Web呢?就是讓客戶端(Web頁面)和伺服器端可以互相通知訊息及呼叫方法,當然這是實時操作的。 WebSockets是HTML5提供的新的API,可以在Web網頁與伺服器端間建立Socket連線,當WebSockets可用時(即瀏覽器支援Html5)SignalR使用WebSockets,當不支援時SignalR將使用其它技術來保證達到相同效果。 SignalR當然也提供了非常簡單易用的高階API,使伺服器端可以單個或批量呼叫客戶端上的JavaScript函式,並且非常 方便地進行連線管理,例如客戶端連線到伺服器端,或斷開連線,客戶端分組,以及客戶端授權,使用SignalR都非常 容易實現。

一、首先,在MVC專案中安裝SingalR包(SingalR2.0需要.net4.5以上,VS2010可以安裝1.1.3版本,本例為VS2010+SignalR1.1.3)。

開啟工具—NuGet程式管理器—程式包管理器控制檯,輸入:

Install-Package Microsoft.AspNet.SignalR-Version 1.1.3

安裝完成後,一定要閱讀彈出的txt,這裡非常重要,

有兩個很重要的提示,一是在Global.asax檔案中加入RouteTable.Routes.MapHubs();二是在頁面前端加入指令碼<scriptsrc="~/signalr/hubs"></script>

(注:本示例採用的是MVC4MVC其他版本有其他的寫法,所以要讀這個readme)。

二、安裝完signalr包後,我們在專案中新增一個Home控制器以及它的ViewView選擇母版頁,記得在這個頁面里加上

<scriptsrc="~/signalr/hubs"></script>

<script src="../../Scripts/jQuery.signalR-1.1.4.min.js"typescript>

然後在專案中建立一個目錄,目錄裡建立Hub類檔案:

public class WorkflowHubHub

    {

        ///<summary>

        ///靜態使用者列表

        ///</summary>

        private IList<string> userList = UserInfo.userList;

        ///<summary>

        ///使用者的connectionID與使用者名稱對照表

        ///</summary>

        private readonly static Dictionary<stringstring>_connections = new Dictionary<stringstring>();

       ///<summary>

       ///傳送函式,前端觸發該函式給伺服器,伺服器在將訊息傳送給前端,(Clients.All.(函式名)是全體廣播,另外Clients提供了組播,廣播排除,組播排除,指定使用者播發等等)

       ///該函式名在前端使用時一定要注意,前端呼叫該函式時,函式首字母一定要小寫

       ///</summary>

       ///<param name="name1">發起者</param>

       ///<param name="name2">訊息接收者</param>

        public voidSendByGroup(string name1, string name2)

        {

            //Client內為使用者的id,是唯一的,SendMessage函式是前端函式,意思是伺服器將該訊息推送至前端

           Clients.Client(_connections[name2]).SendMessage("來自使用者"+name1 + " " + DateTime.Now.ToString("yyyy/MM/ddhh:mm:ss")+"的訊息推送!");

        }

        ///<summary>

        ///使用者上線函式

        ///</summary>

        ///<param name="name"></param>

        public voidSendLogin(string name)

        {

            if (!userList.Contains(name))

            {

               userList.Add(name);

               //這裡便是將使用者id和姓名聯絡起來

               _connections.Add(name, Context.ConnectionId); 

            }

            else

            {

               //每次登陸id會發生變化

               _connections[name] = Context.ConnectionId;

            }

            //新使用者上線,伺服器廣播該使用者名稱

           Clients.All.loginUser(userList);

        }

}

其中

public class UserInfo

    {

        public static IList<string>userList = new List<string>();

    }

為使用者名稱稱列表

 

HomeView中,Index.cshtml

<script src="../../Scripts/jquery.signalR-1.1.4.min.js"type="text/javascript"></script>

    <!--Reference the autogenerated SignalR hub script. 注意一定別忘記寫這句,signalr生成的指令碼都在這-->

    <script src="~/signalr/hubs"></script>

<h1>流程演示</h1>

<input type="hidden" id="displayname" />

<h2 id="thisname"></h2><br />

<select id="username" style="width:153px;">

</select>

<input id="send" type="button" value="傳送" />

<div>

<h1 id="messgae"></h1>

</div>

<script type="text/javascript">

    $(function () {

       //前端Hub的使用,注意的是,Hub的名字是WorkflowHub,這裡使用時首字母小寫

        var work = $.connection.workflowHub;

        $('#displayname').val(prompt('請輸入暱稱:'''));

        $('#thisname').text('當前使用者:'+$('#displayname').val());

        //對應後端的SendMessage函式,訊息接收函式

       work.client.sendMessage = function(message) {

            $('#messgae').append(message + '</br>')

        };

        //後端SendLogin呼叫後,產生的loginUser回撥

       work.client.loginUser = function(userlist) {

           reloadUser(userlist);

        };

        //hub連線開啟

        $.connection.hub.start().done(function () {

            var username = $('#displayname').val();

            //傳送上線資訊

           work.server.sendLogin(username);

            //點選按鈕,傳送訊息

            $('#send').click(function() {

               var friend = $('#username').val();

               //呼叫後端函式,傳送指定訊息

               work.server.sendByGroup(username, friend);

            });

        });

    });

    //重新載入使用者列表

    var reloadUser = function(userlist) {

        $("#username").empty();

        for (i = 0;i < userlist.length; i++) {

            $("#username").append("<option value="+userlist[i]+">"+userlist[i]+"</option>");

        }

    }

</script>

除錯執行,開啟兩個網頁(如果提示signalr必須在jquery之後,就在_layout.cshtml中把jquery放前面),輸入使用者名稱稱,登入後在列表中會顯示當前登入的使用者的資訊:

另一個網頁中輸入李晨:

我們在李晨這裡,給鄧超傳送,這樣我們就會在鄧超這裡看到推送的訊息了:

新增

SignalR跨域訪問基於MVC4.5,SignalR2.2
客戶端配置成服務端的地址:
   
 <script src="~/Scripts/jquery-1.10.2.min.js"></script>
    <script src="http://localhost:58690/Scripts/jquery.signalR-2.1.2.min.js" type="text/JavaScript"></script>
    <!--Reference the autogenerated SignalR hub script. -->
    <script src="http://localhost:58690/signalr/hubs"></script>
    <!--SignalR script to update the chat page and send messages.-->
    <script>
        $(function () {
            // Reference the auto-generated proxy for the hub.
            $.connection.hub.url = "http://localhost:58690/signalr/hubs";
            var chat = $.connection.chatHub;


服務端:startup進行增加跨域訪問設定。
using Microsoft.Owin;
using Owin;
using Microsoft.Owin.Cors;//新增此引用


[assembly: OwinStartupAttribute(typeof(SignalRChat2.Startup))]
namespace SignalRChat2
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.UseCors(CorsOptions.AllowAll);//跨域訪問
            app.MapSignalR();
        }
    }
}

剩下的自行腦補吧,signalr剛剛接觸,如果還看不懂可以去官網看看api

免費資源下載地址:http://download.csdn.net/detail/fuzhenglai/9865435