ساخت Real Time (بلادرنگ) برای چت توسط MVC SignalR Hub

SignalR یک کتابخانه برای توسعه دهندگان است که فرآیند ساده اضافه کردن زمان واقعی قابلیت های وب به برنامه های کاربردی را ایجاد می کند. در این مقاله مفهوم SignalR را از طریق پیاده سازی چت و تعامل بین کلاینت و سرور به صورت بصری توضیح می دهیم که شامل کلاس hub می باشد.

ساخت Real Time (بلادرنگ) برای چت توسط MVC SignalR Hub

مقدمه

امروزه با توجه به افزایش اطلاعات و ضرورت دستیابی به داده در زمان کوتاه، نیاز به تکنولوژی هایی داریم که نیازمندی های ما را در زمان کوتاه پوشش دهد. برای مثال زمانی که قیمت ها در بازار سهام در هر لحظه در حال تغییر است، آیا فکر می کنید کاربر باید در هر لحظه برای اطلاع از آخرین قیمت، صفحه خود را refresh کند؟ بدیهی است که این کار، راه حل منطقی برای چنین مسئله ای نیست. یا با افزایش تولید محصولات و خدمات، به خدمات مشتریان برای کمک به کاربر و خریدار نیاز داریم.

برای این کار بهترین و ارزان قیمت ترین ارتباط، که گفتگو با برنامه چت می باشدرا انتخاب می کنیم. به خاطر اینکه مثلا نمی توانیم کاربران را مجبور کنیم  برای دریافت آخرین پیام ما حتما دکمه ای را فشار دهند.

SignalR یک تکنولوژی real time است که با استفاده از مجموعه کتابخانه های asynchrony یک اتصال دائم بین کلاینت و سرور ایجاد می کند. کاربر می تواند آخرین به روز رسانی داده را از سرور و بدون راه های سنتی مانند refresh کردن صفحه یا فشرن دکمه دریافت کند.

پیش زمینه

قبل از شروع لازم است در مورد تکنولوژی MVC4 و EntityFramework 4 آشنایی کافی داشته باشید. در ادامه مفاهیم مهم را توضیح می دهیم:

WebSocket .1

WebSocket یک پروتکل تمام duplex است و از http handshaking به صورت داخلی استفاده می کند، همچنین به جریان پیام ها اجازه می دهد که در بالای TCP قرار گیرند. این تکنولوژی توسط مرورگرهای Google Chrome، Fire Fox، IE و Win IIS پشتیبانی می شود. با توجه به پیام رمزنگاری شده و full duplex پس WebSocket بهترین راه حل است، در ابتدای کار  signalR، هم وب سرور و هم کلاینت را کنترل می کند که آیا آنها websocket را پشتیبانی می کنند یا خیر.

ارتباط ساده (Simplex Communication)

این روش فقط در ارتباط یک طرفه مشخص می شود، زمانی که  فقط یک نقطه broadcasts می شود در حالی که نقطه دیگر فقط می تواند بدون عمل ارسال به پیام گوش دهد؛ مثل تلویزیون و رادیو

Half duplex

یک نقطه پیغام را ارسال می کند و در این همین لحظه نقطه دیگر نمی تواند پیغام خود را ارسال کند و باید منتظر باشد تا زمانی که نقطه اول انتقال خود را به اتمام برساند و سپس پیام خود را ارسال کند، این یک ارتباط تک خطی در یک لحظه و یک زمان است مانند، دستگاه های قدیمی بی سیم walkie-talkie و پروتکل HTTP می باشد.

Full duplex

در این ارتباط هر دونقطه می توانند پیغام خودشان را به طور همزمان  بفرستند و دریافت کنند و نیازی به منتظر بودن تا زمانی که نقاط دیگر انتقال خود را به پایان برسانند، نیست؛ مانند تلفن و پروتکل websocket

 

Server Sent Events (SSE) .2

انتخاب بعدی برای signalr رویداد server sent می باشد، زیرا که یک ارتباط دائمی بین سرور و کلاینت ایجاد می کند. در این روش، ارتباط قطع نمی شود و آخرین داده به صورت خودکار از سرور به روز رسانی خواهد شد و از طریق اتصال HTTP به کلاینت ارسال می شود. EventSource بخشی از تکنولوژی HTML5 می باشد.

 

var evsrc = new EventSource("url");
       // Load and Register Event Handler for Messages in this section

       evsrc.addEventListener("message", function (event) {
           //processing data in this section
       });

 

Forever Frame .3

زمانی که کلاینت درخواست را به سرور می فرستد، سرور یک iframe پنهان به عنوان بلوک chunked به کلاینت می فرستد، این iframe مسئول نگهداری ارتباط بین کلاینت و سرورforever است. هر زمان که سرور داده ها را تغییر دهد، داده به عنوان تگ script به کلاینت فرستاده می شود (iframe مخفی شده است) و این اسکریپت ها به صورت پی در پی دریافت خواهند شد.

4. رای گیری(Polling)

کلاینت درخواست را به سرور می فرستد و سرور بلافاصله آن را پاسخ می دهد اما بعد از آن که سرور ارتباط را قطع کند، دوباره برای استقرار ارتباط بین سرور و کلاینت، باید برای درخواست بعدی منتظر باشیم. برای حل این مشکل باید یک timeout به صورت دستی تنظیم شود و برای هر 10 ثانیه کلاینت درخواست را به سرور بفرستد تا اصلاح جدید سمت سرور را کنترل کرده و آخرین به روز رسانی سمت سرور را دریافت کند. Polling از منابع استفاده می کند و این یک راه حل اقتصادی نیست.

Long Polling.5

مشتری درخواست را به سرور می فرستد و سرور بلافاصله پاسخ می دهد، این ارتباط تا یک زمان مشخص باقی می ماند و در طول این دوره کلاینت ها درخواست صریح و روشنی به سرور نمی فرستند در حالی که polling client در طول timeout خود درخواست صریح و روشن به سرور می فرستد.برنامه نویسی تیمی این مفهوم را پوشش می دهد.

 

به طور خلاصه کتابخانه SignalR انتخاب یک نوع انتقال داده بین کلاینت و سرور است، اولویت آن با websocket، server sent event،  long polling و forever iframe می باشد. دو کلاس در این کتابخانه وجود دارد که به صورت زیر است:

persistentconnection .1(ارتباط ماندگار)

این یک کلاس سطح پایین است بنابراین پیچیده می باشد و نیاز به پیکربندی بیشتری دارد اما در عوض تسهیلات بیشتری برای کلاس های شخصی بوجود می آورد.

Hub .2

این یک کلاس سطح بالا است و محبوبیت بیشتری برای استفاده دارد.

 

چگونه می توان یک سناریو چت ساده با کمک signalr و کلاس hub ایجاد کرد؟

هدف ما فقط ایجاد یک سناریو random برای درگیر کردن signalr است. شما می توانید از این روش برای سناریو شخصی خود استفاده کنید و ما مراحل زیر را فقط برای ایجاد یک چالش با سرور (کلاس hub) سمت کلاینت انجام می دهیم و نشان می دهد که چگونه کلاینت درخواست را می فرستد و سرور پاسخ می دهد؟ و چگونه آنها باهم در تعامل هستند.

توضیح سناریو

ما می خواهیم یک اپلیکیشن برای بخش خدمات مشتریان ایجاد کنیم. برخی از مدیران که مسئول کمک به مشتری هستند و از سوی دیگر مشتریانی وجود دارد که سوالی می پرسند و نیاز به کمک دارند.

فرض کنید که دو Admin آنلاین هستند و به سرویس چت متصل می باشند و مشتری اول یک سوال می پرسد، بنابراین سیستم به مشتری اول و به admin آزاد متصل می شود، برای مشتری دوم این داستان تکرار می شود، اما کلاینت سوم، از سیستم Alarm دریافت می کند که ادمینی برای کمک وجود ندارد. هر زمان که اولین مشتری ارتباطش قطع شود، ادمین اول آزاد می شود.

قرارداد ما برای این سناریو استفاده از flag برای یادآوری این است که کاربری که متصل شده است User یا Admin  که این خودش دو حالت دارد آن Admin آزاد یا مشغول است.

در پایگاه داده ما، اگر admincode برابر  صفر باشد پس آن User است و در غیر این صورت Admin می باشد و ما یک فلگ به نام "tpflag" تعریف کره ایم ( در برنامه ) که برای کاربر مساوی صفر و برای ادمین مساوی 1 می باشد. هر زمان که آنها به چت متصل شوند، فلگ "freeflag" صفر می شود که نشان می دهد User مشغول است، اگر کلاینت(مشتری) گفتگو(چت) را ترک کند، این فلگ یک می شود و نشان می دهد که آزاد شده است. در کل به صورت زیر تعریف می شود:

اگر freeflag==0 باشد یعنی مشغول است.

اگر freeflag==1 باشد یعنی مشغول است.

اگر tpflag==0 باشد یعنی User است.

اگر tpflag==1 باشد یعنی admin است.

 

پیش نیازها

Visual Studio 2013 .1

SQL server 2008 .2

3. وابستگی های لازم دیگر را از package manager console در Nuget نصب کنید.

 

مرحله 1: پروژه را ایجاد کنید.

File --> New Project --> ASP.Net MVC 4 Web Application و سپس یک نام مناسب برای پروژه قرار دهید.

مرحله 2: PM را برای نصب فایل های وابستگی باز کنید.(جهت نصب فایل های لازم از کنسول Nuget).

از منو بالا گزینه Tools را زده و Library Package Manager و در آخر Package Manager Console را بزنید.

 

مرحله 3: دستورالعمل برای حذف وابستگی های قدیمی

در ابتدا تمام وابستگی های قدیمی را برای نصب ورژن جدید  SignalR 2.x.x پاک کنید:

PM> Uninstall-Package Microsoft.AspNet.SignalR –RemoveDependencies

 

مرحله 4: دستورالعمل برای نصب فایل های وابستگی لازم و ضروری

برای نسخه جدید از دستور زیر استفاده کنید:

PM> Install-Package Microsoft.AspNet.SignalR

ما در اینجا از signslr نسخه2.0.1 استفاده کرده ایم:

PM> Install-Package Microsoft.AspNet.SignalR -Version 2.0.1

با نوشتن این دستورالعمل Nuget تمام وابستگی ها را که شما برای اجرا signalr احتیاج دارید انجام می دهد. اگر شما به بخش رفرنس در solution نگاهی بیاندازید، به خوبی می توانید Microsoft.ASPNet.SignalR.x, Microsoft.Owin.x.x.. و غیره را ببینید، یا اگر شما به بخش Script ها در solution  نگاهی بیاندازید jquery-1.x , jquery.signalR 2.x.x. و غیره را می بینید پس در مورد تمام وابستگی ها مطمئن می شوید.

برای این کار به solution رفته و Reference را باز کنید:

سپس مجدد در Solution قسمت Scripts را بررسی کنید:

به عبارت دیگر بعد از نصب موفق وابستگی های signslR می توانید help کامل را در readme.txt بخوانید. این فایل شامل تمام دستورالعمل های ضروری برای شروع کار با signalR می باشد. ما این دستورالعمل ها را در ادامه توضیح می دهیم:

 

راهنمایی ها (راهنمایی اول؛ Nuget)

اگر با این خطا مواجه شدید" The remote name could not be resolved: 'www.nuget.org" باید تنظیمات Package Manager را تغییر دهید که در مقابل Package Source قرار دارد:

 

می توانید برای حل این مشکل source را از پروتکل https به http تغییر دهید.

راهنمایی 2: OWIN

بخش رفرنس های خود را برای اطمینان از اینکه Owin وجود دارد کنترل کنید، در غیر این صورت به روش زیر عمل کنید:

بر روی References راست کلیک کرده و به صورت زیر ادامه دهید:

Manage NuGet Packages --> Select Online in left side --> search Owin --> Select Owin (Owin IAppBuilder startup interface) --> Install.

 

سپس شما باید Owin  را در بخش reference خود ببینید.

 

مرحله 5: کلاس راه اندازی

برای فعال سازی signalr در پروژه، باید یک کلاس به عنوان startup(راه انداز) ایجاد کنید. ( اگر در نسخه قبلی signalr، منظور من نسخه اول آن است، شما ( )RoutTable.Routes.MapHubs را مورد استفاده قرار داده اید حالا آن را فراموش کرده و فقط کلاس راه اندازی را استفاده کنید. بر روی نام پروژه راست کلیک کرده Add Class->Name:Startup.cs را بزنید.

using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(MvcSignal.Startup))]
namespace MvcSignal
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.MapSignalR();
        }
    }
}

مرحله 6: سازماندهی پایگاه داده با توجه به سناریو ما

مرحله 1-6 : ایجاد "tbl_User"

"tbl_user" کاربر(User) , ادمین (Admin) را در یک جدول جمع آوری خواهد کرد، اگر "AdminCode" توسط تعدادی از جدول های قبلی پر شده باشد، بنابراین admin کسی است که عضو department  دیگر بوده و توسط صفر به کاربران عادی نمایش داده می شود. { “UserID” int + identity=yes and “AdminCode” default value = 0 }

 

مرحله 2-6: ایجاد جدول"tbl_Conversation"

"tbl_Conversation" که داده را از مکالمه بین کاربر و admin جمع آوری می کند،  بعد از اتمام مکالمه پر خواهد شد. { “ConID” int + identity=yes }

مرحله 7: ایجاد کلاس Hub

مرحله 7.1: پوشه Model -> ایجاد کلاس "UserInfo.cs"

public class UserInfo
    {
        public string ConnectionId { get; set; }
        public string UserName { get; set; }
        public string UserGroup { get; set; }

        //if freeflag==0 ==> Busy
        //if freeflag==1 ==> Free
        public string freeflag { get; set; }

        //if tpflag==2 ==> User Admin
        //if tpflag==0 ==> User Member
        //if tpflag==1 ==> Admin

        public string tpflag { get; set; }

        public int UserID { get; set; }
        public int AdminID { get; set; } 
    }

مرحله 7.2: پوشه Model ->ایجاد کلاس "MessageInfo.cs"

public class MessageInfo
  {
      public string UserName { get; set; }

      public string Message { get; set; }

      public string UserGroup { get; set; }

      public string StartTime { get; set; }

      public string EndTime { get; set; }

      public string MsgDate { get; set; }
  }

 

مرحله 7.3: پوشه Controller و ایجاد "HomeController.cs"

public ActionResult Chat()
   {
       ViewBag.Message = "Your contact page.";

       return View();
   }

بر روی Chat راست کلیک کرده و Add View را انتخاب کنید:

مرحله 7.4: ایجاد Chat.cshtml (سمت کلاینت)

@{
    ViewBag.Title = "Chat";
}

<div id="divLogin" class="mylogin">

    User Name:<input id="txtUserName" type="text" /><br />
       Password :   <input id="txtPassword" type="password" /><br />
    <input id="btnLogin" type="button" value="Login" />
    <div id="divalarm"></div>
</div>

<div id="divChat" class="mylogin">

<div id="welcome"></div><br />
<input id="txtMessage" type="text" />
<input id="btnSendMessage" type="button" value="Send" />
<div id="divMessage"></div>

</div>

    <input id="hUserId" type="hidden" />
    <input id="hId" type="hidden" />
    <input id="hUserName" type="hidden" />
    <input id="hGroup" type="hidden" />

@section scripts {
   
    <script src="~/Scripts/jquery-1.8.2.min.js"></script>
    <script src="~/Scripts/jquery.signalR-2.0.1.min.js" type="text/javascript"></script>
    <script src="~/signalr/hubs" type="text/javascript"></script>
    @*<script type="text/javascript" src="@Url.Content("~/signalr/hubs")"></script>*@
    @* <script type="text/javascript" src='<%= ResolveClientUrl("~/signalr/hubs") %>'></script>*@
   
   <script>
       $(function () { //This section will run whenever we call Chat.cshtml page

           $("#divChat").hide();
           $("#divLogin").show();

           var objHub = $.connection.myHub;

           loadClientMethods(objHub);

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

               loadEvents(objHub);

           });
       });

       function loadEvents(objHub) {

           $("#btnLogin").click(function () {

               var name = $("#txtUserName").val();
               var pass = $("#txtPassword").val();

               if (name.length > 0 && pass.length > 0) {
                   // <<<<<-- ***** Return to Server [  Connect  ] *****
                   objHub.server.connect(name, pass);

               }
               else {
                   alert("Please Insert UserName and Password");
               }

           });

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

               var msg = $("#txtMessage").val();

               if (msg.length > 0) {

                   var userName = $('#hUserName').val();
                   // <<<<<-- ***** Return to Server [  SendMessageToGroup  ] *****
                   objHub.server.sendMessageToGroup(userName, msg);

               }
           });

           $("#txtPassword").keypress(function (e) {
               if (e.which == 13) {
                   $("#btnLogin").click();
               }
           });

           $("#txtMessage").keypress(function (e) {
               if (e.which == 13) {
                   $('#btnSendMessage').click();
               }
           });
       }

       function loadClientMethods(objHub) {

           objHub.client.NoExistAdmin = function () {
               var divNoExist = $('<div><p>There is no Admin to response you try again later</P></div>');
               $("#divChat").hide();
               $("#divLogin").show();

               $(divNoExist).hide();
               $('#divalarm').prepend(divNoExist);
               $(divNoExist).fadeIn(900).delay(9000).fadeOut(900);
           }

           objHub.client.getMessages = function (userName, message) {

               $("#txtMessage").val('');
               $('#divMessage').append('<div><p>' + userName + ': ' + message + '</p></div>');

               var height = $('#divMessage')[0].scrollHeight;
               $('#divMessage').scrollTop(height);
           }

           objHub.client.onConnected = function (id, userName, UserID, userGroup) {

               var strWelcome = 'Welcome' + +userName;
               $('#welcome').append('<div><p>Welcome:' + userName + '</p></div>');

               $('#hId').val(id);
               $('#hUserId').val(UserID);
               $('#hUserName').val(userName);
               $('#hGroup').val(userGroup);

               $("#divChat").show();
               $("#divLogin").hide();
           }
       }
    </script>
}

 

مرحله 5-7: ایجاد Model1.edmx

برای داشتن یک روش ساده جهت واکشی و وارد کردن داده به پایگاه داده، یک مدل مانند زیر ایجاد کردیم :

راست کلیک بر روی نام پروژه و مراحل زیر را طی کنید:

project name --> Add New Item --> Select “ADO.NET Entity Data Model” --> Select “Generate From Data Base” --> Make Connection to your data base -->

و جدول خود را انتخاب کنید:

 

 

مرحله 8: یک پوشه ایجاد کنید و نام آن را Hubs قرار دهید سپس یک کلاس ساده ایجاد کرده و نام آن را "MyHub.cs" قرار دهید.

اگر شما آخرین ورژن آپدیت شده Visual Studio را دارید می توانید  add new item را زده و SignalR Hub Class را انتخاب کنید:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.SignalR;
using MvcSignal.Models;
using Microsoft.AspNet.SignalR.Hubs;

namespace MvcSignal
{
    public class MyHub : Hub
    {
        static List UsersList = new List();
        static List<messageinfo> MessageList = new List<messageinfo>();

        //-->>>>> ***** Receive Request From Client [  Connect  ] *****
        public void Connect(string userName, string password)
        {
            var id = Context.ConnectionId;
            string userGroup="";
            //Manage Hub Class
            //if freeflag==0 ==> Busy
            //if freeflag==1 ==> Free

            //if tpflag==0 ==> User
            //if tpflag==1 ==> Admin


            var ctx = new TestEntities();

            var userInfo =
                 (from m in ctx.tbl_User
                  where m.UserName == userName && m.Password == password
                  select new { m.UserID, m.UserName, m.AdminCode }).FirstOrDefault();

            try
            {
                //You can check if user or admin did not login before by below line which is an if condition
                //if (UsersList.Count(x => x.ConnectionId == id) == 0)

                //Here you check if there is no userGroup which is same DepID --> this is User otherwise this is Admin
                //userGroup = DepID
               
               
                if ((int)userInfo.AdminCode == 0)
                {
                    //now we encounter ordinary user which needs userGroup and at this step, 
                    //system assigns the first of free Admin among UsersList
                    var strg = (from s in UsersList where (s.tpflag == "1") 
                    && (s.freeflag == "1") select s).First();
                    userGroup = strg.UserGroup;

                    //Admin becomes busy so we assign zero to freeflag which is shown admin is busy
                    strg.freeflag = "0";

                    //now add USER to UsersList
                    UsersList.Add(new UserInfo { ConnectionId = id, 
                                                 UserID = userInfo.UserID, 
                                                 UserName = userName, 
                                                 UserGroup = userGroup, 
                                                 freeflag = "0", 
                                                 tpflag = "0", });
                    //whether it is Admin or User now both of them has userGroup and I Join this user or admin to specific group 
                    Groups.Add(Context.ConnectionId, userGroup);
                    Clients.Caller.onConnected(id, userName, userInfo.UserID, userGroup);
                }
                else
                {
                    //If user has admin code so admin code is same userGroup
                    //now add ADMIN to UsersList
                    UsersList.Add(new UserInfo { ConnectionId = id, 
                                                 AdminID = userInfo.UserID, 
                                                 UserName = userName, 
                                                 UserGroup = userInfo.AdminCode.ToString(), 
                                                 freeflag = "1", 
                                                 tpflag = "1" });
                    //whether it is Admin or User now both of them has userGroup and I Join this user or admin to specific group 
                    Groups.Add(Context.ConnectionId, userInfo.AdminCode.ToString());
                    Clients.Caller.onConnected(id, userName, userInfo.UserID, userInfo.AdminCode.ToString());
                }                       
            }

            catch
            {
                string msg = "All Administrators are busy, please be patient and try again";
                //***** Return to Client *****
                Clients.Caller.NoExistAdmin();
            }
        }
        // <<<<<-- ***** Return to Client [  NoExist  ] *****

        //--group ***** Receive Request From Client [  SendMessageToGroup  ] *****
        public void SendMessageToGroup(string userName, string message)
        {
            if (UsersList.Count != 0)
            {
                var strg = (from s in UsersList where (s.UserName == userName) select s).First();
                MessageList.Add(new MessageInfo 
                { UserName = userName, Message = message, UserGroup = strg.UserGroup });
                string strgroup = strg.UserGroup;
                // If you want to Broadcast message to all UsersList use below line
                // Clients.All.getMessages(userName, message);

                //If you want to establish peer to peer connection use below line 
                //so message will be send just for user and admin who are in same group
                //***** Return to Client *****
                Clients.Group(strgroup).getMessages(userName, message);
            }
        }
        // <<<<<-- ***** Return to Client [  getMessages  ] *****

        //--group ***** Receive Request From Client ***** 
        //{ Whenever User close session then OnDisconneced will be occurs }
        public override System.Threading.Tasks.Task OnDisconnected()
        {

            var item = UsersList.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId);
            if (item != null)
            {
                UsersList.Remove(item);

                var id = Context.ConnectionId;

                if (item.tpflag == "0")
                {
                    //user logged off == user
                    try
                    {
                        var stradmin = (from s in UsersList where 
                        (s.UserGroup == item.UserGroup) && (s.tpflag == "1") select s).First();
                        //become free
                        stradmin.freeflag = "1";
                    }
                    catch
                    {
                        //***** Return to Client *****
                        Clients.Caller.NoExistAdmin();
                    }                    
                }

                //save conversation to dat abase
            }

            return base.OnDisconnected();
        }
    }
}</messageinfo>

 

قوانین و قراردادها

کلاینت -> سرور // کلاینت به سرور درخواست می فرستد.

1. objHub.server.methodname (نام متد سمت سرور)

دقیقا چنین نامی برای متد سمت سرور وجود دارد.

سرور -> کلاینت // سرور متد کلاینت را فراخوانی می کند ( نام متد سمت کلاینت)

1. Clients.caller.methodname (در این متد فراخوننده فقط کاربری که درخواست را ارسال کرده در نظر دارد).

2. Clients.all.methodname (در این متد تمام کاربرانی که متصل هستند مد نظر است).

3. Clients.Group(groupName).methodname (در این متد فقط کاربری که در گروه مشابه است مد نظر است).

زمانی که در کلاس"MyHub.cs" عبارت Return to Client وجود دارد، به این معناست که شما عملیات Jquery را با نام مشابه در سمت کلاینت نوشته اید.

در واقع تعامل آنها به صورت زیر می شود:

 

مرحله 3: فراخوانی کلاس سرور

راههای مختلفی وجود دارد که هر زمان که شما بخواهید می توانید کلاس سرور را فراخوانی کنید: همیشه در سمت کلاینت باید از قواعد نامگذاری خاصی پیروی کنید، برای نمونه اگر نام کلاس hub شما "MyHub" باشد باید یک نمونه از شیئ خود را از "myHub" ایجاد کنید یا اگر "SendMessageToGroup" را داشته باشید، باید این را از "sendMessageToGroup" فراخوانی کنید بنابراین شبیه به این می شود:

 

 

تست پروژه

برای داشتن نتیجه مشابه، باید پایگاه داده ای شبیه به آن چیزی که در مرحله هفتم توضیح دادم داشته باشید.

مورد 1:

برنامه تست: اگر مشتری تلاش کند که login شود و Admin ای وجود نداشته باشد، آنگاه سیستم یک هشدار(Alarm) نشان می دهد.

مراحل تست

1. پروژه را اجرا کنید.

2. UserName: mahsa

3. Password:123

4. خروجی مورد انتظار: سیستم هشدار را نشان می دهد.

 

مورد 2:

برنامه تست: در اینجا حداقل یک Admin آزاد وجود دارد و سپس یک مشتری وارد شده و بعد ادمین اول به اولین کلاینتی که نیاز به کمک داشته باشد اختصاص داده می شود.

مراحل تست:

1. پروژه را اجرا کنید

2. UserName: admin1

3. ورود:(admin1 به عنوان اولین ادمین می باشد)

5. URL را در یک مرورگر دیگر Copy کنید:

6. UserName:mahsa

7. Passwprd:123

8. ورود به چت

9. اگر "mahsa" پیامی بفرستد، "admin1" آن را خواهد دید، چرا که هردو در یک گروه هستند. زمانی که اولین کلاینت به درستی وارد می شود آنگاه اولین Admin آزاد به آن اختصاص می یابد.

10. URL را در مرورگر دیگر کپی کنید.

11. UserName: kashi

12. Password:123

13. ورود به چت (Kashi به عنوان کلاینت دوم در نظر گرفته می شود).

14. سیستم هشداری با این متن نشان می دهد "there is no admin then system shows an alarm".

15. URl را در مرورگر دیگر کپی کنید.

16. UserName: admin2

17. Password:123

18. "Kashi" و "admin2" نمی توانند مکالمه بین "admin1" و "mahsa" را ببیند.

نکات:

UI .1 های مختلف برای ادمین و کاربر، و نمایش انتظار کاربر برای ادمین

اگر نیاز به UI های مختلف برای برای ادمین از کاربر دارید، باید div متفاوت برای کاربر و ادمین با css های متفاوت قرار دهید و با ویژگی کلاس مجزا برای آنها اختصاص دهید.

زمانی که می خواهید پیغامی به ادمین از کلاس hub به متد کلاینت متفاوت بفرستید از متدهایی مانند objHub.client.getMessagesAdmin و برای کاربر objHub.client.getMessagesUser استفاده می کنیم.

در  Chat.cshtml این متدها را با UI متفاوت توسط div متفاوت "divMessageAdmin" و "divMessageUser" ارائه شدند و شما باید این div ها را توسط پیغام مناسب پر کنید.

پس مراحل زیر را دنبال کنید:

1. ایجاد div های مختلف:

<div class="Admin" id="divMessageAdmin"></div>
<div Class="User" id="divMessageUser"></div>

2. در کلاس Hub -> SendMessageToGroup بروید و کنترل کنید که آیا کاربر Admin است یا خیر.

Clients.Group(strgroup).getMessagesAdmin(userName, message);

کنترل کنید که کاربر عادی است یا خیر

Clients.Group(strgroup).getMessagesUser(userName, message);

 

1. در Chat.cshtml:

اگر کاربر Admin باشد:

       objHub.client.getMessagesAdmin = function (userName, message) {
 
       $("#txtMessage").val('');
       $('#divMessageAdmin').append('<div><p>' + userName + ': ' + message + '</p></div>');
 
       var height = $('#divMessageAdmin')[0].scrollHeight;
       $('#divMessageAdmin').scrollTop(height);
   }

اگر کاربر عادی باشد:

objHub.client.getMessagesUser = function (userName, message) 
    $("#txtMessage").val('');
       $('#getMessagesUser').append('<div><p>' + userName + ': ' + message + '</p></div>');
 
       var height = $('#getMessagesUser')[0].scrollHeight;
       $('#getMessagesUser').scrollTop(height);
   }

 

برای دیدن حالت انتظار کاربران در UI مربوط به Admin:

1. در کلاس Hub->Connect و کد زیر را مشاهده می کنید:

catch
          {
              string msg = "All Administrators are busy, please be patient and try again";
              // Return to Client 
              Clients.Caller.NoExistAdmin();
          }

لطفا username را برای کاربری که منتظر ادمین آزاد است، بفرستید:

Clients.Caller.NoExistAdmin(username);

2. در Chat.cshtml

objHub.client.NoExistAdmin = function (username) {
var divNoExist = $('

<div>
<p>There is no Admin to response you try again later</p>
</div>

<p>'); $("#divChat").hide(); $("#divLogin").show(); $("#divWaitingUser").append('</p>

<div>
<p>' + userName + '</p>
</div>

<p>'); $(divNoExist).hide(); $('#divalarm').prepend(divNoExist); $(divNoExist).fadeIn(900).delay(9000).fadeOut(900); }</p>

<p> </p>

 

فایل های ضمیمه
دانلود نسخه ی PDF این مطلب