البرنامج التعليمي: نشر الرسائل والاشتراك فيها بين عملاء WebSocket باستخدام بروتوكول فرعي

في إنشاء برنامج تعليمي لتطبيق الدردشة، تعلمت كيفية استخدام واجهات برمجة تطبيقات WebSocket لإرسال البيانات وتلقيها باستخدام Azure Web PubSub. يمكنك أن ترى أنه لا توجد حاجة إلى بروتوكول عندما يتصل العميل بالخدمة. على سبيل المثال، يمكنك إرسال أي نوع من البيانات باستخدام WebSocket.send()، ويتلقى الخادمها تماما كما هي. عملية واجهات برمجة تطبيقات WebSocket سهلة الاستخدام، ولكن الوظيفة محدودة. على سبيل المثال، لا يمكنك تحديد اسم الحدث عند إرسال الحدث إلى الخادم، أو نشر رسالة إلى عملاء آخرين بدلا من إرسالها إلى الخادم. في هذا البرنامج التعليمي، ستتعلم كيفية استخدام البروتوكول الفرعي لتوسيع وظائف العميل.

في هذا البرنامج التعليمي، تتعلم كيفية:

  • إنشاء مثيل خدمة Web PubSub
  • إنشاء عنوان URL الكامل لتأسيس اتصال WebSocket
  • نشر الرسائل والاشتراك فيها بين عملاء WebSocket باستخدام بروتوكول فرعي

إذا لم يكن لديك اشتراك في Azure، فأنشئ حساب Azure مجاني قبل أن تبدأ.

المتطلبات الأساسية

  • يتطلب هذا الإعداد الإصدار 2.22.0 أو أعلى من Azure CLI. إذا كنت تستخدم Azure Cloud Shell، يتم تثبيت أحدث إصدار بالفعل.

إنشاء مثيل Azure Web PubSub

إنشاء مجموعة موارد

وتُعد مجموعة الموارد عبارة عن حاوية منطقية يتم فيها توزيع موارد Azure وإدارتها. استخدم الأمر az group create لإنشاء مجموعة موارد باسم myResourceGroup في eastus الموقع.

az group create --name myResourceGroup --location EastUS

إنشاء مثيل Web PubSub

قم بتشغيل ملحق az add لتثبيت أو ترقية ملحق webpubsub إلى الإصدار الحالي.

az extension add --upgrade --name webpubsub

استخدم الأمر Azure CLI az webpubsub create لإنشاء Web PubSub في مجموعة الموارد التي قمت بإنشائها. ينشئ الأمر التالي مورد Web PubSub مجاني ضمن مجموعة الموارد myResourceGroup في EastUS:

هام

يجب أن يكون لكل مورد Web PubSub ويب اسمًا فريدًا. استبدل <your-unique-resource-name> باسم Web PubSub في الأمثلة التالية.

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

يظهر إخراج هذا الأمر خصائص المورد المنشأ حديثًا. دوّن اثنتين من الخصائص المذكورة أدناه:

  • اسم المورد: الاسم الذي قدمته للمعلمة --name أعلاه.
  • اسم المضيف: في المثال، اسم المضيف هو <your-unique-resource-name>.webpubsub.azure.com/.

في هذه المرحلة، حساب Azure هو الوحيد المصرح به لتنفيذ أي عمليات على هذا المورد الجديد.

احصل على ConnectionString لاستخدامها في المستقبل

هام

سلسلة اتصال تتضمن معلومات التخويل المطلوبة لتطبيقك للوصول إلى خدمة Azure Web PubSub. مفتاح الوصول داخل سلسلة الاتصال يشبه كلمة مرور الجذر للخدمة الخاصة بك. في بيئات الإنتاج، كن حذرا دائما لحماية مفاتيح الوصول الخاصة بك. استخدم Azure Key Vault لإدارة المفاتيح وتدويرها بأمان. تجنب توزيع مفاتيح الوصول إلى مستخدمين آخرين، أو ترميزها ترميزًا ثابتًا، أو حفظها في أي مكان في نص عادي يمكن للآخرين الوصول إليه. قم بتدوير المفاتيح الخاصة بك إذا كنت تعتقد أنها قد تعرضت للخطر.

استخدم الأمر Azure CLI az webpubsub key للحصول على الاتصال ionString للخدمة. <your-unique-resource-name> استبدل العنصر النائب باسم مثيل Azure Web PubSub.

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

انسخ سلسلة الاتصال لاستخدامها لاحقا.

انسخ الاتصال ionString التي تم إحضارها واستخدمها لاحقا في هذا البرنامج التعليمي كقيمة .<connection_string>

إعداد المشروع

المتطلبات الأساسية

استخدام علامة فرعية

يمكن للعميل بدء اتصال WebSocket باستخدام البروتوكول الفرعي المحدد. تدعم خدمة Azure Web PubSub بروتوكول فرعي يسمى json.webpubsub.azure.v1 لتمكين العملاء من النشر/الاشتراك مباشرة من خلال خدمة Web PubSub بدلا من رحلة ذهابا وإيابا إلى الخادم المصدر. تحقق من البروتوكول الفرعي JSON WebSocket المدعوم من Azure Web PubSub للحصول على تفاصيل حول البروتوكول الفرعي.

في حال كنت تستخدم أسماء بروتوكول أخرى، سيتم تجاهلها من قبل الخدمة وتمريرها إلى الخادم في معالج أحداث الاتصال، حتى تتمكن من إنشاء البروتوكولات الخاصة بك.

الآن دعونا ننشئ تطبيق ويب باستخدام json.webpubsub.azure.v1 البروتوكول الفرعي.

  1. تثبيت التبعيات

    mkdir logstream
    cd logstream
    dotnet new web
    dotnet add package Microsoft.Extensions.Azure
    dotnet add package Azure.Messaging.WebPubSub
    
  2. إنشاء جانب الخادم لاستضافة /negotiate واجهة برمجة التطبيقات وصفحة الويب.

    قم بالتحديث Program.cs باستخدام التعليمات البرمجية أدناه.

    • استخدم AddAzureClients لإضافة عميل الخدمة، وقراءة سلسلة الاتصال من التكوين.
    • أضف app.UseStaticFiles(); من قبل app.Run(); لدعم الملفات الثابتة.
    • وتحديث app.MapGet لإنشاء رمز وصول العميل مع /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. إنشاء صفحة ويب

    إنشاء صفحة HTML مع المحتوى أدناه وحفظه ك 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>                                                                
    

    تتصل التعليمات البرمجية أعلاه بالخدمة وتطبع أي رسالة تم تلقيها إلى الصفحة. التغيير الرئيسي هو أننا نحدد البروتوكول الفرعي عند إنشاء اتصال WebSocket.

  4. تشغيل الخادم

    نستخدم أداة Secret Manager ل .NET Core لتعيين سلسلة الاتصال. شغل الأمر أدناه، واستبدل <connection_string> بالأمر الذي تم جلبه في الخطوة السابقة، وافتح http://localhost:5000/index.html في المستعرض:

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

    إذا كنت تستخدم Chrome، فيمكنك الضغط على F12 أو النقر بزر الماوس الأيمن فوق ->Inspect ->Developer Tools، وتحديد علامة التبويب Network . قم بتحميل صفحة الويب، ويمكنك رؤية اتصال WebSocket تم تأسيسه. حدد لفحص اتصال WebSocket، يمكنك رؤية رسالة الحدث أدناه connected يتم تلقيها في العميل. يمكنك أن ترى أنه يمكنك الحصول على الذي connectionId تم إنشاؤه لهذا العميل.

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

يمكنك أن ترى أنه بمساعدة البروتوكول الفرعي، يمكنك الحصول على بعض بيانات التعريف للاتصال عندما يكون connectedالاتصال .

يتلقى العميل الآن رسالة JSON بدلا من نص عادي. تحتوي رسالة JSON على مزيد من المعلومات مثل نوع الرسالة ومصدرها. لذلك يمكنك استخدام هذه المعلومات لإجراء المزيد من المعالجة للرسالة (على سبيل المثال، عرض الرسالة بأسلوب مختلف إذا كانت من مصدر مختلف)، والتي يمكنك العثور عليها في الأقسام اللاحقة.

نشر رسائل من العميل

في البرنامج التعليمي إنشاء تطبيق دردشة، عندما يرسل العميل رسالة من خلال اتصال WebSocket بخدمة Web PubSub، تقوم الخدمة بتشغيل حدث مستخدم على جانب الخادم الخاص بك. باستخدام البروتوكول الفرعي، يتمتع العميل بمزيد من الوظائف عن طريق إرسال رسالة JSON. على سبيل المثال، يمكنك نشر الرسائل مباشرة من العميل من خلال خدمة Web PubSub إلى عملاء آخرين.

وهذا مفيد إذا كنت تريد دفق كمية كبيرة من البيانات إلى عملاء آخرين في الوقت الحقيقي. دعونا نستخدم هذه الميزة لإنشاء تطبيق تدفق سجل، والتي يمكن أن تيار سجلات وحدة التحكم إلى المتصفح في الوقت الحقيقي.

  1. إنشاء برنامج البث

    stream إنشاء برنامج:

    mkdir stream
    cd stream
    dotnet new console
    

    تحديث Program.cs بالمحتوى التالي:

    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; }
            }
        }
    }
    
    

    يمكنك أن ترى أن هناك مفهوما جديدا "مجموعة" هنا. المجموعة مفهوم منطقي في لوحة وصل حيث يمكنك نشر رسالة إلى مجموعة من الاتصالات. في لوحة الوصل، يمكنك الحصول على مجموعات متعددة، ويمكن لعميل واحد الاشتراك في مجموعات متعددة في نفس الوقت. عند استخدام بروتوكول فرعي، يمكنك النشر إلى مجموعة فقط بدلاً من البث إلى المحور بأكمله. للحصول على تفاصيل حول المصطلحات، تحقق من المفاهيم الأساسية.

  2. نظرا لأننا نستخدم المجموعة هنا، نحتاج أيضا إلى تحديث صفحة index.html الويب للانضمام إلى المجموعة عند إنشاء اتصال WebSocket داخل ws.onopen رد الاتصال.

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

    يمكنك رؤية العميل ينضم إلى المجموعة عن طريق إرسال رسالة من joinGroup النوع.

  3. قم أيضا بتحديث ws.onmessage منطق رد الاتصال قليلا لتحليل استجابة JSON وطباعة الرسائل فقط من stream المجموعة بحيث تعمل كطابعة بث مباشر.

    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. لاعتبارات الأمان، لا يمكن للعميل بشكل افتراضي نشر مجموعة أو الاشتراك فيها بمفرده. لذلك لاحظت أننا قمنا بتعيين roles إلى العميل عند إنشاء الرمز المميز:

    roles تعيين عندما GenerateClientAccessUri يكون في Startup.cs مثل أدناه:

    service.GenerateClientAccessUri(roles: new string[] { "webpubsub.sendToGroup.stream", "webpubsub.joinLeaveGroup.stream" })
    
  5. وأخيرا أيضا تطبيق بعض الأنماط على index.html بحيث يعرض بشكل جيد.

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

الآن قم بتشغيل التعليمات البرمجية أدناه واكتب أي نص ويتم عرضها في المتصفح في الوقت الفعلي:

ls -R | dotnet run

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

أو يمكنك جعلها أبطأ حتى تتمكن من رؤية البيانات يتم بثها إلى المتصفح في الوقت الحقيقي:

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

يمكنك الاطلاع على نموذج التعليمة البرمجية لهذا البرنامج التعليمي كاملاً من هنا.

الخطوات التالية

يوفر لك هذا البرنامج التعليمي فكرة أساسية عن كيفية الاتصال بخدمة Web PubSub وكيفية نشر الرسائل إلى العملاء المتصلين باستخدام البروتوكول الفرعي.

اطلع على البرامج التعليمة الأخرى للحصول على نظرة شاملة حول كيفية استخدام الخدمة.