ایجاد یک برنامه چت Real-Time با استفاده از SignalR

پنجشنبه 27 اسفند 1394

در این مقاله قصد داریم یک برنامه چت ساده را در MVC با استفاده از قدرت SignalR ایجاد کنیم. SignalR امکان جالب و جدیدی در ASP.net است. API ساده ای را ارائه می دهد که امکان ایجاد برنامه های real-time برای زمانی که سرور نیاز دارد به طور مداوم داده ها را به سرویس گیرنده ارسال کند ، برای ما فراهم کرده است.

ایجاد یک برنامه چت Real-Time با استفاده از  SignalR

SignalR  امکان جالب و جدیدی در  ASP.net است.  API ساده ای را ارائه می دهد که امکان ایجاد برنامه های real-time برای زمانی که سرور نیاز دارد به طور مداوم داده ها را به سرویس گیرنده ارسال کند ، برای ما فراهم کرده است. این برنامه ها معمولا Chat ، news feed ، notification و بازی های چند کاربره هستند.

در این مقاله قصد داریم یک برنامه چت ساده را در  MVC با استفاده از قدرت SignalR ایجاد کنیم. برای شروع یک پروژه جدید  MVC ایجاد کرده و در پنجره انتخاب Template ، گزینه  Basic را انتخاب میکینم.

در تب  Tools گزینه  Library Pakage Manager و سپس Manage Nuget Pakage را انتخاب میکنیم. در اینجا  Microsoft.Aspnet.SignalR را جستجو و نصب میکنیم.

بعد از نصب باید قادر باشید dll های جدید اضافه شده را در پوشه references مشاهده کنید.

 

کنترلر جدیدی با نام  ChatRoomController ایجاد میکنیم که کدها در آن به صورت زیر قرار می گیرند.

namespace MvcDemo.Controllers
{
    public class ChatRoomController : Controller
    {
        //
        // GET: /ChatRoom/

        public ActionResult SignalRChat()
        {
            return View();
        }

    }
}

 

همانطور که میبینید فقط یک متد با نام  SignalRChat داریم که یک View  را باز میگرداند.

 

برای ایجاد View  برروی این متد راست کلیک کرده و آن را اضافه میکنیم.  View  را به صورت زیر تنظیم میکنیم.


<div id="container">
    <input type="hidden" id="nickname" />
    <div id="chatlog"></div>
    <div id="onlineusers">
        <b>کاربران آنلاین</b>
    </div>
    <br/>
    <div id="chatarea">
        <div class="messagelog">
            <textarea spellcheck="true" id="message" class="messagebox"></textarea>
        </div>
        <div class="actionpane">
            <input type="button" id="btnsend" value="ارسال" />
        </div>
        <div class="actionpane">
            <select id="users">
                <option value="All">همه</option>
            </select>
        </div>
    </div>
    <div id="dialog" title="نام خود را برای شروع یک چت وارد کنید.">
        <input type="text" id="nick" />
    </div>
</div>

 

Hub قسمت مرکزی  SignalR است . همانند کنترلر در MVC ، یک hub مسئولیت دریافت ورودی و تولید خروجی را برای سرویس گیرنده به عهده دارد. بر روی آن پوشه راست کلیک کرده و از قسمت Add ، کلاس  ASP.net SignalR Hub را با نام  ChatHub اضافه کنید.

 

کدهای آن را در زیر مشاهده میکنید.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.SignalR;
using System.Collections.Concurrent;
using System.Threading.Tasks;

namespace MvcDemo.Hubs
{
    public class ChatHub : Hub
    {
        static ConcurrentDictionary<string, string> dic = new ConcurrentDictionary<string, string>();

        public void Send(string name, string message)
        {
            Clients.All.broadcastMessage(name, message);
        }

        public void SendToSpecific(string name, string message, string to)
        {
            // Call the broadcastMessage method to update clients.
            Clients.Caller.broadcastMessage(name, message);
            Clients.Client(dic[to]).broadcastMessage(name, message);
        }

        public void Notify(string name, string id)
        {
            if (dic.ContainsKey(name))
            {
                Clients.Caller.differentName();
            }
            else
            {
                dic.TryAdd(name, id);
                foreach (KeyValuePair<String, String> entry in dic)
                {
                    Clients.Caller.online(entry.Key);
                }
                Clients.Others.enters(name);
            }
        }

        public override Task OnDisconnected()
        {
            var name = dic.FirstOrDefault(x => x.Value == Context.ConnectionId.ToString());
            string s;
            dic.TryRemove(name.Key, out s);
            return Clients.All.disconnected(name.Key);
        }

    }
}

 

توجه داشته باشید که کلاس  ChatHub از  Microsoft.Aspnet.SignalR.Hubs.Hub ارث بری میکند. در نظر داشته باشید که متدهای سراسری تعریف شده در کلاس Hub ، برای فراخوانی از کد سرویس گیرنده تعیین شده اند. درواقع نتیجه به صورت یک پاسخ به سرویس گیرنده ارسال می شود.

 

در زمان اجرا یک فایل جاوااسکریپت به صورت داینامیک تولید می شود که شامل پیاده سازی متدهای عمومی تعریف شده سمت سرویس گیرنده در  Hub است. این متدهای سمت سرویس گیرنده برای متدهای سمت سرور به عنوان یک  ProXy عمل خواهند کرد.  شما یک پیاده سازی از متدهای سراسری از  Hub را درون کد سمت سرویس گیرنده می نویسید بنابراین Hub می تواند آن را فراخوانی کرده و پاسخی را فراهم کند.

Dictionary جایی است که لیستی از کاربران در دسترس را که به چت وارد شده اند نگهداری میکند. ما از  dictionary  استفاده کرده ایم و براحتی می توانیم آیتم ها را از آن با استفاده از Key/Value حذف و اضافه کنیم.

متد  Send() پیام را برای همه کاربرانی که نام خود را ارسال کرده اند پخش میکند.  متد  SendToSpecific() ، پیام را برای کاربر خاصی ارسال میکند. متد  Notify() هنگامی که کاربر جدیدی به چت روم وارد شود و به لیست اعضای آنلاین اضافه شود، آن را اطلاع می دهد. متد  OnDisconnected() اگر کسی چت روم را ترک کند ، آن کاربر را حذف خواهد کرد. این متد ناهمزمان است و هر وقت که ارتباط کاربر قطع شود فراخوانی می شود (برای مثال وقتی که مرورگر بسته شود).

برای پیاده سازی کدهای سمت سرویس گیرنده ، کدهای زیر را در  View  اضافه میکنیم.

@section scripts{
             @Scripts.Render("~/Scripts/jquery-ui-1.9.2.min.js")
             @Scripts.Render("~/Scripts/jquery.signalR-1.0.1.min.js")
             @Scripts.Render("/signalr/hubs")
  
          <script type="text/javascript">

              $(function () {
                  showModalUserNickName();
              });

              function showModalUserNickName() {
                  $("#dialog").dialog({
                      modal: true,
                      buttons: {
                          Ok: function () {
                              $(this).dialog("close");
                              startChartHub();
                          }
                      }
                  });
              }
    
              function startChartHub() {
                  var chat = $.connection.chatHub;

                  // Get the user name.
                  $('#nickname').val($('#nick').val());
                  chat.client.differentName = function (name) {
                      showModalUserNickName();
                      return false;
                      // Prompts for different user name
                      $('#nickname').val($('#nick').val());
                      chat.server.notify($('#nickname').val(), $.connection.hub.id);
                  };

                  chat.client.online = function (name) {
                      // Update list of users
                      if (name == $('#nickname').val())
                          $('#onlineusers').append('<div class="border" style="color:green">You: ' + name + '</div>');
                      else {
                          $('#onlineusers').append('<div class="border">' + name + '</div>');
                          $("#users").append('<option value="' + name + '">' + name + '</option>');
                      }
                  };

                  chat.client.enters = function (name) {
                      $('#chatlog').append('<div style="font-style:italic;"><i>' + name + ' joins the conversation</i></div>');
                      $("#users").append('<option value="' + name + '">' + name + '</option>');
                      $('#onlineusers').append('<div class="border">' + name + '</div>');
                  };
                  // Create a function that the hub can call to broadcast chat messages.
                  chat.client.broadcastMessage = function (name, message) {
                      //Interpret smileys
                      message = message.replace(":)", "<img src=\"/images/smile.gif\" class=\"smileys\" />");
                      message = message.replace("lol", "<img src=\"/images/laugh.gif\" class=\"smileys\" />");
                      message = message.replace(":o", "<img src=\"/images/cool.gif\" class=\"smileys\" />");

                      //display the message
                      $('#chatlog').append('<div class="border"><span style="color:red">' + name + '</span>: ' + message + '</div>');
                  };

                  chat.client.disconnected = function (name) {
                      //Calls when someone leaves the page
                      $('#chatlog').append('<div style="font-style:italic;"><i>' + name + ' leaves the conversation</i></div>');
                      $('#onlineusers div').remove(":contains('" + name + "')");
                      $("#users option").remove(":contains('" + name + "')");
                  }

                  // Start the connection.
                  $.connection.hub.start().done(function () {
                      //Calls the notify method of the server
                      chat.server.notify($('#nickname').val(), $.connection.hub.id);

                      $('#btnsend').click(function () {
                          if ($("#users").val() == "All") {
                              // Call the Send method on the hub. 
                              chat.server.send($('#nickname').val(), $('#message').val());
                          }
                          else {
                              chat.server.sendToSpecific($('#nickname').val(), $('#message').val(), $("#users").val());
                          }
                          // Clear text box and reset focus for next comment. 
                          $('#message').val('').focus();
                      });

                  });
              }

            </script>
     }

 

در بالای آن نیاز به یک  @RenderSection داریم که منابع jQuery UI  و اسکریپتهای   SignalR در آن تعریف شوند. منابع باید به ترتیب زیر تعریف شوند:

 

jQuery script

jQueryUI

SignalR script

SignalR/Hubs

اسکریپت سفارشی شما

 

شاید تعجب کنید که منابع jQuery script را در کد بالا قرار ندادیم. دلیل آن این است که هنگام ایجاد پروژه با  Template انتخاب شده، به صورت خودکار این منابع در _Layout.cshtml  قبل از بسته شدن تگ  <body> قرار گرفته است. توجه داشته باشید که اضافه کردن jQuery Script در  View  در هنگام اجرای برنامه شما که از SignalR استفاده میکند باعث خطا می شود. زیرا منابع  jQuery Script تکراری خواهند شد و ترتیب آنها تغییر خواهد کرد و اسکریپت SignalR باعث بروز خطا خواهد شد.

تابع showModalUserNickName() دیالوگ  jQuery UI را فراخوانی میکند. این تابع هنگامی که مرورگر برای دریافت نام کاربر بارگذاری می شود قبل از پیوستن به چت فراخوانی می شود. بعد از وارد کردن نام ، تابع StartChatHub() فراخوانی می شود. این تابع دقیقا جایی است که  پیاده سازی چت تعریف شده است. منطق این است که ابتدا یک اتصال  Proxy برای  Hub ایجاد شود. پس از اتصال می توانیم به متدهای  Public تعریف شده در کلاس  Hub خود دسترسی داشته باشیم. قدم بعدی دریافت نام کاربر است. اگر کاربر در لیست موجود باشد باید نام دیگری را وارد کند . سپس متد Notify() برای اضافه کردن کاربر جدید به دیکشنری و اطلاع به دیگر کاربران که در چت روم هستند فراخوانی می شود.  تابع بعدی پنل گزارش پیام ها، کاربران در دسترس و کاربران آنلاین را هنگامی که یک کاربر اضافه می شود آپدیت میکند.

پس از آن تابع broadCastMessage() برای نمایش پیام های کاربران در گزارش پیام ها فراخوانی می شود. همانطور که مشاهده میکنید بعضی کدها را برای تابعی که  Smiley ها را ترجمه میکند اضافه کردیم. تابع  Client.disconnected() مسئول آپدیت کردن  UI و لیست کاربرانی است که چت روم را ترک کرده اند. و در آخر تابع hub.start() برای شروع اتصال بین سرویس گیرنده و  hub فراخوانی می شود. متد شامل پیاده سازی ارسال پیام به دیگر کاربران یا کاربر خاصی است.

یک فایل  CSS تحت پوشه Content با نام  chat.css ایجاد کرده و استایل های تعریف شده زیررا در آن قرار می دهیم.

 

#container {
  width: 400px ;
  margin-left: auto ;
  margin-right: auto ; 
}
#chatlog {
  width:250px;
  background-color:aliceblue;
  float: left;
}
#onlineusers {
  float: right;
  width: 100px;
  background-color:#D4D4D4;
}
#chatarea {
  clear:both; 
  width:200px;
}
#chatarea .messagelog {
  float:left; 
  height: 90%; 
  top: 10%; 
  position: relative;
}
#chatarea .messagelog .messagebox {
  width:400px; 
  height: 80%;
}
#chatarea .actionpane {
  position:relative; 
  float: left;
}
.smileys {
  width:14px;
  height:14px;
}

 

پوشه دیگری در روت پروژه با نام Images ایجاد کرده و استایلی ها را در آن قرار می دهیم.

 

سپس منابع زیر را در تگ  <head> در  _Layout.cshtml قرار می دهیم.

<link href="~/Content/chat.css" rel="stylesheet" />
    <link href="~/Content/themes/base/jquery-ui.css" rel="stylesheet" />

مسیر پیش فرض در  APP_Start -> RouteConfig آپدیت میکنیم که به صورت خودکار صفحه چت  SignalR را هنگام اجرای برنامه نمایش می دهد.

public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "ChatRoom", action = "SignalRChat", id = UrlParameter.Optional }
            );
        }

در پایان مهمترین چیز اضافه کردن کد زیرتحت رویداد  Application_Start در  Global.asax.cs است .

  RouteTable.Routes.MapHubs();

حتما آن را قبل از  RegisterRoutes() فراخوانی کنید.

خروجی به صورت زیر خواهد بود :

فایل های ضمیمه

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید