首頁

目前文章總數:157 篇

  

最後更新:2024年 12月 07日

0048. .Net Core 實現SignalR架設服務端,推播訊息到客戶端(前端、Android)

日期:2023年 08月 27日

標籤: C# Asp.net Core Web MVC Web Java JavaScript Browser Html SignalR

摘要:C# 學習筆記


應用所需:1. Visual Studio 2022
     2. .net core Web專案 (Website MVC 實現伺服器端、客戶端)
     3. Android Studio(Android java 實現客戶端)
範例檔案:前端、伺服器連結
範例檔案:Android連結
解決問題:1. .net Core如何架設SignalR伺服器端,並且每5秒輪詢推播訊息給客戶端
     2. Web(前端)如何連到SignalR
     3. Android如何連到SignalR
基本介紹:本篇分為四大部分。
第一部分:架設SignalR伺服器端
第二部分:部署IIS
第三部分:前端實現SignalR
第四部分:Android實現SignalR






第一部分:架設SignalR伺服器端

Step 1:範例專案架構


1. SignalR Hub 配置SignalR 可註冊的名稱與實際行為
2. 背景服務 實現推播,客戶端註冊後會從背景服務持續推播資料給客戶端
3. 初始化 使用SignalR的基本配置
4. 檢視器 用Razor實現前端註冊SignalR後接收資料的渲染




Step 2:Nuget安裝

要架設SignalR伺服器首先要先進行Nuget安裝
這邊安裝7.0.0版本,伺服器端的版本建議與客戶端一致,否則容易發生異常

Microsoft.AspNetCore.SignalR.Common



Step 3:SignalR Hub-配置

建立一個UpdateHub.cs,可供註冊SendUpdate方法,並且會回傳Message。
內容如下:

using Microsoft.AspNetCore.SignalR;

namespace NetCoreSignalRWebSiteExample.SignalR
{
    public class UpdateHub : Microsoft.AspNetCore.SignalR.Hub
    {
        //事件名稱SendUpdate 行為:回傳message
        public async Task SendUpdate(string message)
        {
            await Clients.All.SendAsync("SendUpdate", message);
        }
    }
}



Step 4:背景服務-配置

伺服器建立一個背景服務,會在每5秒自動將訊息推播給用戶,並且將Server上的count累加1
實現資訊變化,用戶能即時收到的效果

public class PageBackroundUpdaterService : BackgroundService
{
    private readonly IHubContext<UpdateHub> _hubContext;
    // 1. 配置變數,版本號、間隔時間
    private int _versionNumber = 0;
    private readonly int _second = 5 * 1000;//5秒
    public PageBackroundUpdaterService(IHubContext<UpdateHub> hubContext
        )
    {
        _hubContext = hubContext;
        _versionNumber = 0;
    }
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            // 2. 編寫返回資訊
            var data = $@"回報時間:{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") } 版本號:{_versionNumber}";
            // 3. 推播訊息給客戶端
            await _hubContext.Clients.All.SendAsync("SendUpdate", data);
            // 4. 輪詢時間
            await Task.Delay(_second, stoppingToken);
            _versionNumber++;
        }
    }
    public override void Dispose()
    {
        base.Dispose();
    }
}



Step 5:初始化-配置

打開Program.cs進行以下配置,讓SignalR可以生效,並且有對應的Url進行WebSocket連線
3. 配置 SignalR 路由 : “UpdateHub” 表示提供Hub註冊名稱

using NetCoreSignalRWebSiteExample.Background;
using NetCoreSignalRWebSiteExample.SignalR;

var builder = WebApplication.CreateBuilder(args);

\\配置其他Service...

// 1. 添加 SignalR
builder.Services.AddSignalR();

// 2. 增加背景服務 - 輪詢 Push SignalR
builder.Services.AddHostedService<PageBackroundUpdaterService>();

var app = builder.Build();

\\配置其他app configure...

app.UseEndpoints(endpoints =>
{
    //3. 配置 SignalR 路由
    endpoints.MapHub<UpdateHub>("UpdateHub");
});

app.Run();



Step 6:檢視器-配置

於第三部分. 前端實現SignalR 章節的內容





第二部分:部署IIS

Step 1:發佈檔案-1

先對專案按下滑鼠右鍵
選擇發佈


Step 2:發佈檔案-2

執行發佈


Step 3:搬移檔案

發布後的檔案搬移到資料夾下,範例放在D:\IIS_Server


Step 4:IIS配置-1

選擇新增網站


Step 5:IIS配置-2

黑框的部分自行配置,範例中Port為7531


Step 6:IIS配置-3

完成後,啟動該網站


Step 7:找出本機IP位置

開啟命令提示字元
輸入以下:

ipconfig/all


可以得到本機的IP位置


Step 8:測試網站

打開瀏覽器,輸入以下,可以發現SignalR是正常的

http://192.168.42.177:7531/UpdateHub


頁面放著可以持續得到Server端回傳的資料




第三部分:前端實現SignalR

Step 1:檢視器-配置

對應第一部分 => 1.專案架構 => 4. 檢視器
以下是完整代碼


@{
    ViewData["Title"] = "SignalR連線測試頁面";
}
<!DOCTYPE html>
<html>
<head>    
    <!-- 1. ※此CDN 有可能失效,最好是加入至專案中 -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/7.0.0/signalr.js"></script>
</head>
<body>
    <!-- 2. 接收內容 -->
    <div id="updateContainer"></div>

    <script>
        // 3. 預設頁面值
        var updateContainer = document.getElementById("updateContainer");
        updateContainer.innerHTML = `<p>New update: 初始化 </p>`;

        // 4. 創建 SignalR 連接
        const connection = new signalR.HubConnectionBuilder()
            .withUrl("UpdateHub")
            .build();

        // 5. 監聽 SendUpdate 事件
        connection.on("SendUpdate", (message) => {
            const updateContainer = document.getElementById("updateContainer");
            updateContainer.innerHTML += `<p>New update: ${message}</p>`;
        });

        // 6. 啟動
        connection.start()
            .then(() => {
                console.log("連接 SignalR 成功");
            })
            .catch((error) => {
                console.log("錯誤訊息:" + error);
            });
    </script>
</body>

</html>



Step 2:客戶端對應

伺服器端安裝的是SignalR 7.0.0 版本
因此客戶端需要使用對應的SignalR.js

<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/7.0.0/signalr.js"></script>



Step 3:微軟CDN對應

如果為了測試方便,不將檔案引入前端框架,可以查詢CDN的位置,將對應.js引入前端代碼中

https://learn.microsoft.com/zh-tw/aspnet/core/signalr/javascript-client?view=aspnetcore-7.0&tabs=visual-studio  





第四部分:Android實現SignalR

Step 1:Android專案架構


1. 依賴庫 引用SignalR客戶端套件
2. 權限項 配置網路使用權限、Http可明文使用(範例測試用)
3. 主程式 顯示接收到SignalR伺服器端的資料




Step 2:依賴庫-配置

打開bundle.gradle
在dependencies最下方引用,版本建議與Server一致

implementation group: 'com.microsoft.signalr', name: 'signalr', version: '7.0.0'




Step 3:權限項-配置-1

打開Android.Manifest.xml
1. 添加連線到網路權限

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />


2. 在Android 9(API_28)以後,不允許Http(明文)的連線,為了方便測試我們要繞過Android的安全策略

    android:networkSecurityConfig="@xml/network_security_config"




Step 4:權限項-配置-2

繞過Android的安全策略,還需要添加一個檔案 network_security_config.xml 以下位置
※根目錄 app 開始

G:\\AndroidGetSignalRExample\\app\\src\\main\\res\\xml




Step 5:權限項-配置-3

network_security_config.xml 內容如下:
IP是對應自己本機的位置,模擬器Debug模式下是連線本機IP (192.168.42.177),不可直接用localhost
※也可以從代理Proxy配置是另一Http測試解法


<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">192.168.42.177</domain>
    </domain-config>
</network-security-config>



Step 6:主程式-配置

依序完成下方六個步驟

package com.example.androidgetsignalrexample;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.widget.TextView;

//SignalR 依賴庫
import com.microsoft.signalr.HubConnection;
import com.microsoft.signalr.HubConnectionBuilder;
import com.microsoft.signalr.HubConnectionState;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;

public class MainActivity extends AppCompatActivity {
    private HubConnection hubConnection;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //1. 檢查網路
        if (isNetworkAvailable(this)) {
            Log.d("MyApp", "有網路");
        } else {
            Log.d("MyApp", "無網路");
        }

        //2-1. 建立Signalr連接
        try {
            hubConnection = HubConnectionBuilder.create("http://192.168.42.177:7531/UpdateHub").build();
            // 2-2. 註冊SignalR,等待被推播
            hubConnection.on("SendUpdate", (message) -> {
                try {
                    //2-3. 執行緒更新UI上物件
                    runOnUiThread(() -> {
                        TextView textView = findViewById(R.id.Information);
                        textView.setText("SignalR Server回傳訊息:" + message);
                    });
                }
                catch (Exception e)
                {
                    Log.e("MyApp" , "錯誤訊息: "+e.getMessage());
                }

            }, String.class);

            // 3. 啟動SignalR連線
            if(hubConnection.getConnectionState() == HubConnectionState.DISCONNECTED)
            {
                Log.d("MyApp", "進行連接SignalR");
                hubConnection.start().blockingAwait();
            }
        }
        catch (Exception e)
        {
            Log.d("MyApp", e.getMessage());
        }
    }
    private Handler handler = new Handler();

    //檢查網路狀態
    public static boolean isNetworkAvailable(Context context) {
        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (connectivityManager != null) {
            NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
            return activeNetworkInfo != null && activeNetworkInfo.isConnected();
        }
        return false;
    }
}



Step 7:測試Android

運行Android Studio 模擬器




Step 8:測試成功

模擬器成功收到SignalR伺服器傳來的資料




Step 9:交叉驗證-成功

手機、Web的資訊是一致的,在同個時間下伺服器推播的資訊,大家都相同(時間、版本號一致)