Samouczek: publikowanie i subskrybowanie wiadomości między klientami WebSocket przy użyciu podprotokołu

W samouczku Tworzenie aplikacji czatu dowiesz się, jak używać interfejsów API WebSocket do wysyłania i odbierania danych za pomocą usługi Azure Web PubSub. Jeśli klient komunikuje się z usługą, nie jest potrzebny żaden protokół. Na przykład można wysłać dowolny typ danych przy użyciu polecenia WebSocket.send(), a serwer odbiera je tak samo, jak jest. Korzystanie z interfejsów API WebSocket jest łatwe, ale ich możliwości są ograniczone. Na przykład nie można określić nazwy zdarzenia podczas wysyłania zdarzenia na serwer lub publikowania komunikatu do innych klientów zamiast wysyłania go na serwer. Z tego samouczka dowiesz się, jak za pomocą podprotocol rozszerzyć funkcjonalność klienta.

Z tego samouczka dowiesz się, jak wykonywać następujące czynności:

  • Utwórz wystąpienie usługi Web PubSub
  • Generowanie pełnego adresu URL w celu ustanowienia połączenia protokołu WebSocket
  • Publikowanie komunikatów między klientami WebSocket za pomocą podprotokołu

Jeśli nie masz jeszcze konta platformy Azure, przed rozpoczęciem utwórz bezpłatne konto.

Wymagania wstępne

  • Ta konfiguracja wymaga wersji 2.22.0 lub nowszej interfejsu wiersza polecenia platformy Azure. W przypadku korzystania z usługi Azure Cloud Shell najnowsza wersja jest już zainstalowana.

Ważne

Nieprzetworzone parametry połączenia są wyświetlane tylko w tym artykule w celach demonstracyjnych.

parametry połączenia zawiera informacje autoryzacyjne potrzebne Twojej aplikacji, aby uzyskać dostęp do usługi Azure Web PubSub. Klucz dostępu w parametry połączenia działa jak hasło root dla Twojej usługi. W środowiskach produkcyjnych zawsze chroń klucze dostępu. Użyj usługi Azure Key Vault, aby bezpiecznie zarządzać kluczami i obracać je oraz zabezpieczać połączenie za pomocą usługi WebPubSubServiceClient.

Unikaj dystrybuowania kluczy dostępu do innych użytkowników, kodowania ich lub zapisywania ich w dowolnym miejscu w postaci zwykłego tekstu, który jest dostępny dla innych użytkowników. Obracanie kluczy, jeśli uważasz, że mogły one zostać naruszone.

Tworzenie wystąpienia usługi Azure Web PubSub

Tworzenie grupy zasobów

Grupa zasobów to logiczny kontener przeznaczony do wdrażania zasobów platformy Azure i zarządzania nimi. Użyj polecenia az group create, aby utworzyć grupę zasobów o nazwie myResourceGroup w eastus lokalizacji.

az group create --name myResourceGroup --location EastUS

Utwórz wystąpienie usługi Web PubSub

Uruchom az extension add, aby zainstalować rozszerzenie webpubsub lub uaktualnić je do bieżącej wersji.

az extension add --upgrade --name webpubsub

Użyj polecenia interfejsu wiersza polecenia platformy Azure az webpubsub create, aby utworzyć usługę Web PubSub w utworzonej wcześniej grupie zasobów. Poniższe polecenie tworzy zasób Web PubSub w warstwie Free w grupie zasobów myResourceGroup w regionie EastUS:

Ważne

Każdy zasób Web PubSub musi mieć unikatową nazwę. Zastąp <your-unique-resource-name> nazwą Twojego zasobu Web PubSub w poniższych przykładach.

az webpubsub create --name "<your-unique-resource-name>" --resource-group "myResourceGroup" --location "EastUS" --sku Free_F1

Dane wyjściowe tego polecenia pokazują właściwości nowo utworzonego zasobu. Zanotuj dwie poniższe właściwości:

  • Nazwa zasobu: nazwa podana w parametrze --name powyżej.
  • hostName: w przykładzie nazwa hosta to <your-unique-resource-name>.webpubsub.azure.com/.

W tym momencie Twoje konto platformy Azure jest jedynym autoryzowanym do wykonywania jakichkolwiek operacji na tym nowym zasobie.

Pobierz ConnectionString do późniejszego użycia

Nieprzetworzone parametry połączenia są wyświetlane tylko w tym artykule w celach demonstracyjnych. W środowiskach produkcyjnych zawsze chroń klucze dostępu. Użyj usługi Azure Key Vault, aby bezpiecznie zarządzać kluczami i obracać je oraz zabezpieczać połączenie za pomocą usługi WebPubSubServiceClient.

Użyj polecenia interfejsu wiersza polecenia platformy Azure az webpubsub key, aby pobrać parametr ConnectionString usługi. Zastąp symbol zastępczy <your-unique-resource-name> nazwą swojej instancji usługi Azure Web PubSub.

az webpubsub key show --resource-group myResourceGroup --name <your-unique-resource-name> --query primaryConnectionString --output tsv

Skopiuj parametry połączenia do późniejszego użycia.

Skopiuj pobrany element ConnectionString i użyj go w dalszej części tego samouczka jako wartość <connection_string>.

Konfigurowanie projektu

Wymagania wstępne

Używanie podprotokolu

Klient może uruchomić połączenie protokołu WebSocket przy użyciu określonego podprotokolu. Usługa Azure Web PubSub obsługuje podprotokół o nazwie json.webpubsub.azure.v1, który umożliwia klientom publikowanie/subskrybowanie bezpośrednio za pośrednictwem usługi Web PubSub, bez konieczności komunikacji zwrotnej z serwerem nadrzędnym. Szczegółowe informacje na temat tego podprotokołu można znaleźć w artykule Obsługiwany przez usługę Azure Web PubSub podprotokół JSON WebSocket.

Jeśli używasz innych nazw protokołów, usługa je zignoruje i przekaże do serwera w procedurze obsługi zdarzenia connect, dzięki czemu możesz tworzyć własne protokoły.

Teraz utwórzmy aplikację internetową przy użyciu podprotokolu json.webpubsub.azure.v1 .

  1. Instalowanie zależności

    mkdir logstream
    cd logstream
    dotnet new web
    dotnet add package Microsoft.Extensions.Azure
    dotnet add package Azure.Messaging.WebPubSub
    
  2. Utwórz stronę serwera do hostowania interfejsu /negotiate API i strony internetowej.

    Zaktualizuj Program.cs za pomocą poniższego kodu.

    • Użyj AddAzureClients polecenia , aby dodać klienta usługi i odczytać parametry połączenia z konfiguracji.
    • Dodaj app.UseStaticFiles(); element przed app.Run(); , aby obsługiwać pliki statyczne.
    • I zaktualizuj app.MapGet, aby wygenerować token dostępu klienta za pomocą żądań /negotiate.
    using Azure.Messaging.WebPubSub;
    using Microsoft.Extensions.Azure;
    
    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddAzureClients(s =>
    {
        s.AddWebPubSubServiceClient(builder.Configuration["Azure:WebPubSub:ConnectionString"], "stream");
    });
    
    var app = builder.Build();
    app.UseStaticFiles();
    app.MapGet("/negotiate", async context =>
    {
        var service = context.RequestServices.GetRequiredService<WebPubSubServiceClient>();
        var response = new
        {
            url = service.GetClientAccessUri(roles: new string[] { "webpubsub.sendToGroup.stream", "webpubsub.joinLeaveGroup.stream" }).AbsoluteUri
        };
        await context.Response.WriteAsJsonAsync(response);
    });
    
    app.Run();
    
  3. Tworzenie strony internetowej

    Utwórz stronę HTML z poniższą zawartością i zapisz ją jako wwwroot/index.html:

    <html>
      <body>
        <div id="output"></div>
        <script>
          (async function () {
            let res = await fetch('/negotiate')
            let data = await res.json();
            let ws = new WebSocket(data.url, 'json.webpubsub.azure.v1');
            ws.onopen = () => {
              console.log('connected');
            };
    
            let output = document.querySelector('#output');
            ws.onmessage = event => {
              let d = document.createElement('p');
              d.innerText = event.data;
              output.appendChild(d);
            };
          })();
        </script>
      </body>
    </html>                                                                
    

    Powyższy kod łączy się z usługą i wyświetla wszelkie komunikaty odebrane na stronie. Główną zmianą jest określenie podprotokolu podczas tworzenia połączenia protokołu WebSocket.

  4. Uruchamianie serwera

    Do ustawienia parametry połączenia używamy narzędzia Secret Manager dla platformy .NET Core. Uruchom poniższe polecenie, zastępując <connection_string> element pobrany w poprzednim kroku i otwórz http://localhost:5000/index.html w przeglądarce:

    dotnet user-secrets init
    dotnet user-secrets set Azure:WebPubSub:ConnectionString "<connection-string>"
    dotnet run
    

    Jeśli używasz Chrome, możesz nacisnąć F12 lub kliknąć prawym przyciskiem myszy opcję ->Inspect ->Developer Tools i wybrać kartę Network. Załaduj stronę internetową, a zobaczysz, że połączenie WebSocket zostało nawiązane. Wybierz tę opcję, aby sprawdzić połączenie WebSocket. Poniżej widać, że klient odbiera komunikat zdarzenia connected. Zobaczysz, że możesz pobrać connectionId wygenerowany dla tego klienta.

    {"type":"system","event":"connected","userId":null,"connectionId":"<the_connection_id>"}
    

Widać, że za pomocą podprotokołu można uzyskać pewne metadane połączenia, gdy połączenie ma stan connected.

Klient otrzymuje teraz komunikat JSON zamiast zwykłego tekstu. Komunikat JSON zawiera więcej informacji, takich jak typ i źródło komunikatu. Dzięki temu możesz użyć tych informacji, aby wykonać więcej przetwarzania komunikatu (na przykład wyświetlić komunikat w innym stylu, jeśli pochodzi z innego źródła), które można znaleźć w kolejnych sekcjach.

Publikowanie komunikatów z klienta

W samouczku Tworzenie aplikacji do czatu, gdy klient wysyła wiadomość przez połączenie WebSocket do usługi Web PubSub, usługa wywołuje zdarzenie użytkownika po stronie Twojego serwera. Dzięki podprotokołowi klient zyskuje więcej funkcji poprzez wysyłanie komunikatu JSON. Można na przykład publikować komunikaty bezpośrednio z klienta za pośrednictwem usługi Web PubSub do innych klientów.

Jest to przydatne, jeśli chcesz przesyłać strumieniowo dużą ilość danych do innych klientów w czasie rzeczywistym. Użyjmy tej funkcji, aby utworzyć aplikację do przesyłania strumieniowego dzienników, która może przesyłać strumieniowo dzienniki konsoli do przeglądarki w czasie rzeczywistym.

  1. Tworzenie programu do strumieniowania

    Utwórz stream program:

    mkdir stream
    cd stream
    dotnet new console
    

    Zaktualizuj Program.cs przy użyciu następującej zawartości:

    using System;
    using System.Net.Http;
    using System.Net.WebSockets;
    using System.Text;
    using System.Text.Json;
    using System.Threading.Tasks;
    
    namespace stream
    {
        class Program
        {
            private static readonly HttpClient http = new HttpClient();
            static async Task Main(string[] args)
            {
                // Get client url from remote
                var stream = await http.GetStreamAsync("http://localhost:5000/negotiate");
                var url = (await JsonSerializer.DeserializeAsync<ClientToken>(stream)).url;
                var client = new ClientWebSocket();
                client.Options.AddSubProtocol("json.webpubsub.azure.v1");
    
                await client.ConnectAsync(new Uri(url), default);
    
                Console.WriteLine("Connected.");
                var streaming = Console.ReadLine();
                while (streaming != null)
                {
                    if (!string.IsNullOrEmpty(streaming))
                    {
                        var message = JsonSerializer.Serialize(new
                        {
                            type = "sendToGroup",
                            group = "stream",
                            data = streaming + Environment.NewLine,
                        });
                        Console.WriteLine("Sending " + message);
                        await client.SendAsync(Encoding.UTF8.GetBytes(message), WebSocketMessageType.Text, true, default);
                    }
    
                    streaming = Console.ReadLine();
                }
    
                await client.CloseAsync(WebSocketCloseStatus.NormalClosure, null, default);
            }
    
            private sealed class ClientToken
            {
                public string url { get; set; }
            }
        }
    }
    
    

    Tutaj możesz zobaczyć, że pojawiło się nowe pojęcie „group”. Grupa to logiczna koncepcja w hubie, w ramach której można opublikować wiadomość do grupy połączeń. W centrum można mieć wiele grup, a jeden klient może jednocześnie subskrybować wiele grup. W przypadku korzystania z podprotocol można publikować tylko w grupie zamiast emitować do całego centrum. Aby uzyskać szczegółowe informacje na temat terminów, zapoznaj się z podstawowymi pojęciami.

  2. Ponieważ tutaj używamy grupy, musimy również zaktualizować stronę internetową index.html, aby dołączała do grupy po ustanowieniu połączenia WebSocket w funkcji wywołania zwrotnego ws.onopen.

    let ackId = 0;
    ws.onopen = () => {
      console.log('connected');
      ws.send(JSON.stringify({
        type: 'joinGroup',
        group: 'stream',
        ackId: ++ackId
      }));
    };
    

    Możesz zobaczyć, że klient dołącza do grupy, wysyłając wiadomość typu joinGroup.

  3. Nieznacznie zaktualizuj również logikę funkcji zwrotnej ws.onmessage, aby parsowała odpowiedź JSON i wypisywała komunikaty tylko z grupy stream, tak aby działała jak mechanizm wypisywania strumienia na żywo.

    ws.onmessage = event => {
      let message = JSON.parse(event.data);
      if (message.type === 'message' && message.group === 'stream') {
        let d = document.createElement('span');
        d.innerText = message.data;
        output.appendChild(d);
        window.scrollTo(0, document.body.scrollHeight);
      }
    };
    
  4. Ze względów bezpieczeństwa klient domyślnie nie może samodzielnie publikować w grupie ani subskrybować grupy. Jak więc zauważyliście, ustawiliśmy roles jako klienta podczas generowania tokenu:

    Ustaw roles, gdy GenerateClientAccessUri w Startup.cs, jak poniżej:

    service.GenerateClientAccessUri(roles: new string[] { "webpubsub.sendToGroup.stream", "webpubsub.joinLeaveGroup.stream" })
    
  5. Na koniec zastosuj również jakiś styl, aby index.html dobrze się wyświetlał.

    <html>
    
      <head>
        <style>
          #output {
            white-space: pre;
            font-family: monospace;
          }
        </style>
      </head>
    

Teraz uruchom poniższy kod i wpisz dowolny tekst i są one wyświetlane w przeglądarce w czasie rzeczywistym:

ls -R | dotnet run

# Or call `dir /s /b | dotnet run` when you are using CMD under Windows

Albo możesz spowolnić działanie, aby zobaczyć, że dane są przesyłane strumieniowo do przeglądarki w czasie rzeczywistym:

for i in $(ls -R); do echo $i; sleep 0.1; done | dotnet run

Kompletny przykład kodu tego samouczka można znaleźć tutaj.

Następne kroki

Ten samouczek przedstawia podstawy łączenia się z usługą Web PubSub oraz publikowania komunikatów do połączonych klientów przy użyciu podprotokołu.

Zapoznaj się z innymi samouczkami, aby dowiedzieć się więcej na temat korzystania z usługi.