+3

Real-time application với SignalR

1. Real-time application là gì?

  • Real-time application là một loại ứng dụng cung cấp và xử lý thông tin cho người dùng ngay lập tức, không có sự chờ đợi đáng kể.
  • Một số real-time application phổ biến như:
    • Chat trực tuyến: Ứng dụng chat trực tuyến nơi người dùng có thể gửi và nhận tin nhắn mà không cần reload lại trang.
    • Dashboards and monitoring apps: hiển thị thông tin thời gian thực như dữ liệu từ cảm biến, dữ liệu thị trường tài chính, hoặc các thông báo hệ thống.
    • Notification apps: Các ứng dụng yêu cầu thông báo hệ thống như Social networks, email, chat, games, travel alerts,...
    • Và các loại ứng dụng khác. image.png

2. SignalR là gì?

  • SignalR là một thư viện open-source giúp xây dựng ứng dựng thời gian thực (real-time application), cho phép gửi và nhận dữ liệu từ server đến clients và ngược lại.
  • Định nghĩa ở trang Microsoft:

ASP.NET Core SignalR is an open-source library that simplifies adding real-time web functionality to apps. Real-time web functionality enables server-side code to push content to clients instantly.

image.png

  • Để hiểu rõ signalR ta sẽ xem cách hoạt động của giao thức http. Là một giao thức truyền tải dữ liệu giữa máy khách và máy chủ trên World Wide Web. Nó là giao thức không trạng thái, có nghĩa là mỗi yêu cầu từ máy khách đến máy chủ được xử lý độc lập và không giữ lại thông tin trạng thái trước đó. Lúc client gửi request tới server và server sẽ trả response về cho client. Client nhận data xong sẽ đóng kết nối. Lần sau khi cần thì sẽ mở kết nối để gửi request. Không thích hợp cho ứng dụng real-time do phải có action từ client để gửi yêu cầu và đóng mở kết nối liên tục. image.png

3. SignalR Transports type

  • SignalR Transports type là các cơ chế hoặc giao thức được sử dụng để thiết lập và duy trì kết nối giữa máy khách và máy chủ. SignalR hỗ trợ nhiều loại phương tiện truyền tải để đảm bảo tính linh hoạt và tương thích trên nhiều môi trường. Dưới đây là một số loại phương tiện truyền tải phổ biến trong SignalR:
    • WebSockets: là giao thức cung cấp kết nối liên tục giúp gửi và nhận dữ liệu 2 chiều giữa clients và server. Các loại ứng dụng sử dụng websocket như chat trực tuyến, Bảng Điều Khiển Thời Gian Thực, Tích Hợp Thông Báo Hệ Thống... image.png
    • Server-Sent Events (SSE): SSE là một giao thức trong đó máy chủ có thể gửi sự kiện và dữ liệu mới đến máy khách một cách tự động. Nó là một phương tiện truyền tải đơn hướng, máy khách không thể gửi dữ liệu đến máy chủ. Phù hợp với các ứng dụng như: Cập nhật sự kiện thể Thao và Giải Trí Trực Tuyến tới người dùng image.png
    • Long Polling: Long Polling là một kỹ thuật trong đó máy khách gửi một yêu cầu đến máy chủ, và máy chủ giữ kết nối mở cho đến khi có dữ liệu mới hoặc hết thời gian chờ. Sau đó, máy khách và máy chủ sẽ thiết lập một kết nối mới. SignalR với phương tiện truyền tải Long Polling thường được sử dụng trong các tình huống mà việc duy trì kết nối liên tục không phải là ưu tiên, nhưng vẫn cần tính năng thời gian thực. Phương tiện truyền tải Long Polling được sử dụng khi không khả dụng hoặc không thích hợp với các phương tiện truyền tải khác như WebSockets hoặc Server-Sent Events. Mặc dù có thể tăng độ trễ so với các phương tiện truyền tải đầy đủ, nhưng nó vẫn cung cấp tính năng thời gian thực mà không yêu cầu duy trì kết nối liên tục. image.png
  • Cách cấu hình transport type cho singalR. Default transport type là websocket
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // ... other middleware configuration

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapHub<ChatHub>("/chathub")
            .RequireCors("AllowAll")
            .WithOrigins("http://localhost:3000") // Replace with your client app URL
            .WithMethods("GET", "POST")
            .AllowCredentials()
            .WithHubOptions(hubOptions =>
            {
                hubOptions.Transports = Microsoft.AspNetCore.Http.Connections.HttpTransportType.WebSockets |
                                        Microsoft.AspNetCore.Http.Connections.HttpTransportType.LongPolling;
            });
    });
}

4. Hubs trong signalR

image.png

  • Hub là một đối tượng phía server cung cấp một cách trung tâm để giao tiếp hai chiều giữa các client và server. Hubs cho phép truyền tin nhắn từ server đến client và ngược lại, tạo ra một mô hình giao tiếp realtime giữa các thành phần khác nhau của ứng dụng web.
  • Hub sử dụng mô hình "publish-subscribe" để thông báo cho các client về các sự kiện hoặc thay đổi xảy ra phía server và ngược lại. Việc này giúp tạo ra trải nghiệm tương tác thời gian thực cho người dùng, thích hợp cho các ứng dụng đòi hỏi cập nhật nhanh chóng và đồng bộ giữa client và server.

5. Flow cơ bản khi làm việc với SignalR (typical flow)

  1. Tạo SignalR Hub
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;

public class ChatHub : Hub
{
    public async Task SendMessage(string user, string message)
    {
        // Broadcast the message to all clients
        await Clients.All.SendAsync("ReceiveMessage", user, message);
    }
}
  1. Configure SignalR trong Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
    services.AddSignalR();
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // ... other middleware configuration

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapHub<ChatHub>("/chathub");
        // ... other endpoint mappings
    });
}
  1. Thêm client side signalR: Khởi tạo project ở client (có thể sử dụng js hoặc framework FE)
  2. Kết nối signalR hub với Client qua Javascript
const connection = new signalR.HubConnectionBuilder()
    .withUrl("/chathub") // Replace with the actual Hub URL
    .build();

connection.start().then(function () {
    console.log("Connection to Hub established");
}).catch(function (err) {
    console.error(err.toString());
});
  1. Handle Hub Events
connection.on("ReceiveMessage", function (user, message) {
    console.log(`${user}: ${message}`);
    // Update your UI or perform other actions based on the received message
});
  1. Invoke Hub Method connection.invoke("SendMessage", "John", "Hello, SignalR!").catch(function (err) { console.error(err.toString()); });

6. Khác nhau giữa connection.invoke và connection.send

  • invoke được sử dụng khi bạn muốn gửi yêu cầu từ client tới server và nhận kết quả trả về từ server. Nó trả về một Promise, cho phép bạn sử dụng async/await để xử lý kết quả.
connection.invoke("MethodName", arg1, arg2)
    .then(function (result) {
        // Xử lý kết quả từ server
    })
    .catch(function (error) {
        // Xử lý lỗi nếu có
    });
try {
    let result = await connection.invoke("MethodName", arg1, arg2);
    // Xử lý kết quả từ server
} catch (error) {
    // Xử lý lỗi nếu có
}
  • send được sử dụng khi bạn muốn gửi yêu cầu từ client tới server nhưng không mong đợi một phản hồi trực tiếp từ server. Nó không trả về một Promise.
connection.send("MethodName", arg1, arg2);
  • Chọn giữa invoke và send phụ thuộc vào yêu cầu cụ thể của ứng dụng. Nếu bạn cần biết kết quả của yêu cầu (như việc gửi tin nhắn và nhận được xác nhận), bạn nên sử dụng invoke. Ngược lại, nếu yêu cầu không đòi hỏi phản hồi trực tiếp, send có thể là lựa chọn tốt hơn để giảm độ trễ và tăng hiệu suất.

7. 1 số flow gửi message cơ bản

  • Gửi Tới Tất Cả Các Client (Using Clients.All)
// Server-side Hub method using Clients.All
public async Task SendBroadcastMessage(string message)
{
    // Broadcast the message to all connected clients
    await Clients.All.SendAsync("ReceiveBroadcastMessage", message);
}
  • Gửi Tới Một Client Cụ Thể (Using Clients.Client). Ở đây, targetUserId là một định danh duy nhất của client, có thể là Connection ID hoặc một giá trị đặc biệt nào đó bạn đang sử dụng để xác định client.
// Server-side Hub method using Clients.Client
public async Task SendPrivateMessage(string targetUserId, string message)
{
    // Send the private message to a specific client
    await Clients.Client(targetUserId).SendAsync("ReceivePrivateMessage", message);
}
  • Gửi Tới Một Nhóm (Using Clients.Group):
// Server-side Hub method using Clients.All
public async Task SendBroadcastMessage(string message)
{
    // Broadcast the message to all connected clients
    await Clients.All.SendAsync("ReceiveBroadcastMessage", message);
}

8. Group trong signalR

  • Group là một khái niệm giúp tổ chức và quản lý các kết nối (connections) của các client. Mỗi group có một tên duy nhất và các kết nối có thể tham gia hoặc rời khỏi group. Việc sử dụng groups cho phép gửi tin nhắn tới tất cả các thành viên trong một nhóm, quản lý trạng thái nhóm, và thực hiện tương tác giữa các thành viên. Các phương thức như AddToGroupAsync và RemoveFromGroupAsync giúp thêm và loại bỏ client khỏi group, trong khi Clients.Group(groupName).SendAsync() cho phép gửi tin nhắn đến một nhóm cụ thể.

  • Một số phương thức cơ bản như:

//Thêm client vào group
await Groups.AddToGroupAsync(Context.ConnectionId, "groupName");
//Rời khỏi group
await Groups.RemoveFromGroupAsync(Context.ConnectionId, "groupName");
//Gửi Tin Nhắn Đến Group
await Clients.Group("groupName").SendAsync("ReceiveMessage", message);
//Kiểm Tra Client Thuộc Group
bool isInGroup = await Groups.IsInGroupAsync(Context.ConnectionId, "groupName");

9. Xây dựng ứng dụng chat đơn giản với signalR sử dụng group

  • Tạo hub
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;

public class ChatHub : Hub
{
    public async Task SendMessage(string user, string message)
    {
        await Clients.All.SendAsync("ReceiveMessage", user, message);
    }
}
  • Cấu hình signalR
public void ConfigureServices(IServiceCollection services)
{
    services.AddSignalR();
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // ... other middleware configuration

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapHub<ChatHub>("/chathub");
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
}
  • Tạo giao diện client
@{
    ViewData["Title"] = "Simple Chat App";
}

<div>
    <input type="text" id="userInput" placeholder="Enter your name" />
    <input type="text" id="messageInput" placeholder="Type your message" />
    <button id="sendButton">Send</button>
</div>

<div id="chat">
    <ul id="messagesList"></ul>
</div>

@section scripts {
    <script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/3.1.9/signalr.min.js"></script>
    <script>
        const connection = new signalR.HubConnectionBuilder()
            .withUrl("/chathub")
            .build();

        connection.start().then(function () {
            console.log("Connection to Hub established");
        }).catch(function (err) {
            console.error("Connection failed:", err.toString());
        });

        document.getElementById("sendButton").addEventListener("click", function () {
            const user = document.getElementById("userInput").value;
            const message = document.getElementById("messageInput").value;

            connection.invoke("SendMessage", user, message).catch(function (err) {
                console.error(err.toString());
            });

            document.getElementById("messageInput").value = "";
        });

        connection.on("ReceiveMessage", function (user, message) {
            const messagesList = document.getElementById("messagesList");
            const li = document.createElement("li");
            li.textContent = `${user}: ${message}`;
            messagesList.appendChild(li);
        });
    </script>
}

10. Tổng kết

  • Qua bài viết này hy vọng mọi người hiểu thêm về real-time application và cách xây dựng nó sử dụng signalR.
  • Cảm ơn mọi người đã xem bài viết. Chúc anh em một ngày làm việc hiệu quả và đầy năng lượng. Nếu có thắc mắc về các phần trong bài này mọi người có thể inbox qua facebook:https://www.facebook.com/FriendsCode-108096425243996 Mình sẽ giải đáp thắc mắc trong tầm hiểu biết. Cảm ơn mọi người!
  • Hoặc liên hệ mình qua facebook cá nhân: https://www.facebook.com/Flamesofwars/

P/S: Team mình có nhận làm freelancer nên bạn nào cần thì liên hệ nha. (Tech-stack: .NET, Nodejs, ReactJs, Vue)

11. Tham khảo:


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.