البث من العميل والبث ثنائي الاتجاه
ابنوا قنوات للبث من العميل والبث ثنائي الاتجاه كامل الازدواج للسيناريوهات ذات معدل النقل المرتفع.
البث من العميل والبث ثنائي الاتجاه درس مجاني في C# Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في C# Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة C# Academy 4 دروس في المجموع.
البث من العميل والبث ثنائي الاتجاه
بالإضافة إلى Unary والبث من الخادم، يدعم gRPC البث من العميل، حيث يرسل العميل رسائل متعددة ويرد الخادم مرة واحدة، والبث ثنائي الاتجاه، حيث يرسل الطرفان رسائل متعددة في الوقت نفسه عبر اتصال واحد.
البث من العميل: تعريف Proto
أضف الكلمة المفتاحية stream قبل نوع الطلب. يرسل العميل سلسلة من الرسائل، وعند الانتهاء، يعيد الخادم استجابة واحدة.
service UploadService {
// Client streams chunks, server returns a summary
rpc UploadFile (stream FileChunk) returns (UploadSummary);
}
message FileChunk { bytes data = 1; string filename = 2; }
message UploadSummary { int64 bytes_received = 1; string checksum = 2; }تنفيذ البث من العميل على الخادم
استخدم IAsyncStreamReader<T> لقراءة رسائل العميل فور وصولها. استدعِ MoveNext() أو كرّر باستخدام ReadAllAsync().
public override async Task<UploadSummary> UploadFile(
IAsyncStreamReader<FileChunk> requestStream,
ServerCallContext context)
{
long totalBytes = 0;
using var ms = new MemoryStream();
await foreach (var chunk in requestStream.ReadAllAsync(context.CancellationToken))
{
await ms.WriteAsync(chunk.Data.Memory, context.CancellationToken);
totalBytes += chunk.Data.Length;
}
var checksum = ComputeMd5(ms.ToArray());
return new UploadSummary { BytesReceived = totalBytes, Checksum = checksum };
}استدعاء البث من العميل
افتح استدعاء البث، واكتب الرسائل باستخدام RequestStream.WriteAsync()، ثم أشر إلى اكتمال الإرسال باستخدام CompleteAsync(). انتظر الاستجابة.
using var call = client.UploadFile();
var fileBytes = await File.ReadAllBytesAsync("large-file.bin");
const int chunkSize = 64 * 1024; // 64 KB
for (int offset = 0; offset < fileBytes.Length; offset += chunkSize)
{
var chunk = fileBytes.Skip(offset).Take(chunkSize).ToArray();
await call.RequestStream.WriteAsync(new FileChunk
{
Filename = "large-file.bin",
Data = Google.Protobuf.ByteString.CopyFrom(chunk)
});
}
await call.RequestStream.CompleteAsync(); // signal end
var summary = await call; // await server response
Console.WriteLine($"Uploaded {summary.BytesReceived} bytes");البث ثنائي الاتجاه: تعريف Proto
أضف stream إلى كلا الطرفين لتحقيق اتصال ثنائي الاتجاه بالكامل. يمكن لكل من العميل والخادم إرسال الرسائل في أي وقت وبشكل مستقل.
service ChatService {
rpc Chat (stream ChatMessage) returns (stream ChatMessage);
}
message ChatMessage {
string user = 1;
string content = 2;
int64 sent_at = 3;
}تنفيذ البث ثنائي الاتجاه على الخادم
اقرأ من IAsyncStreamReader واكتب إلى IServerStreamWriter بالتزامن. استخدم Task.WhenAll لتشغيل الحلقتين في الوقت نفسه.
public override async Task Chat(
IAsyncStreamReader<ChatMessage> requestStream,
IServerStreamWriter<ChatMessage> responseStream,
ServerCallContext context)
{
// Broadcast to all connected clients
var readTask = Task.Run(async () =>
{
await foreach (var msg in requestStream.ReadAllAsync(context.CancellationToken))
{
_chatHub.Broadcast(msg);
}
});
var writeTask = Task.Run(async () =>
{
await foreach (var msg in _chatHub.GetMessagesAsync(context.CancellationToken))
{
await responseStream.WriteAsync(msg);
}
});
await Task.WhenAll(readTask, writeTask);
}البث ثنائي الاتجاه من العميل
أرسل واستقبل بالتزامن عبر تشغيل مهمتين — إحداهما لكتابة الرسائل والأخرى لقراءة الردود.
using var call = client.Chat();
// Read task
var readTask = Task.Run(async () =>
{
await foreach (var msg in call.ResponseStream.ReadAllAsync())
Console.WriteLine($"{msg.User}: {msg.Content}");
});
// Write task
while (Console.ReadLine() is string text && text != "exit")
{
await call.RequestStream.WriteAsync(
new ChatMessage { User = "Alice", Content = text,
SentAt = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() });
}
await call.RequestStream.CompleteAsync();
await readTask;التحكم في التدفق والضغط العكسي
يحتوي HTTP/2 على تحكم مضمّن في التدفق. إذا كان المستهلك بطيئًا، يوقف gRPC المرسل مؤقتًا تلقائيًا، مما يمنع تجاوز سعة الذاكرة. لا تخزّن الرسائل مؤقتًا يدويًا؛ دع البث يتولى ذلك.
// DO: Let gRPC flow control handle backpressure
await foreach (var chunk in requestStream.ReadAllAsync(ct))
{
await ProcessChunkAsync(chunk); // naturally paced
}
// DON'T: Buffering all chunks defeats flow control
var allChunks = new List<FileChunk>();
await foreach (var chunk in requestStream.ReadAllAsync(ct))
allChunks.Add(chunk); // may OOM on large uploadsالإغلاق النصفي واكتمال البث
في البث ثنائي الاتجاه، يمكن لأي من الطرفين إغلاق نصف الكتابة لديه مع الاستمرار في القراءة من الطرف الآخر. يتيح ذلك للعميل الإشارة إلى «انتهيت من الإرسال» مع الاستمرار في انتظار استجابات الخادم.
// Client signals it's done sending
await call.RequestStream.CompleteAsync(); // half-close
// Continue reading server responses after half-close
await foreach (var reply in call.ResponseStream.ReadAllAsync())
{
Console.WriteLine(reply.Content);
}استخدام القنوات للبث ثنائي الاتجاه الآمن مع الخيوط
اجمع System.Threading.Channels مع البث ثنائي الاتجاه لتطبيق نمط منتِج-مستهلك واضح يوزّع الرسائل بأمان على مستمعين متعددين.
private readonly Channel<ChatMessage> _broadcast =
Channel.CreateUnbounded<ChatMessage>();
// Producer: receives from each client stream
public async Task ReceiveLoopAsync(
IAsyncStreamReader<ChatMessage> reader, CancellationToken ct)
{
await foreach (var msg in reader.ReadAllAsync(ct))
await _broadcast.Writer.WriteAsync(msg, ct);
}
// Consumer: writes to each client's response stream
public async Task SendLoopAsync(
IServerStreamWriter<ChatMessage> writer, CancellationToken ct)
{
await foreach (var msg in _broadcast.Reader.ReadAllAsync(ct))
await writer.WriteAsync(msg);
}من الواقع العملي: مسار تحليلات فورية
تقبل خدمة لاستيعاب البيانات بثًا من أحداث القياس عن بُعد القادمة من أجهزة إنترنت الأشياء، وتبث إحصاءات مجمّعة في الوقت الفعلي — وهو مثال مثالي على استخدام البث ثنائي الاتجاه.
service TelemetryService {
rpc StreamTelemetry(stream TelemetryEvent) returns (stream AggregatedStats);
}
// Server implementation sends a rolling aggregate every 100 events:
public override async Task StreamTelemetry(
IAsyncStreamReader<TelemetryEvent> requests,
IServerStreamWriter<AggregatedStats> responses,
ServerCallContext context)
{
int count = 0;
double total = 0;
await foreach (var e in requests.ReadAllAsync(context.CancellationToken))
{
total += e.Value;
if (++count % 100 == 0)
await responses.WriteAsync(
new AggregatedStats { Count = count, Average = total / count });
}
}تحقّق سريعًا
في البث من العميل، متى يرسل الخادم استجابته الوحيدة؟
مراجعة: البث من العميل والبث ثنائي الاتجاه
أهم النقاط:
- البث من العميل: يرسل العميل رسائل متعددة ويرسل الخادم ردًا واحدًا — وهو مناسب لتحميل الملفات
- البث ثنائي الاتجاه: يرسل الطرفان بشكل مستقل — وهو مناسب للمحادثات وتدفّقات البيانات المباشرة
- استخدم
ReadAllAsync()لاستهلاك البث باستخدامawait foreach - استدعِ
RequestStream.CompleteAsync()لإغلاق جانب الكتابة لدى العميل إغلاقًا نصفيًا - يتولى تحكم HTTP/2 في التدفق معالجة الضغط العكسي تلقائيًا — لا تخزّن التدفقات كاملة مؤقتًا
- يتكامل System.Threading.Channels جيدًا مع البث ثنائي الاتجاه في سيناريوهات التوزيع على عدة مستهلكين
الأسئلة الشائعة
هل درس «البث من العميل والبث ثنائي الاتجاه» مجاني؟
نعم — نص درس «البث من العميل والبث ثنائي الاتجاه» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة C# Academy، انتقل إلى CoddyKit PRO. تتضمن دورة C# Academy 4 دروس في المجموع.
ماذا ستتعلم في «البث من العميل والبث ثنائي الاتجاه»؟
ابنوا قنوات للبث من العميل والبث ثنائي الاتجاه كامل الازدواج للسيناريوهات ذات معدل النقل المرتفع. تتمرن على C# Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ C# Academy؟
لا تُشترط خبرة سابقة. C# Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.
كم من الوقت يستغرق درس «البث من العميل والبث ثنائي الاتجاه»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس C# Academy هذا؟
نعم. كل درس في C# Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- أساسيات gRPC وProtobuf
- استدعاءات RPC الأحادية والبث من الخادم
- البث من العميل والبث ثنائي الاتجاه
- المهل الزمنية والإلغاء والمعترضات