C# SignalR 即時通訊 聊天室


一.SignalR簡介

SignalR:當所連接的客戶端變得可用時服務器代碼可以立即向其推送內容,而不是讓服務器等待客戶端請求新的數據。實現實時服務器與客戶端通信。是一個開源.NET 庫生成需要實時用戶交互或實時數據更新的 web 應用程序。

SignalR的出現,讓頁面通過javascript可以很簡單的調用后端服務的方法,而在后端也可以很簡單的直接調用javascript所實現的方法,前后端可以進行實時通信。實現了服務器主動推送(Push)消息到客戶端頁面,這樣客戶端就不必重新發送請求或使用輪詢技術來獲取消息。

注意:SignalR 會自動管理連接。客戶端和服務器之間的連接是持久性的,不像傳統的 HTTP 連接。

二.SignalR傳輸方式

SignalR會根據當前瀏覽器自動選擇適當的傳輸方式。在最壞的情況下,SignalR會選擇使用長輪詢(Long Polling).

SignalR會依照下列順序來判定使用那種傳輸方式:

  • 1.如果瀏覽器是 Internet Explorer8 或更早版本,則使用長輪詢。
  • 2.如果配置了 JSONP(即連接啟動時 jsonp 參數設置為 true),則使用長輪詢。
  • 3.如果要建立跨域連接(即 SignalR 終結點和宿主頁不在相同的域中),並且滿足以下條件,則會使用 WebSocket:
    • 3.1客戶端支持 CORS(跨域資源共享)
    • 3.2客戶端支持 WebSocket
    • 3.3服務器支持 WebSocket
    • 如果這些條件中的任何一條不滿足,將使用長輪詢.
  • 4.如果未配置 JSONP 並且連接沒有跨域,只要客戶端和服務器都支持的話,將使用 WebSocket。
  • 5.如果客戶端或服務器不支持 WebSocket,則盡量使用服務器發送事件。Forever Frame。
  • 7.如果 Forever Frame 失敗,則使用長輪詢。

長輪詢(long polling)與傳統Ajax的不同之處:

  • 1.服務器端會阻塞請求直到有數據傳遞或超時才返回。
  • 2.客戶端 JavaScript 響應處理函數會在處理完服務器返回的信息后,再次發出請求,重新建立連接。
  • 3.當客戶端處理接收的數據、重新建立連接時,服務器端可能有新的數據到達;這些信息會被服務器端保存直到客戶端重新建立連接,客戶端會一次把當前服務器端所有的信息取回。

三.SignalR使用(個人理解)

下面是聊天室的主要代碼:

C#代碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

//工具 -> 庫程序包管理器 -> 程序包管理器控制台 輸入下面命令  
//install-package Microsoft.AspNet.SignalR -Version 1.1.4  
using Microsoft.AspNet.SignalR;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR.Hubs;

namespace SignalR.Controllers
{
    [HubName("ChatRoomHub")]
    public class ChatHub : Hub
    {
        static List<UserEntity> users = new List<UserEntity>();

        /// <summary>
        /// 添加用戶
        /// </summary>
        /// <param name="nickName"></param>
        public void UserEnter(string nickName)
        {
            UserEntity userEntity = new UserEntity
            {
                NickName = nickName,
                ConnectionId = Context.ConnectionId
            };

            users.Add(userEntity);
            Clients.All.NotifyUserEnter(nickName, users);//調用前台NotifyUserEnter方法
        }

        /// <summary>
        /// 發送消息
        /// </summary>
        /// <param name="nickName"></param>
        /// <param name="message"></param>
        public void SendMessage(string nickName, string message)
        {
            Clients.All.NotifySendMessage(nickName, message);//調用前台NotifySendMessage方法
        }

        /// <summary>
        /// 斷開(刷新頁面可以觸發此方法)
        /// </summary>
        /// <returns></returns>
        public override Task OnDisconnected()
        {
            var currentUser = users.FirstOrDefault(u => u.ConnectionId == Context.ConnectionId);
            if (currentUser != null)
            {
                users.Remove(currentUser);
                Clients.Others.NotifyUserLeft(currentUser.NickName, users);//調用前台NotifyUserLeft方法
            }
            return base.OnDisconnected();
        }
    }

    public class UserEntity
    {
        public string NickName { get; set; }

        public string ConnectionId { get; set; }
    }


    public class BaseController : Controller
    {
/// <summary> /// 聊天室 /// </summary> /// <returns></returns> public ActionResult BroadcastTest() { return View(); } } }

前台主要JavaScript代碼:

    <script type="text/javascript">
        var userNickName;//昵稱
        var notification;//消息

        jQuery(document).ready(function () {
            //沒有用戶名彈出輸入框
            while (!userNickName) {
                userNickName = window.prompt("請輸入昵稱!");
            }

            var chatHub = $.connection.ChatRoomHub;//對應后台的類ChatHub

            //添加用戶
            chatHub.client.NotifyUserEnter = function (nickName, users) {
                buildUserTemplate(users);
            }

            //用戶離開
            chatHub.client.NotifyUserLeft = function (nickName, users) {
                buildUserTemplate(users);
            }

            //處理消息內容
            chatHub.client.NotifySendMessage = function (nickName, message) {
                var userAvatar = 'http://forum.csdn.net/PointForum/ui/scripts/csdn/Plugin/001/face/29.gif';
                //判斷消息歸屬
                if (nickName == userNickName) {
                    $("#div_msg").append("<div style='text-align:right;'>"
                    + "<div float:right> <span style='margin-right:10px'>" + nickName + "</span>"
                     + "<img src='" + userAvatar + "' style='height:30px;width:30px;position:relative'/>"
                    + "<div class='demo clearfix fr'>"
                    + "<span class='triangle'></span>"
                    + "<div class='article' style='word'>" + message
                    + "</div></div></div></div><div class='clear-float'/>");
                }
                else {
                    $("#div_msg").append("<div>"
                     + "<img src='" + userAvatar + "' style='height:30px;width:30px;position:relative'/>"
                     + "<span style='left:10px;position:relative'>" + nickName + "</span>"
                     + "<div class='demo clearfix'>"
                     + "<span class='triangle'></span>"
                     + "<div class='article'>" + message
                     + "</div></div></div>");
                }
                //消息彈出框
                if (Notification.permission == "granted") {
                    notification = new Notification(nickName, {
                        body: message,
                        icon: userAvatar,
                        renotify: true,
                        tag: 1,
                        noscreen: true
                    });

                    notification.onclick = function () {
                        notification.close();
                    };
                } else if (Notification.permission != "denied") {
                    Notification.requestPermission(function (permission) {
                        notification = new Notification(nickName, {
                            body: message,
                            icon: userAvatar,
                            renotify: true,
                            tag: 1,
                            noscreen: true
                        });

                        notification.onclick = function () {
                            notification.close();
                        };
                    });
                }
                var objDiv = document.getElementById("div_msgbody");
                objDiv.scrollTop = objDiv.scrollHeight;
            }

            $.connection.hub.start().done(function () {
                chatHub.server.userEnter(userNickName);
            });

            //聊天框發送消息
            $("#message").keydown(function (event) {
                if (event.keyCode == 13) {
                    if ($("#message").val() != "") {
                        chatHub.server.sendMessage(userNickName, $("#message").val());
                        $("#message").val("");
                    }
                }
            });

            //發送按鈕
            $("#btn_Send").click(function () {
                if ($("#message").val() != "") {
                    chatHub.server.sendMessage(userNickName, $("#message").val());
                    $("#message").val("");
                }
            });

            //用戶列表
            function buildUserTemplate(users) {
                $("#lab_total").text(users.length);
                var userTemplate = "<ul style='list-style:none;'>"
                $.each(users, function (e, v) {
                    var userAvatar = 'http://forum.csdn.net/PointForum/ui/scripts/csdn/Plugin/001/face/29.gif';
                    userTemplate += "<li style='padding-top:5px;'>"
                        + "<img class='round-img-list' src='" + userAvatar + "'/>"
                        + "<label style='color:#666666;margin-left:10px'>" + v.NickName + "</label>"
                        + "</li>";
                });

                userTemplate += "</ul>";

                $("#div_member").html(userTemplate);
            }
        });
    </script>

完整SignalR的源碼(包括聊天室,進度條)(我用的vs2013):

百度網盤:鏈接: https://pan.baidu.com/s/1gf7s5oB 密碼: mdi2

最后:

  1、Clients.All.NotifySendMessage(nickName, message);調用的是前台JschatHub.client.NotifySendMessage = function (nickName, message) {}這段代碼。

  2、javascript中,注意使用client和server關鍵字來調用前端方法和后端方法。

聊天室例子:

下面是進度條例子:

廣播例子:

 


相關文章:http://www.cnblogs.com/frozenzhang/p/5406773.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM