اتصال طويل أم قناة دائمة: كيف ينقل C# البيانات في الوقت الفعلي
\n\nتخيل: أنت تكتب تطبيق دردشة بلغة C#. أو محطة تداول. أو نظام إشعارات. العميل ينتظر البيانات - وهو غير مستعد للانتظار طويلاً. في السابق، كان المطورون يتدبرون أمرهم كما يستطيعون: يضعون مؤقتات، ويستدعون الخادم كل 5 ثوانٍ، ويولدون طلبات زائدة. اليوم لدينا نهجان متحضران - Long Polling و WebSocket. كلاهما يسمح للخادم بإرسال البيانات إلى العميل دون طلب صريح منه. لكنهما يعملان بشكل مختلف جوهريًا.
\n\nفي هذه المقالة، سنشرح كيفية تنفيذ كلا النهجين بلغة C# (ASP.NET Core)، ونقارنهما من حيث السرعة والحمل والتعقيد، ونفهم أيهما تختار لمشروعك. هيا بنا.
\n\nما هو Long Polling؟ (ولماذا ليس سحرًا)
\n\nLong Polling هو محاكاة للوقت الفعلي عبر HTTP العادي. يرسل العميل طلبًا إلى الخادم، لكن الخادم لا يرد فورًا. يبقي الاتصال مفتوحًا حتى يظهر حدث جديد (أو ينتهي المهلة). بمجرد أن تصبح البيانات جاهزة - يرسل الخادم الرد، ويعالجه العميل ويفتح على الفور طلبًا "طويلاً" جديدًا.
\n\nهذه خدعة ذكية تعمل على أي خادم HTTP وفي أي متصفح. لكن لها ثمن: كل اتصال يعلق في ذاكرة الخادم، وإذا كان لديك آلاف العملاء - فهذه مشكلة.
\n\nمثال Long Polling بلغة C# (ASP.NET Core)
\n\nلنكتب وحدة تحكم بسيطة تحاكي قائمة انتظار الإشعارات. يقوم العميل باستدعاء نقطة النهاية، وينتظر الخادم بيانات جديدة (على سبيل المثال، من قناة Channel<string>) ويرسلها.
using Microsoft.AspNetCore.Mvc;\nusing System.Threading.Channels;\n\n[ApiController]\n[Route("api/[controller]")]\npublic class NotificationsController : ControllerBase\n{\n private static readonly Channel<string> _channel = Channel.CreateUnbounded<string>();\n\n // نقطة نهاية لنشر إشعار (على سبيل المثال، من خدمة أخرى)\n [HttpPost("publish")]\n public async Task<IActionResult> Publish([FromBody] string message)\n {\n await _channel.Writer.WriteAsync(message);\n return Ok("تم إرسال الإشعار");\n }\n\n // Long Polling: العميل ينتظر حتى تظهر رسالة جديدة\n [HttpGet("long-poll")]\n public async Task<IActionResult> LongPoll(CancellationToken cancellationToken)\n {\n // انتظار رسالة جديدة؛ إذا لم يكن هناك شيء - انتظر حتى 30 ثانية\n using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);\n cts.CancelAfter(TimeSpan.FromSeconds(30));\n\n try\n {\n var message = await _channel.Reader.ReadAsync(cts.Token);\n return Ok(new { data = message });\n }\n catch (OperationCanceledException)\n {\n // انتهاء المهلة - نعيد ردًا فارغًا، وسيكرر العميل الطلب\n return Ok(new { data = (string?)null });\n }\n }\n}\n\n\nالعميل (JavaScript) سيبدو هكذا:
\n\nasync function startLongPoll() {\n while (true) {\n const response = await fetch('/api/notifications/long-poll');\n const result = await response.json();\n if (result.data) {\n console.log('إشعار جديد:', result.data);\n }\n // نبدأ فورًا استقصاء جديدًا\n }\n}\nstartLongPoll();\n\n\nكل شيء بسيط. لكن هناك فارق بسيط: إذا تمت إعادة تشغيل الخادم أو فقد العميل الاتصال - فستفقد البيانات. Long Polling لا يضمن التسليم.
\n\nWebSocket: نفق ثنائي الاتجاه بلغة C#
\n\nWebSocket هو بروتوكول كامل للاتصال ثنائي الاتجاه عبر TCP. بعد المصافحة (طلب HTTP GET عادي مع ترقية) يتواصل العميل والخادم مباشرة، دون تكاليف إضافية لرؤوس HTTP. يمكن للخادم إرسال رسالة في أي لحظة، وسيتلقاها العميل فورًا.
\n\nفي ASP.NET Core، WebSocket مدمج بشكل افتراضي. لا حاجة لمكتبات خارجية (