الأنابيب المسمّاة عمليّاً ── IPC القياسيّ في Windows من التصميم إلى الأمان

· آخر تحديث: · · Windows, الاتّصال بين العمليّات, تطوير Windows, C#, C++, الأمان, Win32 API

سجل التعديلات (النسخة الأولى، نُشرت في 22 Aug، 2026)
النشر الأول
الاستشهاد بهذا المقال(DOI (الأرشيف المسجّل): 10.5281/zenodo.22176759)

تشير معرّفات DOI أدناه إلى إصدارات مؤرشفة سابقًا، وقد تختلف عن النص الحالي. للإشارة إلى النص الحالي، استخدم رابط هذه الصفحة.

غو كومورا (2026). الأنابيب المسمّاة عمليّاً ── IPC القياسيّ في Windows من التصميم إلى الأمان. شركة كومورا سوفت ذ.م.م.. https://comcomponent.com/ar/blog/windows-named-pipes-practical-guide/

DOI (الأرشيف المسجّل)
10.5281/zenodo.22176759
DOI (آخر إصدار مسجّل)
10.5281/zenodo.22241137

«أريد إرسال أوامر من شاشة إعدادات إلى خدمة مقيمة.» «أريد فصل العمل الذي يحتاج صلاحيّات مدير فقط إلى عمليّة منفصلة.» عندما تبني هذا النوع من الاتّصال بين العمليّات (IPC) على Windows، أوّل ما يُنظَر فيه أنبوب مسمّى.

سبب كونه المرشّح الأوّل للاتّصال داخل الحاسوب نفسه ليس سهولة القراءة والكتابة وحدها. المزيّة الكبيرة أنّك تستطيع تضييق من يتّصل بـ ACL في Windows، وعند الحاجة فحص حساب Windows للطرف وفعل العمل بصلاحيّاته. غير أنّ إنشاء أنبوب لا يجعله آمناً من تلقاء نفسه؛ يجب ضبط الاتّصال والصلاحيّات.123

يرتّب هذا المقال التصميم بترتيب شكل الاتّصال، وحدود الرسالة، وبنية الخادم، والأمان، والسلوك عند الفشل. موجَّه إلى المطوِّرين الذين يكتبون تطبيقات أعمال وخدمات على Windows. يأخذ «المرشّح الأوّل لـ IPC على الجهاز نفسه» من مقالة جدول قرار IPC ويحفر حتّى القرارات التي تتّخذها وقت التنفيذ.

1. الخلاصة أوّلاً: خمسة قرارات تصميم

قبل اختيار واجهة، قرِّر طرف الاتّصال، ووحدة البيانات، والاتّصالات المتزامنة، والصلاحيّات، وكيف تُعالَج الإخفاقات.

القرار قاعدة الإبهام الأساسيّة اقرأ المزيد
مع من تتّصل لعمليّات Windows على الحاسوب نفسه، اجعل الأنبوب المسمّى المرشّح الأوّل. إن كانت النشر البعيد أو أنظمة تشغيل أخرى أو إعادة استخدام بروتوكول قائم مهمّة، فانظر في خيار قائم على TCP الفصل 2
ما الذي يُعدّ عنصراً واحداً إن عوملت كتابة واحدة عنصراً واحداً، وضع الرسالة. إن كان لديك تأطير أصلاً، وضع البايت الفصل 3
كيف تقبل عدّة عملاء وفِّر عدّة نُسَخ بالاسم نفسه. لتنفيذ .NET جديد، الإدخال/الإخراج غير المتزامن وasync/await الاختيار المباشر الفصل 4
من يجوز له الاتّصال واستعارة الصلاحيّات صمِّم رفض البعيد وACL وضمان النسخة الأولى ومستوى انتحال هويّة العميل مجموعة واحدة الفصلان 5 و6
ماذا تفعل عندما يفشل الاتّصال ابنِ انتظار البدء وإعادة الاتّصال وحدّ حجم الرسالة وتأكيد الردّ في البروتوكول الفصل 7

مع أنبوب مسمّى، التحكّم في الاتّصال عبر ACL وانتحال هويّة العميل متاحان آليّات Windows. مع TCP على localhost، عليك تصميم مصادقة منفصلة لإثبات من الطرف. من جهة أخرى، إن أردت التوسّع لاحقاً إلى اتّصال بعيد، أو الاتّصال بأنظمة تشغيل أخرى، أو استخدام أصول قائمة كـ gRPC، فخيار قائم على TCP مفيد.23

النقطة الأهمّ ألّا تنفِّذ أبداً طلباً فشل انتحال هويّته. تجاهل الفشل فتستمرّ المعالجة بصلاحيّات الخادم نفسه لا بصلاحيّات العميل. إن كنت تبني خدمة مميَّزة، اقرأ الفصلين 5 و6 حتّى النهاية، لا عيّنات الشيفرة وحدها.4

2. كيف تعمل الاتّصالات: اسم مشترك واحد، قناة واحدة لكلّ عميل

2.1 افصل أدوار الإنشاء والاتّصال والقراءة/الكتابة

الأنبوب المسمّى قناة أحاديّة الاتّجاه أو ثنائيّته تُعرَّف باسم مثل \\.\pipe\MyCompany.MyApp.Control. يبدأ الخادم والعميل بواجهات مختلفة.1

المرحلة جانب الخادم جانب العميل
إعداد القناة أنشئ نسخة بـ CreateNamedPipe استخدم اسم الأنبوب الذي أعدّه الخادم
الاتّصال انتظر عميلاً بـ ConnectNamedPipe افتح الاسم نفسه بـ CreateFile
تبادل البيانات اقرأ واكتب بـ ReadFile / WriteFile اقرأ واكتب بـ ReadFile / WriteFile

بعد الاتّصال، يعامله الطرفان بالطريقة نفسها كإدخال/إخراج ملفّ. بعد تحديد الاسم، استيعاب أفكار النُسَخ والاتّجاه التالية يجعل الاتّصالات المتعدّدة وأخطاء الاتّصال أسهل فهماً.

2.2 نسخة واحدة تتولّى عميلاً واحداً

يمكن إنشاء عدّة أنابيب بالاسم نفسه، وتصير نسخة واحدة القناة إلى عميل واحد. مشاركة اسم لا تعني أنّ كلّ عميل يشارك قناة واحدة. أوّل CreateNamedPipe يحدِّد العدد الأقصى للنُسَخ. PIPE_UNLIMITED_INSTANCES متاح أيضاً حداً أعلى.15

البنية الأساسيّة لأنبوب مسمّىينشئ الخادم عدّة نُسَخ أنبوب بالاسم نفسه وينتظر الاتّصالات بـ ConnectNamedPipe؛ ويفتح كلّ عميل الاسم بـ CreateFile ويملك قناة ثنائيّة الاتّجاه واحداً لواحد مع نسخة واحدةالخادمالنسخة 1النسخة 2النسخة 3العميل Aالعميل Bالعميل C

الشكل 1: مثال أنبوب ثنائيّ الاتّجاه. بتوفير عدّة نُسَخ بالاسم نفسه، يتّصل الخادم واحداً لواحد مع كلّ من عدّة عملاء.

2.3 «الاتّجاه» يُسمَّى من وجهة نظر الخادم

يجب أن يطابق تخصيص وصول العميل اتّجاه الأنبوب الذي أنشأه الخادم. إن اختلفا، يفشل CreateFile.6

الاتّجاه الذي ينشئه الخادم سلوك الخادم الوصول الذي يحدِّده العميل
ثنائيّ الاتّجاه يقرأ ويكتب يمكن فتحه للقراءة أو للكتابة أو للاثنين
صادر يكتب فقط افتح للقراءة فقط
وارد يقرأ فقط افتح للكتابة فقط

إن كانت كلّ النُسَخ قيد الاستخدام، يعيد CreateFile للعميل ERROR_PIPE_BUSY. انتظر نسخة حرّة بـ WaitNamedPipe، ثمّ استدعِ CreateFile مرّة أخرى. هذا فشل يختلف عن عدم إنشاء الخادم الأنبوب بعد، لذا يعالجه القسم 7.1 مع سباقات ترتيب البدء.6

2.4 حتّى للاستخدام المحلّي، قرِّر كيف تُعالَج الاتّصالات البعيدة

يمكن استخدام أنبوب مسمّى أيضاً لاتّصالات بعيدة عبر SMB، بالشكل \\server\pipe\name. في تصميم حديث، غير أنّه نادراً ما يوجد سبب لاستخدام هذا المسار بنشاط، ولـ IPC المحلّي المهمّ ألّا تترك مساراً لا تستخدمه مفتوحاً. ارفضه صراحة بـ PIPE_REJECT_REMOTE_CLIENTS في القسم 5.1.17

3. اختيار الوضع: قرِّر «كم يُعدّ عنصراً واحداً»

3.1 وضع الرسالة للطلب/الردّ، ووضع البايت إن كانت لديك حدود أصلاً

الفرق بين وضعي النقل هل يحفظ الأنبوب حدود الكتابات.5

الوضع ما يراه المستقبل يناسب
وضع البايت (PIPE_TYPE_BYTE) تدفّق بايتات بلا انقطاع، كـ TCP بيانات تحمل تأطيرها الخاصّ كبادئة طول، أو نقل تدفّق
وضع الرسالة (PIPE_TYPE_MESSAGE مع وضع قراءة الرسالة) وحدات تكون فيها كتابة واحدة رسالة واحدة طلبات وردود واحداً واحداً
الفرق بين وضع البايت ووضع الرسالةفي وضع البايت تصير ثلاث كتابات تدفّق بايتات بلا انقطاع على المستقبل تقسيمه بنفسه، بينما في وضع الرسالة تُحفَظ وحدة كلّ كتابة وتصل إلى المستقبل كما هيوضع البايت: اكتب AAA وBB وCCCCيُستقبَل تدفّق البايتات AAABBCCCCحدود (تأطير) تصمِّمها بنفسكوضع الرسالة: الكتابات الثلاث نفسهايُستقبَل ثلاثة عناصر: AAA وBB وCCCCوحدة كلّ كتابة محفوظة

الشكل 2: في وضع البايت يدير المستقبل الحدود. وضع الرسالة يستطيع حفظ وحدة كلّ كتابة، لكن قراءة واحدة لا تستقبل بالضرورة الرسالة كلّها.

إن لم تكن لديك حدودك بعد وتريد معاملة كتابة واحدة طلباً واحداً، وضع الرسالة مريح. إن كنت تستخدم أصلاً شيئاً كتنسيق تسلسل مسبوق بالطول، وضع البايت مناسب. في .NET، PipeTransmissionMode.Message هو تخصيص نوع الرسالة.8

لأنبوب ثنائيّ الاتّجاه من نوع الرسالة ثمّة أيضاً TransactNamedPipe، يرسل طلباً ويستقبل الردّ في استدعاء واحد.9

3.2 نوع الرسالة ووضع القراءة إعدادان منفصلان

وضع القراءة يُضبَط لكلّ مقبض. تحديد PIPE_READMODE_MESSAGE في CreateNamedPipe إعداد جانب الخادم. مقبض فتحه العميل بـ CreateFile يكون افتراضيّاً وضع قراءة بايت.6

التنفيذ جانب الخادم جانب العميل
Win32 حدِّد PIPE_TYPE_MESSAGE وPIPE_READMODE_MESSAGE بعد الاتّصال، حدِّد PIPE_READMODE_MESSAGE بـ SetNamedPipeHandleState
.NET حدِّد PipeTransmissionMode.Message بعد الاتّصال، اضبط NamedPipeClientStream.ReadMode إلى Message

النقطة ألّا تفترض أنّ جعل الخادم وحده من نوع الرسالة يتيح للعميل القراءة بالوحدات نفسها.

3.3 حتّى في وضع الرسالة، تحتاج قراءة الباقي

حفظ حدود الرسالة وإدخال الرسالة كلّها في مخزن الاستقبال شيئان مختلفان. إن كان المخزن صغيراً، يعيد ReadFile القيمة ERROR_MORE_DATA وتُقسَم الرسالة. تحتاج حلقة تبقي الجزء المستقبَل حتّى الآن، وتقرأ الباقي، وتجمعهما.56

نتيجة القراءة المعالجة
نجاح عالج الرسالة مكتملة
ERROR_MORE_DATA ثمّة المزيد، فاقرأ الباقي واجمع
أيّ خطأ آخر لا تواصل معالجة الطلب؛ عامله فشل اتّصال كانقطاع

إن مرّت الطلبات الصغيرة وانكسرت الكبيرة فقط، افحص منطق قراءة الباقي هذا. كذلك، الجمع بلا حدّ غير مقبول. قرِّر حجماً أقصى لرسالة واحدة جزءاً من البروتوكول، وتبنَّ سياسة قطع الاتّصال عند تجاوز الحدّ. هذا يمنع هدر الذاكرة الذي تسبّبه رسائل هائلة.

4. بنية الخادم: افصل القبول عن معالجة العميل

4.1 قارن تصميم مؤشّر الترابط المتزامن وتصميم Overlapped

عمليّة الخادم الأساسيّة الدورة أنشئ نسخة، انتظر اتّصالاً، اقرأ واكتب، اقطع وانتقل إلى التالي. لقبول عدّة عملاء في آن، شغِّل هذا التدفّق على عدّة نُسَخ.

التصميم الآليّة المزايا والتحذيرات
تصميم مؤشّر ترابط متزامن خصِّص مؤشّر ترابط لكلّ نسخة وعالج بإدخال/إخراج متزامن الشيفرة مباشرة. غير أنّها تستهلك مؤشّر ترابط لكلّ عميل، وتحتاج طريقة للخروج من إدخال/إخراج حاجب عند الإيقاف
تصميم Overlapped (غير متزامن) أنشئ بـ FILE_FLAG_OVERLAPPED وأصدر الاتّصال والقراءة والكتابة بصورة غير متزامنة يستطيع عدد صغير من مؤشّرات الترابط معالجة اكتمالات لعدّة نُسَخ

عيّنة مايكروسوفت الرسميّة تضع حدث كلّ نسخة في مصفوفة، وتنتظر بـ WaitForMultipleObjects، وتعالج عدّة اتّصالات على مؤشّر ترابط واحد. تفصل عدد العملاء عن عدد مؤشّرات الترابط. على نطاق أكبر، يمكنك أيضاً اختيار إرفاق IOCP أو إدخال/إخراج مجمع مؤشّرات الترابط.10

لكيفيّة عمل الإدخال/الإخراج غير المتزامن نفسه، انظر مقالة الإدخال/الإخراج المتزامن وغير المتزامن.

4.2 لتنفيذ .NET جديد، async/await الاختيار المباشر

في .NET يمكنك استخدام WaitForConnectionAsync / ReadAsync / WriteAsync في NamedPipeServerStream مع async/await. ما لم يكن ثمّة سبب خاصّ بخلاف ذلك، أوصي بهذا التصميم غير المتزامن للتنفيذات الجديدة. عندما تتلقّى حلقة القبول اتّصالاً، تسلِّمه إلى معالجة لكلّ عميل وتعود إلى القبول على النسخة التالية.8

التالي هيكل حلقة القبول تلك. كمثال للاستخدام بين عمليّات المستخدم نفسه، يحدِّد PipeOptions.Asynchronous وPipeOptions.CurrentUserOnly. على Windows، يقيِّد CurrentUserOnly الاتّصالات بالمستخدم نفسه ومستوى الرفع نفسه. هذا ليس مثالاً لاتّصال خدمة وواجهة تحت حسابين مختلفين. تلك الحالة تحتاج تصميم ACL في القسم 5.2.11

// C#: هيكل خادم يقبل عدّة عملاء
while (!token.IsCancellationRequested)
{
    var server = new NamedPipeServerStream(
        "MyCompany.MyApp.Control",
        PipeDirection.InOut,
        NamedPipeServerStream.MaxAllowedServerInstances,
        PipeTransmissionMode.Message,
        PipeOptions.Asynchronous | PipeOptions.CurrentUserOnly);

    try
    {
        await server.WaitForConnectionAsync(token);
    }
    catch
    {
        await server.DisposeAsync();        // عند المغادرة قبل اتّصال، تخلَّص منه بنفسك
        throw;
    }
    _ = HandleClientAsync(server, token);   // بعد الاتّصال، تنتقل الملكيّة إلى المعالج
}

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

هذه الشيفرة هيكل يحذف بروتوكول الاتّصال وجسم المعالج. في الإنتاج، أدِر استثناءات واكتمال المهام المنفصلة، واجمعها مع قراءة الباقي وحدّ الحجم من الفصل 3، والأمان من الفصلين 5 و6، ومعالجة الانقطاع وتأكيد الردّ من الفصل 7. CurrentUserOnly خيار مريح لتقييد المتّصلين دون كتابة ACL بنفسك، لكنّه وحده لا يُكمل تصميماً لخدمة مميَّزة.

5. الأمان: ثلاث نقاط جانب الخادم ونقطة جانب العميل

مزايا أمان الأنابيب المسمّاة تُكتسَب فقط عندما تُضبَط صحّة. خصوصاً، في تصميم الوسيط «خدمة بصلاحيّات مدير زائد تطبيق واجهة منخفض الصلاحيّات»، الأنبوب هو حدّ الصلاحيّة.

5.1 جانب الخادم: ارفض الاتّصالات البعيدة غير اللازمة

إن استخدمت الأنبوب IPC محلّيّاً، حدِّد PIPE_REJECT_REMOTE_CLIENTS في CreateNamedPipe. يُرفَض العملاء البعيدون تلقائيّاً، فيُغلَق المسار حيث يمكن لأنبوب قصدته لتطبيقات على الحاسوب نفسه أن يُفتَح أيضاً من الشبكة.7

5.2 جانب الخادم: ضيِّق المتّصلين والحقوق بـ ACL

مرِّر واصف أمان في SECURITY_ATTRIBUTES واجعل صريحاً أيّ المستخدمين والمجموعات يجوز لهم الاتّصال. ACL الافتراضي ليس بالضرورة صارماً بما يكفي لاستخدامك.2

ما يسهل تفويته هنا صلاحية الكتابة التي تمنحها للعملاء. إن منح ACL كتابة عامّة (GENERIC_WRITE / FILE_GENERIC_WRITE)، فإنّها تشمل الحقّ المكافئ لـ FILE_CREATE_PIPE_INSTANCE. يستطيع عميل مصرَّح له عندئذ إنشاء نسخة خادم بالاسم نفسه بنفسه واعتراض الاتّصالات اللاحقة.2

امنح القراءة والكتابة الحقوق الفرديّة اللازمة، ولا تسلِّم العملاء حقّ إنشاء النُسَخ. تصميم ACL يغطّي لا «من مسموح» فحسب، بل أيضاً «ماذا مسموح».

5.3 جانب الخادم: افحص أنّ النسخة الأولى لم تُؤخَذ قبلك

إن أنشأ عمليّة خبيثة أنبوباً بالاسم نفسه أوّلاً، يتّصل العملاء بذلك الخادم المزوَّر. يحدِّد الخادم FILE_FLAG_FIRST_PIPE_INSTANCE فقط عند إنشاء النسخة الأولى، ضامناً أنّه الأوّل. إن فشل ذلك، اشتبه بأنّ الاسم أُخذ قبلك وتوقّف.5

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

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

5.4 جانب العميل: قرِّر إلى أيّ مدى يجوز للخادم استعارة صلاحيّاتك

دفاعاً ضدّ خادم مزوَّر، يُبقي العميل مستوى انتحال الهويّة عند الحدّ الأدنى اللازم. إن سمح التصميم بالتحقّق من الهويّة فقط، حدِّد SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION في CreateFile. يستطيع الخادم عندئذ فحص الحساب لكن لم يعد يستطيع استعارة صلاحيّاته لأداء وصول حقيقي.3

ما تريد من الخادم فعله سياسة جانب العميل
التحقّق من هويّة المتّصل فقط ضيِّق إلى مستوى التعرّف (SECURITY_IDENTIFICATION)
أداء وصول حقيقي، كفتح ملفّات بصلاحيّات العميل يجب السماح بمستوى انتحال الهويّة (SECURITY_IMPERSONATION). اقرنه بالتحقّق من أنّك متّصل بالخادم الشرعي

عند مستوى التعرّف، معالجة الوسيط التي تؤدّي وصولاً حقيقيّاً بصلاحيّات العميل غير ممكنة. بالمقابل، السماح بانتحال الهويّة دون التحقّق من هدف الاتّصال يعرِّض صلاحيّاتك للاستعارة من خادم مزوَّر.

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

6. استخدام انتحال الهويّة: لا تنفِّذ أبداً طلباً فشل انتحال هويّته

6.1 انتحل بعد قراءة الطلب، لا بعد الاتّصال فحسب

الواجهة ليتحقّق الخادم من هويّة العميل ويعالج بصلاحيّاته هي ImpersonateNamedPipeClient. موضوعها سياق أمان مرسل آخر رسالة قُرئت من ذلك الأنبوب. اقرأ الطلب أوّلاً، ثمّ استدعِها.4

يسري انتحال الهويّة على مؤشّر الترابط المستدعي. إن فتحت ملفّاً في تلك الحالة، يُحكَم في السماح بالوصول مقابل صلاحيّات العميل. حتّى لخدمة مميَّزة، هذه آليّة «تنفيذ العمليّة المطلوبة بصلاحيّات الطالب».3

6.2 اجعل القراءة وتأكيد النجاح والعودة وحدة واحدة

الترتيب المطلوب اقرأ الطلب، حاول انتحال الهويّة، أكِّد النجاح، اعمل بصلاحيّات العميل، وعد إلى السياق الأصلي.

تدفّق معالجة طلب يستخدم انتحال الهويّةيقرأ الخادم طلباً من الأنبوب، ويؤكِّد نجاح ImpersonateNamedPipeClient، ثمّ يؤدّي العمليّة بصلاحيّات العميل ويعود إلى سياقه بـ RevertToSelf. إن فشل انتحال الهويّة، يرفض الطلب دون تنفيذهالخادمالعميلالخادمالعميلعند الفشل، ارفض دون تنفيذ الطلبإرسال طلبقراءة الطلبImpersonateNamedPipeClientأداء العمليّة بصلاحيّات العميلالعودة إلى السياق الأصلي بـ RevertToSelfالردّ بالنتيجة

الشكل 3: إن فشل انتحال الهويّة، لا تنفِّذ الطلب. وحتّى عند النجاح، لا تكتمل المتتالية حتّى يعيد RevertToSelf السياق الأصلي بعد العمل.

لا تنتقل أبداً إلى معالجة الطلب دون فحص قيمة إرجاع ImpersonateNamedPipeClient. عند الفشل، لم ينتقل مؤشّر الترابط إلى صلاحيّات العميل، وتُستخدَم صلاحيّات عمليّة الخادم نفسها. إن عمل الخادم تحت حساب مميَّز، يمرِّر عمليّات غير مسموح بها للعميل. تنصّ وثائق مايكروسوفت صراحة أيضاً على أنّه عند الفشل يجب عدم تنفيذ طلب العميل.4

عندما ينتهي العمل، عُد بموثوقيّة إلى السياق الأصلي بـ RevertToSelf. ضع فحص النجاح والتنظيف كليهما. تفاصيل كالرموز ومستويات انتحال الهويّة وSeImpersonatePrivilege مشمولة في مقالة رموز انتحال الهويّة في Windows.

7. مطبّات عمليّة: صمِّم على افتراض أنّ الاتّصال سينكسر

7.1 عامل انتظار البدء وانتظار نسخة حرّة إخفاقين مختلفين

عندما لا تستطيع الاتّصال، ميِّز بين عدم إنشاء الخادم الأنبوب بعد وكون كلّ النُسَخ المُنشأة قيد الاستخدام.69

الحالة فعل العميل
الأنبوب غير موجود اسمح بأن يكون الخادم ما يزال يبدأ؛ انتظر قليلاً وأعد المحاولة
ERROR_PIPE_BUSY انتظر نسخة حرّة بـ WaitNamedPipe وأعد محاولة CreateFile
متّصل انتقل إلى الاتّصال. إن لزم، اضبط أيضاً وضع القراءة من القسم 3.2
تدفّق إعادة محاولة اتّصال العميلافتح الأنبوب بـ CreateFile؛ إن لم يوجد الأنبوب فانتظر قليلاً وأعد المحاولة؛ وإن كانت كلّ النُسَخ قيد الاستخدام وأُعيد ERROR_PIPE_BUSY فانتظر نسخة حرّة بـ WaitNamedPipe ثمّ أعد المحاولة؛ عند النجاح ابدأ الاتّصالنجاحالأنبوب غير موجودERROR_PIPE_BUSYافتح بـ CreateFileالنتيجة؟ابدأ الاتّصالانتظر قليلاً (الخادم لم يبدأ)انتظر نسخة حرّة بـ WaitNamedPipe

الشكل 4: «غير موجود» و«ممتلئ» حالتان مختلفتان. في الحالتين، انتظر كما تتطلّب الحالة، ثمّ أعد محاولة الاتّصال.

في جانب الخادم، المبدأ بدء الانتظار بـ ConnectNamedPipe قبل أن يبدأ العميل. سباقات ترتيب البدء يمكن أن تحدث مع ذلك، لذا ابنِ إعادة المحاولة في جانب العميل أيضاً.9

7.2 اجعل الانقطاع مساراً عاديّاً في الشيفرة، لا حالة طارئة

عندما تخرج عمليّة الطرف، تفشل القراءات والكتابات بـ ERROR_BROKEN_PIPE وما شابه. عامل هذا اتّصالاً يوميّاً.

عندما يكشف الخادم انقطاعاً، يفصل النسخة بـ DisconnectNamedPipe ويستعدّ للاتّصال التالي. يعيد العميل الاتّصال. فكرة «إعادة اتّصال idempotent»، التي لا تفسد الحالة مهما تكرّرت، المشروحة في مقالة الاستئناف من السكون، فعّالة هنا أيضاً.

7.3 الرسائل الكبيرة تحتاج «قراءة الباقي» و«حدّاً» كليهما

معالجة ERROR_MORE_DATA في القسم 3.3 منطق قراءة رسالة على أجزاء. حجم الرسالة الأقصى، بالمقابل، قاعدة لتقييد كمّ البيانات التي تقبلها. لا تخلط بينهما.

إن أرسل طرف خبيث أو معطوب رسالة هائلة، يهدر تنفيذ يقرأ الباقي بلا حدّ الذاكرة. تبنَّ سياسة تعريف حدّ في البروتوكول وقطع الاتّصال عند تجاوزه.

7.4 ميِّز نجاح الكتابة عن إنهاء الطرف معالجته

نجاح WriteFile لا يعني أنّ تطبيق الطرف قد عالج البيانات. للعمليّات التي تحتاج معرفة أنّها نُفِّذت فعليّاً، أكِّد برسالة ردّ.

كذلك، كي يمكن مطابقة كلّ ردّ بالطلب الذي يجيب عنه، أدرج علاقة الطلبات والردود في البروتوكول. فصل «أُرسل» عن «انتهت المعالجة» هو ما يقود إلى تأكيد الاكتمال بمفهوم الأعمال.

8. الخلاصة: قرِّر القناة والبيانات والصلاحيّات كلّاً على حدة

يجمع الأنبوب المسمّى سهولة استخدام إدخال/إخراج الملفّ مع نموذج أمان Windows لـ ACL وانتحال الهويّة، وهو المرشّح الأوّل لـ IPC على الجهاز نفسه. غير أنّ إنشاء قناة أمر يختلف عن إنشاء بروتوكول آمن يصعب كسره.

في تصميم الاتّصال والبيانات، ابدأ من أنّ نسخة واحدة تتولّى عميلاً واحداً. اختر وضع الرسالة للطلب/الردّ ووضع البايت إن كان لديك تأطير أصلاً، واضبط وضع قراءة الرسالة في جانب العميل أيضاً. معالجة القراءات المجزّأة وحجم أقصى لازمان أيضاً. لتنفيذ .NET جديد يقبل عدّة اتّصالات، الإدخال/الإخراج غير المتزامن وasync/await الاختيار المباشر.

في تصميم الصلاحيّات، عامل رفض البعيد وACL صريحاً وضمان النسخة الأولى وتقليل مستوى انتحال هويّة جانب العميل مجموعة واحدة. إن استخدمت انتحال الهويّة، استدعِه بعد قراءة الطلب، ولا تنفِّذ عند الفشل، وعُد بـ RevertToSelf بعد العمل.

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

مقالات ذات صلة

مجالات الاستشارة ذات الصلة

تتعامل شركة كومورا سوفت ذ.م.م. مع تصميم وتنفيذ أنظمة تتضمّن اتّصالاً بين العمليّات، كفصل خدمة عن تطبيق واجهتها وعزل صلاحيّات المدير؛ واستبدال IPC قائم (ذاكرة مشتركة، مقابس محليّة، COM، وما شابه) بأنابيب مسمّاة؛ ومراجعات أمان لاتّصال أنبوب خدمة مميَّزة. تواصل معنا حتّى إن أردت فقط مناقشة تصميم بروتوكول.

روابط مرجعيّة

  1. Microsoft Learn, Named Pipes. حول كون الأنبوب المسمّى قناة أحاديّة الاتّجاه أو ثنائيّته بين خادم أنبوب وعميل أنبوب واحد أو أكثر؛ ومشاركة كلّ نسخة الاسم نفسه مع امتلاك مخازن ومقابض مستقلّة؛ وإمكان الاستخدام من عمليّات محلّيّة وبعيدة. ↩ ↩2 ↩3 ↩4

  2. Microsoft Learn, Named Pipe Security and Access Rights. حول تركيب حقوق وصول الأنبوب المسمّى؛ وتضمّن GENERIC_WRITE لـ FILE_CREATE_PIPE_INSTANCE، لذا فإنّ منح عميل كتابة عامّة يسمح أيضاً بإنشاء نسخة خادم؛ ومنح قراءة البيانات وكتابتها حقوق وصول فرديّة. ↩ ↩2 ↩3 ↩4

  3. Microsoft Learn, Impersonating a Named Pipe Client. حول إتاحة انتحال الهويّة لمؤشّر ترابط الخادم العمل ضمن صلاحيّات العميل؛ وكون مستوى انتحال الهويّة الافتراضي SecurityImpersonation؛ وقدرة العميل على التحكّم في مستوى انتحال الهويّة بعلم SECURITY_SQOS_PRESENT وقت CreateFile (SECURITY_IDENTIFICATION يسمح بالتعرّف فقط). ↩ ↩2 ↩3 ↩4

  4. Microsoft Learn, ImpersonateNamedPipeClient function (namedpipeapi.h). حول بدء مؤشّر ترابط جانب الخادم انتحال الهويّة في سياق أمان عميل آخر رسالة قُرئت من الأنبوب؛ والعودة بـ RevertToSelf بعد الاكتمال؛ واستمرار بعد فشل انتحال الهويّة فيسبّب التنفيذ في سياق عمليّة الخادم نفسه (المميَّز)، لذا يجب فحص قيمة الإرجاع دائماً وعند الفشل يجب عدم تنفيذ طلب العميل. ↩ ↩2 ↩3

  5. Microsoft Learn, CreateNamedPipeW function (namedpipeapi.h). حول اتّجاه الأنبوب (وارد، صادر، ثنائيّ)، ونوع البايت ونوع الرسالة (PIPE_TYPE_BYTE / PIPE_TYPE_MESSAGE) ووضع القراءة (PIPE_READMODE_MESSAGE)، والعدد الأقصى للنُسَخ (PIPE_UNLIMITED_INSTANCES)، والوضع غير المتزامن عبر FILE_FLAG_OVERLAPPED، وضمان النسخة الأولى عبر FILE_FLAG_FIRST_PIPE_INSTANCE، والمهلة الافتراضيّة التي يستخدمها WaitNamedPipe. ↩ ↩2 ↩3 ↩4 ↩5

  6. Microsoft Learn, Named Pipe Client. حول فتح العميل الأنبوب بـ CreateFile؛ وERROR_PIPE_BUSY عندما تكون كلّ النُسَخ قيد الاستخدام وانتظار واحدة حرّة بـ WaitNamedPipe؛ وكون المقبض المفتوح افتراضيّاً قراءة بايت وحاجباً وغير overlapped، مع قدرة SetNamedPipeHandleState على تغييره إلى وضع قراءة الرسالة. ↩ ↩2 ↩3 ↩4 ↩5

  7. Microsoft Learn, CreateNamedPipeW function (namedpipeapi.h). حول وضعَي العميل البعيد، PIPE_ACCEPT_REMOTE_CLIENTS (قبول الاتّصالات البعيدة وفحصها مقابل واصف الأمان) وPIPE_REJECT_REMOTE_CLIENTS (رفض الاتّصالات من عملاء بعيدين تلقائيّاً). ↩ ↩2

  8. Microsoft Learn, How to: Use Named Pipes for Network Interprocess Communication (.NET). حول الاتّصال والقراءة/الكتابة بـ NamedPipeServerStream / NamedPipeClientStream، ونقل وحدة الرسالة عبر PipeTransmissionMode.Message، ومعالجة عدّة عملاء بدوال غير متزامنة. ↩ ↩2

  9. Microsoft Learn, Named Pipe Operations. حول العمليّات المتداخلة عبر ReadFileEx / WriteFileEx، وقراءة غير مستهلِكة عبر PeekNamedPipe، وإرسال TransactNamedPipe طلباً واستقبال الردّ في استدعاء واحد على أنبوب ثنائيّ الاتّجاه من نوع الرسالة، وإمكان أن تسبّب قراءة حاجبة قبل بدء العميل سباقاً. ↩ ↩2 ↩3

  10. Microsoft Learn, Named Pipe Server Using Overlapped I/O. حول العيّنة الرسميّة لخادم بمؤشّر ترابط واحد يعالج اتّصالات متزامنة مع عدّة عملاء عبر عمليّات متداخلة. حول البنية التي تنتظر هيكل OVERLAPPED لكلّ نسخة وحدثها بـ WaitForMultipleObjects وتقدِّم آلة حالة النسخة المكتملة، وتأكيد اكتمال إدخال/إخراج معلَّق بـ GetOverlappedResult. ↩

  11. Microsoft Learn, PipeOptions Enum (System.IO.Pipes). حول تمكين الإدخال/الإخراج غير المتزامن بـ Asynchronous، وقدرة CurrentUserOnly على السماح بالاتّصالات فقط مع عمليّات المستخدم نفسه (ومستوى الرفع نفسه). ↩

أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.

ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.

الأسئلة الشائعة

أسئلة شائعة حول موضوع هذه المقالة.

كيف أختار بين الأنابيب المسمّاة وTCP (مقبس localhost)؟
للاتّصال بين العمليّات على الجهاز نفسه، الأنبوب المسمّى المرشّح الأوّل. السبب نموذج الأمان. يتيح الأنبوب التحكّم فيمن يجوز له الاتّصال على مستوى نظام التشغيل بواصف أمان Windows (ACL)، ويستطيع الخادم فحص حساب Windows للطرف المتّصل واستعارته بـ ImpersonateNamedPipeClient. ذلك بخلاف منفذ TCP على localhost، الذي يستطيع أيّ كان الاتّصال به، لذا عليك إثبات هوية الطرف بمصادقة خاصّة بك. من جهة أخرى، خيار قائم على TCP مفيد عندما يُرجَّح أن ينمو الاتّصال لاحقاً إلى اتّصال بعيد، أو عندما تتحدّث أيضاً مع عمليّات على أنظمة تشغيل أخرى، أو عندما تريد استخدام أصول بروتوكول قائمة كـ gRPC. هذا القرار معروض أيضاً في مقالة جدول قرار IPC.
هل أستخدم وضع البايت أم وضع الرسالة؟
إن أردت معاملة كتابة واحدة وحدة معنى واحدة، وضع الرسالة (PIPE_TYPE_MESSAGE + PIPE_READMODE_MESSAGE) مريح. يستطيع المستقبل القراءة بوحدات ما كتبه المرسل، فلا تحتاج إدارة الحدود بنفسك. وضع البايت تدفّق بايتات بلا انقطاع، كـ TCP، وعليك تصميم التأطير (بادئة طول مثلاً) بنفسك. إن كنت تحمل بروتوكولاً يملك تأطيراً أصلاً (مثلاً تنسيق تسلسل مسبوق بالطول)، وضع البايت مناسب. تنبيه واحد: حتّى في وضع الرسالة يحدث قراءة مجزّأة (ERROR_MORE_DATA) عندما يكون مخزن الاستقبال أصغر من الرسالة، لذا ما تزال تحتاج معالجة ذلك. كذلك وضع القراءة إعداد لكلّ مقبض، وCreateNamedPipe يضبطه في جانب الخادم فقط. يجب أن يحدِّد العميل PIPE_READMODE_MESSAGE بـ SetNamedPipeHandleState بعد CreateFile. في .NET يحدِّد الخادم PipeTransmissionMode.Message، ويضبط العميل NamedPipeClientStream.ReadMode إلى Message بعد الاتّصال.
كيف أبني خادماً يتحدّث إلى عدّة عملاء في آن واحد؟
يمكن لأنبوب مسمّى أن يملك عدّة نُسَخ تُنشأ تحت الاسم نفسه، ونسخة واحدة تتولّى عميلاً واحداً. ثمّة تصميمان. أحدهما تصميم متزامن يخصِّص مؤشّر ترابط لكلّ عميل؛ التنفيذ مباشر، لكنّه يستهلك مؤشّر ترابط لكلّ عميل. الآخر يستخدم إدخالاً/إخراجاً غير متزامن بـ FILE_FLAG_OVERLAPPED ويتولّى عدد صغير من مؤشّرات الترابط ConnectNamedPipe وReadFile وWriteFile لكلّ نسخة؛ عيّنة مايكروسوفت الرسميّة تعرض أيضاً تنفيذاً يعالج عدّة نُسَخ على مؤشّر ترابط واحد. في .NET، يتيح WaitForConnectionAsync في NamedPipeServerStream وasync/await كتابة التصميم غير المتزامن ببساطة تقارب المتزامن. ما لم يكن ثمّة سبب خاصّ بخلاف ذلك، أوصي بتصميم .NET غير المتزامن للتنفيذات الجديدة.
ما الحدّ الأدنى الذي ينبغي فعله لأمان الأنابيب المسمّاة؟
أربع نقاط. الأولى، إن لم تُحتَج الاتّصالات البعيدة، حدِّد PIPE_REJECT_REMOTE_CLIENTS وارفض صراحة الاتّصالات عبر الشبكة. الثانية، اضبط ACL مناسباً بـ SECURITY_ATTRIBUTES وضيِّق المستخدمين والمجموعات التي يجوز لها الاتّصال (ACL الافتراضي فضفاض لبعض الاستخدامات). الثالثة، حدِّد FILE_FLAG_FIRST_PIPE_INSTANCE عند إنشاء النسخة الأولى، فتكشف اختطاف الاسم حيث يُنشأ أنبوب بالاسم نفسه قبلك (لا تضع هذا العلم على النسخة الثانية وما بعدها). الرابعة، عميل يريد من الخادم التحقّق من هويّته فقط ينبغي أن يحدِّد SECURITY_SQOS_PRESENT|SECURITY_IDENTIFICATION على CreateFile، كي لا يستطيع خادم مزوَّر استعارة (انتحال) صلاحيّاته. في تصميم وسيط يؤدّي فيه الخادم وصولاً حقيقيّاً بصلاحيّات العميل، يجب السماح بانتحال الهويّة، لذا تطبيق هذا القيد يعتمد على ما إذا كان التصميم يتيح للخادم استعارة الصلاحيّات.
هل ثمّة تحذيرات عند استخدام ImpersonateNamedPipeClient؟
الأهمّ فحص قيمة الإرجاع. إن واصلت بعد فشل انتحال الهويّة، تعمل العمليّات اللاحقة بصلاحيّات عمليّة الخادم نفسها (غالباً عالية)، وتمرّ عمليّات لم يكن ينبغي السماح بها للعميل. تنصّ الوثائق الرسميّة صراحة أيضاً على أنّه عند الفشل يجب عدم تنفيذ طلب العميل. كذلك، لأنّ انتحال الهويّة يُؤدَّى في سياق آخر رسالة قُرئت من الأنبوب، يجب أن تقرأ شيئاً قبل استدعائه، ومتى انتهى العمل يجب العودة بموثوقيّة إلى السياق الأصلي بـ RevertToSelf. الآليّة حول انتحال الهويّة (الرموز ومستويات الانتحال وSeImpersonatePrivilege) مشروحة بالتفصيل في مقالة رموز انتحال الهويّة.

الملف الشخصي للمؤلف

صفحة الملف الشخصي لمؤلف المقالة.

غو كومورا

مؤسّس شركة كومورا سوفت ذ.م.م.

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

العودة إلى المدونة