في هذه المقالة الأخيرة من سلسلة (مفاهيم في الشبكات) نختم باستعراض مفهوم المقابس sockets وفكرة البرمجة مع المقابس socket programming.
مفهوم المقابس Concept of Sockets
من أجل تمكين التطبيقات من أن تتصل بشبكة حاسوب وتتبادل البيانات مع غيرها من التطبيقات عبر الشبكة نفسها، تم إنشاء مفهوم المقبس socket، كتجريد، يشبه مفهوم واصف الملف file descriptor، في سياق أنظمة التشغيل. لنتوقف أولاً قليلاً لنراجع المفاهيم السابقة من أجل أن نلخص الصورة الكبيرة بسرعة.
تتحكم البروتوكولات في عمليات الاتصال، لأنها هي التي تملي القواعد التي يجب أن يسير عليها جميع أطراف الاتصال. على الإنترنت، مجموعة بروتوكولات TCP/IP هي المسؤولة عن هذا الدور. هذه المجموعة هي عبارة عن مجموعة من البروتوكولات تتوزع على الطبقات المختلفة كما مرّ، ويجب أن يعمل عدد منها (واحد على الأقل في كل طبقة) من أجل إتمام الاتصال وتبادل البيانات. في صميم هذه المجموعة من البروتوكولات، بوتوكول الإنترنت IP، المسؤول عن توصيل الرسائل من طرف إلى آخر، عبر عدة خطوات (مروراً بعدة أطراف وسيطة). هذا البروتوكول يتطلب أن يكون لكل طرف على الشبكة معرّف فريد، أو عنوان خاص لا يتكرر مع أكثر من طرف واحد (هذا العنوان يرتبط بكرت الشبكة، وليس بالحاسوب، لذا فإن حاسوباً واحداً قد يملك أكثر من عنوان). لأن هذه العناوين مرتبطة ببروتوكول IP (هو الذي يتطلبها ويفهما)، فإنها تسمى عناوين IP.
المشكلة هنا أن حاسوباً واحداً بكرت شبكة وحيد وعنوان وحيد قد يحتاج إلى أكثر من اتصال في نفس الوقت، وهنا لم يعد العنوان الواحد كافياً لتوصيل الرسائل إلى وجهتها. المثال على ذلك حاسوب خادم server يوفر خدمة وب، وخدمة بريد، وخدمة نقل ملفات. من الواضح أن أكثر من نوع من الرسائل يصل إلى هذا الحاسوب، بعضها موجّه إلى برنامج خادم الوب، وبعضها يتصل ببرنامج خادم البريد، وآخر يتبادل الملفات مع برنامج نقل الملفات. كل الرسائل ستصل إلى نفس الجهاز (لأن له عنواناً واحداً)، لكن هذا غير كافٍ لإتمام الاتصال وتبادل البيانات بشكل صحيح ودون خلط. الحقيقة أن هذا الوضع هو الشائع هذه الأيام، حيث يكثر أن يجلس الواحد منا إلى جهازه، ويفتح متصفح الوب (ويفتح أكثر من موقع، مما يعني أنه يتبادل البيانات مع أكثر من جهاز)، وفي نفس الوقت قد يقوم بتنزيل ملف أو أكثر، وتصفح البريد أيضاً. هنا تلاحظ أن هناك الكثير من الاتصالات المتزامنة في الحاسوب الذي نجلس عليه، وفي الحواسيب الأخرى البعيدة التي نتصل بها (وغالباً، يتصل بها معنا الكثيرون من كل مكان). لا بد أن تذهب كل رسالة إلى التطبيق الذي يخصها على نفس الجهاز. كيف؟
هنا يأتي دور بروتوكولات أخرى (من نفس مجموعة بروتوكولات TCP/IP)، في طبقة أخرى (طبقة النقل). هذه البروتوكولات (في الغالب الأعم إما TCP أو UDP)، هي المسؤولة عن إنشاء الاتصال ليس بين طرفين فحسب، وإنما بين تطبيقين محددين على كل طرف. هي مسؤولة عن تمييز الرسائل الواردة (والصادرة)، وإعطاء كل ذي حق حقه. الوسيلة التي يتم بها ذلك تشبه فكرة العنونة في بروتوكول IP، لكن العناوين هنا أبسط وأقصر، وتميز فقط كل الخدمات (التطبيقات) ضمن جهاز واحد بعنوان IP واحد. العنوان لكل تطبيق في هذه الطبقة يسمى رقم منفذ port number. ولذلك تسمع كثيراً عن المنافذ المفتوحة والمغلقة في كل جهاز، والتي تعبّر في النهاية عن الخدمات المتوفرة (أو التطبيقات الشغالة) في كل جهاز. بعض أرقام المنافذ المتعارف عليها لخدمات شهيرة، منفذ 80 لخدمة الوبHTTP ، ومنفذ 25 لخدمة البريد SMTP، ومنفذ 20 لخدمة نقل الملفات FTP. تستطيع أن تجد قائمة كاملة بأرقام المنافذ على هذا الرابط: www.iana.org/assignments/port-numbers.
من ذلك تعرف أن التطبيقات إن أرادت أن تتواصل فيما بينها، فلا بد لها من تحديد عنوان الطرف الآخر، وعنوان التطبيق النظير على ذلك الطرف. عندما يكتب المبرمجون التطبيقات فإنهم يحددون هذه الأشياء، أو يتركون تحديدها للمستخدم، كما هو الحال في المتصفح. ربما كان من المفيد أن أذكر هنا أن المبرمج أو المستخدم قد لا يضطر لتحديد عنوان IP مباشرة (من الصعب جداً تذكر هذه العناوين)، لأن هناك نظاماً خاصاً يحول بين عناوين IP وبين أسماء أكثر تعبيراً وأسهل للتذكر تسمى أسماء الدومين domain names. المهم هنا أن هناك ثلاثة عناصر تحدد كل اتصال: عنوان IP، ورقم منفذ، ونوع البروتوكول في طبقة النقل (TCP أو UDP)، حيث يمكن أن يستخدم نفس رقم المنفذ مع أي من هذين البروتوكولين. هذه الثلاث عناصر على طرف، مع ما يقابلها من ثلاث عناصر على الطرف الثاني، تحدد اتصالاً فريداً بين تطبيقين محددين.
لقد وصلنا بالفعل إلى بيت القصيد. باختصار: مجموع المكونات الثلاثة: عنوان IP ورقم منفذ وبروتوكول محدد هو اصطلاحاً مقبس. تخيّل أن الاتصال بين تطبيقين هو عبارة عن سلك طويل في كلا طرفيه قابس plug. وتخيّل أنك تريد أن يكون هذا الاتصال بين تطبيقين محددين على حاسوبين محددين. هذا يعني أنك تريد أن تدخل القابس على أحد أطراف سلك الاتصال في مكان، وتدخل القابس على الطرف الثاني في مكان آخر. القابس يحتاج إلى مقبس socket ليدخل فيه. المقبس هنا هو مجموع عنوان الحاسوب مع رقم المنفذ مع نوع البروتوكول (منطقياً).
من ناحية تقنية، في البرمجيات التي تمثل بروتوكولات TCP/IP، يرتبط المقبس بهيكل بيانات معين data structure، إلى جانب عدد صحيح فريد يعرّف المقبس. تحتوي هياكل البيانات هذه على معلومات تتعلق برقم المنفذ للتطبيق المصدر والتطبيق الوجهة، وعنوان بروتوكول الإنترنتIP للطرف المصدر والطرف الوجهة، وربما معلومات أخرى اعتماداً على نوع المقبس. يوضح الشكل رقم 3 مفهوم المقابس، وعلاقاتها مع التطبيقات وأرقام المنافذ.
مفهوم برمجة المقابس Socket Programming Concept
البرمجيات التي تمثّل مجموعة بروتوكولات TCP/IP (هذا يعني أنها تقوم بعمل البروتوكلات) هي أساساً جزء من نظام التشغيل، لكن مبرمجي التطبيقات لهم القدرة على كتابة تطبيقات الشبكات باستخدام المقابس من خلال مكتبات واجهات برمجية خاصة، مزودة من قبل نظام التشغيل، وتضم مجموعة من الدوال التي تضع الوظيفة المنفّذة من قبل نظام التشغيل في متناول المبرمجين. تسمى هذه المكتبات واجهات برمجة التطبيقاتAPI، والمكتبة الأصل في هذا الصدد هي واجهة مقابس بيركلي Berkeley sockets API، التي بنيت على أساسها العديد من الواجهات، بما في ذلك مقابس ويندوز (WinSock). تمت كتابة هذه المكتبات بلغة سي C، لذلك فإن مبرمجي هذه اللغة مدعومين بشكل افتراضي. ومع ذلك، تملك العديد من لغات البرمجة مكتبات غنية بدوال الشبكات، والتي توفر واجهات مماثلة لتلك التي كتبت بلغة سي، مثل لغة البرمجة جافا ، وبايثون ، وبيرل.
هذه التقنية (برمجة المقابس) أساسية جداً لكتابة تطبيقات الشبكات ومنها الإنترنت، بما في ذلك جميع أدوات الشبكات التي تستخدم إما كوسيلة مساعدة في إدارة الشبكة، أو لتنفيذ تقنيات كسر/اختبار أمن الشبكات.
هناك قدر كبير من الدروس التي تشرح برمجة المقابس على الإنترنت، لكن العمل الكلاسيكي من قبل ويليام ريتشارد ستيفنز، في كتابه برمجة شبكات يونكس، الجزء الأول(Unix Network Programming, volume1)، يبقى المرجعية المعيارية لمبرمجي المقابس منذ عام 1990 ميلادي، لا سيما بعد صدور طبعته الثالثة عام 2003 (بعد وفاته في الحقيقة).
هناك العديد من التفاصيل التي لا بد من الإلمام بها والتعامل معها عند البرمجة الفعلية مع المقابس، وبطبيعة الحال، فإن ذلك يقع خارج نطاق هذه المقالة التي تقدم للمفهوم فحسب، لكن من باب منح فكرة عن ماهية البرمجة مع المقابس عمليّاً، أختم المقال بقائمة تلخص الدوال التي توفرها مكتبة واجهة بيركلي (مترجمة من صفحة ويكيبيديا على الرابط التالي http://en.wikipedia.org/wiki/Berkeley_sockets):
socket()
تنشئ مقبساً جديداً من نوع محدد، وتعرّفه برقم صحيح، وتخصص له موارد النظام اللازمة.
bind()
تستخدم عادة في جانب الخادم، وتربط مقبساً بهيكل عنوان مقبس، مما يعني رقم منفذ محلياً وعنوان IP.
listen()
تستخدم في جانب الخادم، وتجعل مقبس TCP مربوطاً يدخل في حالة الاستماع.
connect()
تستخدم في جانب العميل، وتعيّن رقم منفذ محلياً وغير مستخدم من قبل لمقبس. في حالة مقبس من نوع TCP، تتسبب هذه الدالة في محاولة إنشاء اتصال TCP جديد.
accept()
تستخدم في جانب الخادم. تقوم بقبول المحاولات المستقبلة القادمة من عميل بعيد من أجل إنشاء اتصال TCP، وتنشئ مقبساً جديداً مرتبطاً بالعنوان القادم مع طلب الاتصال.
send() و recv(), أو write() و read(), أو sendto() و recvfrom()
تستخدم لإرسال واستقبال البيانات إلى/من مقبس بعيد.
close()
تتسبب في أن يحرر النظام الموارد التي خصّصت لمقبس. في حالة TCP، ينتهي الاتصال.
gethostbyname() و gethostbyaddr()
تحول بين أسماء وعناوين الأجهزة. فقط في IPv4.
select()
تستخدم من أجل فحص مجموعة معطاة من المقابس لتحديد تلك الجاهزة للقراءة أو الكتابة أو التي تعاني من أخطاء.
poll()
تستخدم لفحص حالة مقبس من مجموعة من المقابس. يمكن أن يتم فحص المجموعة لمعرفة ما إذا كان هناك مقبساً يمكن الكتابة إليه، أو القراءة منه، أو إذا حدث خطأ ما.
getsockopt()
تستخدم لاستعادة القيمة الحالية لخيار معين من خيارات المقبس المحدد.
setsockopt()
تستخدم لتعيين خيار معين من خيارات المقبس المحدد.
كيف تستخدم هذه الدوال، ومتى، ولماذا؟ هذه هي الأسئلة التي يتوجب عليك أن تبحث عن إجابتها، وتفهمها، إذا كنت حقاً تجرؤ أن تكون مبرمج شبكات!
هذه السلسلة:
- مفاهيم في الشبكات (3)