مقابس بيركلي

تُعدّ مقابس بيركلي واجهة برمجة تطبيقات (API) لمقابس نطاق الإنترنت ومقابس نطاق يونكس ، وتُستخدم للتواصل بين العمليات (IPC). ويتم تنفيذها عادةً كمكتبة من الوحدات النمطية القابلة للربط. وقد ظهرت لأول مرة مع نظام التشغيل يونكس 4.2BSD ، الذي صدر عام 1983.

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

تطورت مقابس بيركلي، مع تعديلات طفيفة، من معيار فعلي إلى مكون من مكونات مواصفات POSIX . يُعد مصطلح مقابس POSIX مرادفًا لمقابس بيركلي ، ولكنها تُعرف أيضًا باسم مقابس BSD ، نسبةً إلى أول تطبيق لها في توزيعة برمجيات بيركلي .

التاريخ والتطبيقات

نشأت مقابس بيركلي مع نظام التشغيل يونكس 4.2 BSD ، الذي صدر عام 1983، كواجهة برمجة. إلا أنه لم يتسنَّ لجامعة كاليفورنيا، بيركلي، إصدار نسخ من نظام التشغيل ومكتبة الشبكات خالية من قيود الترخيص الخاصة بنظام يونكس المملوك لشركة AT&T إلا في عام 1989 .

تُطبّق جميع أنظمة التشغيل الحديثة نسخةً من واجهة Berkeley socket. وقد أصبحت هذه الواجهة المعيارية للتطبيقات التي تعمل على الإنترنت . حتى تطبيق Winsock لنظام التشغيل Windows، الذي أنشأه مطورون مستقلون، يتبع هذا المعيار بدقة.

تمت كتابة واجهة برمجة تطبيقات مقابس BSD بلغة البرمجة C. توفر معظم لغات البرمجة الأخرى واجهات مماثلة، وعادة ما تُكتب كمكتبة تغليف تعتمد على واجهة برمجة تطبيقات C. [ 1 ]

مقابس BSD و POSIX

مع تطور واجهة برمجة تطبيقات Berkeley socket API وصولاً إلى واجهة برمجة تطبيقات POSIX socket API، [ 2 ] تم إيقاف بعض الوظائف أو إزالتها واستبدالها بوظائف أخرى. كما صُممت واجهة برمجة تطبيقات POSIX API لتكون قابلة لإعادة الاستخدام وتدعم بروتوكول IPv6.

فعلبي إس ديبوسيكس
التحويل من عنوان نصي إلى عنوان مضغوطinet_atoninet_pton
التحويل من عنوان مضغوط إلى عنوان نصيinet_ntoainet_ntop
البحث الأمامي عن اسم المضيف/الخدمةgethostbyname، gethostbyaddr، getservbyname، getservbyportgetaddrinfo
البحث العكسي عن اسم المضيف/الخدمةgethostbyaddr, getservbyportgetnameinfo

البدائل

توفر واجهة برمجة تطبيقات طبقة النقل (TLI) القائمة على STREAMS بديلاً لواجهة برمجة تطبيقات المقابس. كما توفر العديد من الأنظمة التي توفر واجهة برمجة تطبيقات طبقة النقل (TLI) واجهة برمجة تطبيقات مقابس بيركلي.

غالباً ما تُتيح الأنظمة غير التابعة لنظام يونكس واجهة برمجة تطبيقات Berkeley socket مع طبقة ترجمة إلى واجهة برمجة تطبيقات الشبكات الأصلية. يستخدم كل من Plan 9 [ 3 ] و Genode [ 4 ] واجهات برمجة تطبيقات نظام الملفات مع ملفات التحكم بدلاً من مُعرّفات الملفات.

ملفات الرأس

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

ملفوصف
<arpa/inet.h>وظائف لمعالجة عناوين IP الرقمية.
<netinet/in.h>عائلات عناوين AF_INET و AF_INET6 وعائلات البروتوكولات المقابلة لها، PF_INET و PF_INET6 . وتشمل هذه عناوين IP القياسية وأرقام منافذ TCP و UDP.
<netdb.h>وظائف لترجمة أسماء البروتوكولات وأسماء المضيفين إلى عناوين رقمية. يبحث في البيانات المحلية وكذلك خدمات الأسماء.
<sys/socket.h>وظائف المقابس الأساسية وهياكل البيانات.
<sys/un.h>عائلة عناوين PF_UNIX و PF_LOCAL . تُستخدم للاتصال المحلي بين البرامج التي تعمل على نفس الحاسوب.

وظائف واجهة برمجة تطبيقات Socket

مخطط انسيابي لمعاملة العميل والخادم باستخدام المقابس مع بروتوكول التحكم في الإرسال (TCP).

توفر واجهة برمجة تطبيقات Berkeley socket عادةً الوظائف التالية:

  • socket()يقوم بإنشاء مقبس جديد من نوع معين، يتم تحديده بواسطة رقم صحيح، ويخصص موارد النظام له.
  • bind()يستخدم عادة على جانب الخادم، ويربط مقبسًا ببنية عنوان المقبس، أي عنوان IP محلي محدد ورقم منفذ.
  • listen()يتم استخدامه على جانب الخادم، ويتسبب في دخول مقبس TCP المرتبط في حالة الاستماع.
  • connect()يُستخدم هذا الأمر من جانب العميل، ويُخصص رقم منفذ محلي متاح لمأخذ التوصيل. في حالة مأخذ توصيل TCP، فإنه يُؤدي إلى محاولة إنشاء اتصال TCP جديد.
  • accept()يتم استخدامه على جانب الخادم. يقبل محاولة واردة لإنشاء اتصال TCP جديد من العميل البعيد، وينشئ مقبسًا جديدًا مرتبطًا بزوج عناوين المقبس لهذا الاتصال.
  • send()تُستخدم هذه الدوال لإرسال واستقبال البيانات. recv()ويمكن أيضًا استخدام الدوال القياسية .sendto()recvfrom()write()read()
  • close()يؤدي ذلك إلى تحرير النظام للموارد المخصصة للمقبس. في حالة بروتوكول TCP، يتم إنهاء الاتصال.
  • gethostbyname()وتُستخدم gethostbyaddr()لحل أسماء المضيفين وعناوينهم. IPv4 فقط.
  • getaddrinfo()وتُستخدم freeaddrinfo()لحل أسماء المضيفين وعناوينهم. IPv4، IPv6.
  • select()تُستخدم هذه الخاصية للتعليق، في انتظار أن يكون واحد أو أكثر من قائمة المقابس المقدمة جاهزًا للقراءة، أو جاهزًا للكتابة، أو أن يكون به أخطاء.
  • poll()تُستخدم هذه الأداة للتحقق من حالة مقبس ضمن مجموعة من المقابس. ويمكن اختبار المجموعة لمعرفة ما إذا كان بالإمكان الكتابة إلى أي مقبس أو القراءة منه، أو ما إذا حدث خطأ.
  • getsockopt()تُستخدم لاسترجاع القيمة الحالية لخيار مقبس معين للمقبس المحدد.
  • setsockopt()يُستخدم لتعيين خيار مقبس معين للمقبس المحدد.

المقبس

تُنشئ هذه الدالة socket()نقطة نهاية للاتصال وتُعيد مُعرّف ملف للمقبس. وتستخدم ثلاثة وسائط:

  • domain، والذي يحدد عائلة البروتوكول الخاصة بالمقبس المُنشأ. على سبيل المثال:
    • PF_INET لبروتوكول الشبكة IPv4 (IPv4 فقط)
    • PF_INET6 لبروتوكول IPv6 (وفي بعض الحالات، متوافق مع الإصدارات السابقة من IPv4)
    • PF_UNIX للمقبس المحلي (باستخدام عقدة نظام ملفات خاصة)
  • type، أحد ما يلي:
    • SOCK_STREAM (خدمة موثوقة موجهة نحو التدفق أو مقابس التدفق )
    • SOCK_DGRAM (خدمة مخطط البيانات أو مقابس مخطط البيانات )
    • SOCK_SEQPACKET (خدمة حزم متسلسلة موثوقة)
    • SOCK_RAW (البروتوكولات الخام فوق طبقة الشبكة)
  • protocolتحديد بروتوكول النقل الفعلي المراد استخدامه. من أكثر البروتوكولات شيوعًا: IPPROTO_TCP و IPPROTO_SCTP و IPPROTO_UDP و IPPROTO_DCCP . تُحدد هذه البروتوكولات في ملف . يمكن استخدام القيمة 0 لاختيار بروتوكول افتراضي من النطاق والنوع المحددين.<netinet/in.h>

تُرجع الدالة القيمة -1 في حالة حدوث خطأ. وإلا، فإنها تُرجع عددًا صحيحًا يُمثل المُعرّف المُعيّن حديثًا.

ربط

bind()يربط هذا الأمر المقبس بعنوان. عند إنشاء مقبس باستخدام هذه الدالة socket()، يُمنح فقط عائلة بروتوكول، ولكن لا يُخصص له عنوان. يجب إجراء هذا الربط قبل أن يتمكن المقبس من قبول الاتصالات من مضيفين آخرين. تحتوي الدالة على ثلاثة وسائط:

  • sockfd، واصف يمثل المقبس
  • my_addr، مؤشر إلى sockaddrبنية تمثل العنوان المراد الربط به.
  • addrlen، حقل من النوع socklen_tيحدد حجم البنية sockaddr.

bind()تُرجع القيمة 0 في حالة النجاح و -1 في حالة حدوث خطأ.

يستمع

بعد ربط المقبس بعنوان، listen()يتم تجهيزه للاتصالات الواردة. مع ذلك، هذا ضروري فقط لأنماط البيانات الموجهة بالتدفق (الموجهة بالاتصال)، أي لأنواع المقابس ( SOCK_STREAM, SOCK_SEQPACKET). listen()يتطلب وسيطين:

  • sockfd، واصف مقبس صالح.
  • backlog، وهو عدد صحيح يمثل عدد الاتصالات المعلقة التي يمكن وضعها في قائمة الانتظار في أي وقت. وعادةً ما يضع نظام التشغيل حدًا أقصى لهذه القيمة.

بمجرد قبول الاتصال، يتم إخراجه من قائمة الانتظار. في حالة النجاح، يتم إرجاع القيمة 0. في حالة حدوث خطأ، يتم إرجاع القيمة -1.

يقبل

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

  • sockfd، وهو وصف لمقبس الاستماع الذي تم وضع الاتصال في قائمة الانتظار الخاصة به.
  • cliaddr، مؤشر إلى بنية sockaddr لتلقي معلومات عنوان العميل.
  • addrlen، مؤشر إلى socklen_tموقع يحدد حجم بنية عنوان العميل التي تم تمريرها إلى accept(). عند accept()الإرجاع، يحتوي هذا الموقع على حجم البنية (بالبايت).

accept()يُعيد هذا الأمر مُعرّف المقبس الجديد للاتصال المقبول، أو القيمة -1 في حال حدوث خطأ. وسيتمّ التواصل مع المضيف البعيد عبر هذا المقبس الجديد.

لا تتطلب مقابس البيانات معالجة من قبل accept()المتلقي لأنه يمكنه الاستجابة فورًا للطلب باستخدام مقبس الاستماع.

يتصل

connect()يقوم بإنشاء رابط اتصال مباشر مع مضيف بعيد محدد يتم التعرف عليه من خلال عنوانه عبر مقبس يتم التعرف عليه من خلال واصف الملف الخاص به.

عند استخدام بروتوكول موجه للاتصال ، يُنشئ هذا اتصالاً. بعض أنواع البروتوكولات غير موجهة للاتصال، وأبرزها بروتوكول بيانات المستخدم (User Datagram Protocol ). عند استخدامه مع البروتوكولات غير الموجهة للاتصال، connectيُحدد عنوان الجهاز البعيد لإرسال واستقبال البيانات، مما يسمح باستخدام وظائف مثل ` sendconnect` و`connect` recv. في هذه الحالات، تمنع وظيفة الاتصال (connect) استقبال حزم البيانات من مصادر أخرى.

connect()تُعيد الدالة عددًا صحيحًا يُمثل رمز الخطأ: 0 يُشير إلى النجاح، بينما -1 يُشير إلى الخطأ. تاريخيًا، في الأنظمة المُشتقة من BSD، تكون حالة مُعرّف المقبس غير مُحددة إذا فشلت عملية الاستدعاء (كما هو مُحدد في مواصفات يونكس المُوحدة )، وبالتالي، يجب على التطبيقات القابلة للنقل إغلاق مُعرّف المقبس فورًا والحصول على مُعرّف جديد باستخدام الدالة ، في حالة فشل عملية الاستدعاء. [ 5 ]connectsocket()connect()

gethostbyname و gethostbyaddr

gethostbyname()تُستخدم الدالتان gethostbyaddr()لحل أسماء المضيفين وعناوينهم في نظام أسماء النطاقات أو آليات حل أسماء المضيفين الأخرى (مثل /etc/hostsالبحث). تُعيدان مؤشرًا إلى كائن من النوع struct hostent، والذي يصف مضيف بروتوكول الإنترنت . تستخدم الدالتان الوسائط التالية:

  • nameيحدد اسم المضيف.
  • addrيحدد مؤشرًا إلى كائن struct in_addrيحتوي على عنوان المضيف.
  • lenيحدد طول الملف بالبايت addr.
  • typeيحدد نوع عائلة العنوان (على سبيل المثال، AF_INET) لعنوان المضيف.

تُعيد الدوال قيمة NULLفي حالة حدوث خطأ، وفي هذه الحالة قد يتم التحقق من العدد الصحيح الخارجي لمعرفة ما إذا كان هذا عطلًا مؤقتًا أو مضيفًا غير صالح أو غير معروف. وإلا، فسيتم إرجاع قيمة صالحة.h_errnostruct hostent*

لا تُعدّ هذه الدوال جزءًا أساسيًا من واجهة برمجة تطبيقات مقابس BSD، ولكنها تُستخدم غالبًا بالتزامن مع دوال واجهة برمجة التطبيقات للبحث عن مضيف. وتُعتبر هذه الدوال الآن واجهات قديمة للاستعلام عن نظام أسماء النطاقات. وقد تم تعريف دوال جديدة مستقلة تمامًا عن البروتوكول (تدعم IPv6). هذه الدوال الجديدة هي getaddrinfo() و getnameinfo() ، وتعتمد على بنية بيانات جديدة . [ 6 ][[addrinfo]]

ظهرت هاتان الدالتان بالتزامن مع واجهة برمجة تطبيقات مقابس BSD في الإصدار 4.2BSD (1983)، [ 7 ] وهو نفس العام الذي أُنشئ فيه نظام أسماء النطاقات (DNS). لم تكن الإصدارات المبكرة تستعلم عن DNS، بل كانت تُجري عمليات بحث في ملف /etc/hosts فقط. أضاف الإصدار 4.3BSD (1984) نظام DNS بطريقة بدائية. يعتمد التنفيذ الحالي، باستخدام مُبدِّل خدمة الأسماء (NSS )، على نظام Solaris، ولاحقًا على NetBSD 1.4 (1999). [ 8 ] تم تعريف NSS في البداية لنظام NIS+ ، وهو يجعل DNS أحد الخيارات العديدة المتاحة للبحث بواسطة هاتين الدالتين، ويمكن تعطيل استخدامه حتى اليوم. [ 9 ]

البروتوكول ومخاطبة العائلات

تُعد واجهة برمجة تطبيقات Berkeley socket واجهة عامة للشبكات والتواصل بين العمليات وتدعم استخدام بروتوكولات الشبكة المختلفة وهياكل العناوين.

فيما يلي قائمة بعينة من عائلات البروتوكولات (المسبوقة بالمعرف الرمزي القياسي) المحددة في تطبيق حديث لنظامي التشغيل لينكس أو بي إس دي :

المعرفالوظيفة أو الاستخدام
PF_APPLETALKأبل توك
PF_ATMPVCالدوائر الافتراضية الدائمة في وضع النقل غير المتزامن
PF_ATMSVCالدوائر الافتراضية المحولة في وضع النقل غير المتزامن
PF_AX25راديو الهواة AX.25
PF_CANشبكة منطقة التحكم
PF_BLUETOOTHمنافذ بلوتوث
جسر PFجسر متعدد البروتوكولات
PF_DECnetمخصص لمشروع DECnet
PF_ECONETأكورن إيكونيت
PF_INETبروتوكول الإنترنت الإصدار 4
PF_INET6بروتوكول الإنترنت الإصدار 6
PF_IPXتبادل حزم الشبكة الداخلية من نوفيل
PF_IRDAمقابس الأشعة تحت الحمراء
PF_KEYواجهة برمجة تطبيقات إدارة المفاتيح PF_KEY
PF_LOCAL ، PF_UNIX ، PF_FILEمحلي إلى المضيف (الأنابيب ونطاق الملف)
PF_NETROMNET/ROM راديو الهواة (متعلق بـ AX.25) [ 10 ]
PF_NETBEUIمخصص لمشروع 802.2LLC
PF_SECURITYرد اتصال أمني زائف AF
PF_NETLINK ، PF_ROUTEواجهة برمجة تطبيقات التوجيه
PF_PACKETمقابس التقاط الحزم
PF_PPPOXبروتوكول PPP عبر منافذ X
PF_SNAمشروع هندسة شبكات أنظمة لينكس (SNA)
PF_WANPIPEمقابس واجهة برمجة تطبيقات سانغوما وانبايب

يتم إنشاء مقبس للاتصالات باستخدام socket()الدالة، عن طريق تحديد عائلة البروتوكول المطلوبة ( PF_ -identifier) ​​كوسيط.

ميّز التصميم الأصلي لواجهة المقبس بين أنواع البروتوكولات (العائلات) وأنواع العناوين المحددة التي يمكن لكل منها استخدامها. كان من المتوقع أن تحتوي عائلة البروتوكولات على عدة أنواع من العناوين. تم تعريف أنواع العناوين بواسطة ثوابت رمزية إضافية، باستخدام البادئة AF بدلاً من PF . تُستخدم مُعرّفات AF لجميع هياكل البيانات التي تتعامل تحديدًا مع نوع العنوان وليس عائلة البروتوكول. مع ذلك، لم يجد مفهوم فصل البروتوكول عن نوع العنوان دعمًا في التطبيقات، وتم تعريف ثوابت AF بواسطة مُعرّف البروتوكول المقابل، مما جعل التمييز بين ثوابت AF و PF مجرد جدل تقني لا أهمية عملية له. في الواقع، يوجد الكثير من اللبس حول الاستخدام الصحيح لكلا الشكلين. [ 11 ]

لا تحدد مواصفات POSIX.1—2008 أي ثوابت PF ، ولكن فقط ثوابت AF [ 12 ]

المقابس الخام

توفر المقابس الخام واجهة بسيطة تتجاوز معالجة حزمة بروتوكولات TCP/IP الخاصة بالمضيف. وهي تسمح بتنفيذ بروتوكولات الشبكات في مساحة المستخدم وتساعد في تصحيح أخطاء حزمة البروتوكولات. [ 13 ] تُستخدم المقابس الخام من قِبل بعض الخدمات، مثل ICMP ، التي تعمل على مستوى طبقة الإنترنت في نموذج TCP/IP.

وضع الحظر ووضع عدم الحظر

يمكن لمقابس بيركلي أن تعمل في أحد الوضعين: وضع الحجب أو وضع عدم الحجب.

لا يُعيد المقبس المُعطِّل التحكم إلا بعد إرسال (أو استقبال) بعض أو كل البيانات المُحدَّدة للعملية. ومن الطبيعي ألا يُرسل المقبس المُعطِّل جميع البيانات. يجب على التطبيق التحقق من القيمة المُعادة لتحديد عدد البايتات المُرسَلة أو المُستقبَلة، ويجب عليه إعادة إرسال أي بيانات لم تتم معالجتها بعد. [ 14 ] عند استخدام المقابس المُعطِّلة، ينبغي إيلاء اهتمام خاص للدالة accept()، إذ قد تستمر في التعطيل بعد الإشارة إلى إمكانية القراءة إذا انقطع اتصال العميل أثناء مرحلة الاتصال.

تقوم المقابس غير المحظورة بإعادة البيانات الموجودة في مخزن الاستقبال المؤقت ثم تتابع عملها فورًا. وإذا لم تُكتب البرامج التي تستخدم المقابس غير المحظورة بشكل صحيح، فإنها تكون عرضة بشكل خاص لحالات التزامن غير المتوقعة نتيجة لاختلاف سرعة اتصال الشبكة.

يتم عادةً ضبط المقبس على وضع الحظر أو وضع عدم الحظر باستخدام الوظيفتين fcntl و ioctl .

مقابس إنهاء التوصيل

لا يُحرر نظام التشغيل الموارد المخصصة للمقبس إلا بعد إغلاقه. وهذا أمر بالغ الأهمية، خاصةً إذا فشلت عملية الاتصال ، حيث ستُعاد المحاولة.

عندما يُغلق تطبيقٌ ما منفذًا، لا يُدمَّر سوى واجهة هذا المنفذ. تقع مسؤولية تدمير المنفذ داخليًا على عاتق نواة النظام. في بعض الأحيان، قد يدخل المنفذ في حالة انتظار مؤقتة (TIME_WAIT) على جانب الخادم لمدة تصل إلى 4 دقائق. [ 15 ]

في أنظمة SVR4 ، قد يؤدي استخدام SO_LINGER close()إلى فقدان البيانات. وقد يكون استخدام shutdown()SO_LINGER ضروريًا في هذه الأنظمة لضمان تسليم جميع البيانات. [ 16 ]

مثال على نظام العميل والخادم باستخدام بروتوكول TCP

بروتوكول التحكم بالنقل (TCP) هو بروتوكول موجه للاتصال يوفر مجموعة متنوعة من ميزات تصحيح الأخطاء وتحسين الأداء لنقل تدفقات البايت. يقوم البرنامج بإنشاء مقبس TCP عن طريق استدعاء الدالة socket()مع معلمات عائلة البروتوكول ( PF INET ، PF_INET6 )، ووضع المقبس لمقابس التدفق ( SOCK_STREAM )، ومعرف بروتوكول IP لـ TCP ( IPPROTO_TCP ).

الخادم

يتضمن إنشاء خادم TCP الخطوات الأساسية التالية:

  • إنشاء مقبس TCP عن طريق استدعاء socket().
  • ربط المقبس بمنفذ الاستماع bind()بعد تحديد رقم المنفذ.
  • إعداد المقبس للاستماع إلى الاتصالات (جعله مقبس استماع)، مع استدعاء لـ listen().
  • قبول الاتصالات الواردة ( accept()). يؤدي هذا إلى إيقاف العملية مؤقتًا حتى يتم استقبال اتصال وارد، ويعيد وصفًا للمقبس الخاص بالاتصال المقبول. يبقى الوصف الأولي وصفًا للاستماع، ويمكن accept()استدعاؤه مرة أخرى في أي وقت باستخدام هذا المقبس، حتى يتم إغلاقه.
  • التواصل مع المضيف البعيد باستخدام وظائف واجهة برمجة التطبيقات (API) send()و recv()، بالإضافة إلى الوظائف العامة write()و read().
  • إغلاق كل مقبس تم فتحه بعد الاستخدام باستخدام الوظيفةclose()

يقوم البرنامج التالي بإنشاء خادم TCP يستمع على المنفذ رقم 1100:

#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h>#include <arpa/inet.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> int main ( void ) { int sockfd = socket ( PF_INET , SOCK_STREAM , IPPROTO_TCP ); if ( sockfd == -1 ) { fprintf ( stderr , "فشل إنشاء المقبس! \n " ); return EXIT_FAILURE ; } struct sockaddr_in sa = { . sin_family = AF_INET , . sin_port = htons ( 1100 ), . sin_addr . s_addr = htonl ( INADDR_ANY ) }; إذا فشلت عملية الربط ( bind ( sockfd , ( struct sockaddr * ) & sa , sizeof ( sa )) ) في `-1` ، فسيتم طباعة رسالة " فشل ربط المقبس! \n " على الخطأ القياسي ، ثم إغلاق المقبس ( sockfd وإرجاع رمز الخطأ EXIT_FAILURE . وبالمثل ، إذا فشلت عملية الاستماع ( listen ( sockfd , 10 )) في `-1` ، فسيتم طباعة رسالة " فشل الاستماع على المقبس! \n " على الخطأ القياسي ، ثم إغلاق المقبس ( sockfd وإرجاع رمز الخطأ EXIT_FAILURE . ثم يتم التحقق من قبول الاتصال (connfd = accept ( sockfd , NULL , NULL ) وإذا فشلت هذه العملية ، فسيتم طباعة رسالة "فشل قبول الاتصال! \n " على الخطأ القياسي ، ثم إغلاق المقبس ( sockfd وإرجاع رمز الخطأ EXIT_FAILURE .EXIT_FAILURE ; } // تنفيذ عمليات القراءة والكتابة... // read(connfd, buff, size) if ( shutdown ( connfd , SHUT_RDWR ) == -1 ) { fprintf ( stderr , "فشل إغلاق الاتصال! \n " ); close ( connfd ); close ( sockfd ); return EXIT_FAILURE ; } close ( connfd ); }أغلق ( sockfd أرجع EXIT_SUCCESS ؛ }

عميل

تتضمن برمجة تطبيق عميل TCP الخطوات التالية:

  • إنشاء مقبس TCP.
  • الاتصال بالخادم ( connect())، عن طريق تمرير sockaddr_inبنية مع sin_familyتعيين AF_INET ، وتعيين المنفذ الذي تستمع إليه نقطة النهاية (بترتيب بايت الشبكة)، وتعيين عنوان IP الخاص بالخادم المستمع (أيضًا بترتيب بايت الشبكة).sin_portsin_addr
  • التواصل مع المضيف البعيد باستخدام وظائف واجهة برمجة التطبيقات (API) send()و recv()، بالإضافة إلى الوظائف العامة write()و read().
  • إغلاق كل منفذ تم فتحه بعد الاستخدام باستخدام الوظيفة close().
#include <stdio.h> #include <stdlib.h> #include <string.h>#include <arpa/inet.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> int main ( void ) { int sockfd = socket ( PF_INET , SOCK_STREAM , IPPROTO_TCP ); if ( sockfd == -1 ) { fprintf ( stderr , "فشل إنشاء المقبس! \n " ); return EXIT_FAILURE ; } struct sockaddr_in sa = { . sin_family = AF_INET , . sin_port = htons ( 1100 ), }; int res = inet_pton ( AF_INET , "192.168.1.3" , & sa . sin_addr );إذا فشلت عملية الاتصال ( connect ( sockfd , ( struct sockaddr * ) & sa , sizeof ( sa )) ) في الوصول إلى عنوان IP الخاص بالمنفذ (sa) ، فسيتم طباعة رسالة "فشل الاتصال!" في الخطأ القياسي ( stderr )، ثم إغلاق المنفذ ( sockfd ) ، وإرجاع قيمة EXIT_FAILURE . وإلا ، فسيتم تنفيذ عمليات القراءة والكتابة، ثم إغلاق المنفذ ( sockfd وإرجاع قيمة EXIT_SUCCESS .

مثال على نظام العميل والخادم باستخدام بروتوكول UDP

بروتوكول بيانات المستخدم (UDP) هو بروتوكول غير متصل ، ولا يضمن وصول حزم البيانات. قد تصل حزم UDP بترتيب غير صحيح، أو عدة مرات، أو قد لا تصل على الإطلاق. وبسبب هذا التصميم البسيط، يتميز UDP بانخفاض الحمل الزائد مقارنةً ببروتوكول TCP. كونه غير متصل يعني عدم وجود مفهوم للتدفق أو اتصال دائم بين جهازين. تُعرف هذه البيانات باسم حزم البيانات ( مآخذ حزم البيانات ).

مساحة عناوين UDP، وهي مساحة أرقام منافذ UDP (في مصطلحات ISO، TSAPs )، منفصلة تمامًا عن مساحة منافذ TCP.

الخادم

يمكن للتطبيق إعداد خادم UDP على رقم المنفذ 7654كما يلي. يحتوي البرنامج على حلقة لا نهائية تستقبل حزم بيانات UDP باستخدام الدالة recvfrom().

#include <stdbool.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h>#include <netinet/in.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h>int main ( void ) { struct sockaddr_in sa = { . sin_family = AF_INET , . sin_addr . s_addr = htonl ( INADDR_ANY ), . sin_port = htons ( 7654 ) }; char buffer [ 1024 ]; ssize_t recsize ; socklen_t fromlen = sizeof ( sa );int sock = socket ( PF_INET , SOCK_DGRAM , IPPROTO_UDP ); if ( bind ( sock , ( struct sockaddr * ) & sa , sizeof ( sa )) == -1 ) { fprintf ( stderr , "فشل ربط المقبس! \n " ); close ( sock ); return EXIT_FAILURE ; }بينما ( صحيح ) { recsize = recvfrom ( sock , ( void * ) buffer , sizeofbuffer , 0 , ( struct sockaddr * ) & sa , & fromlen ); إذا ( recsize < 0 ) { fprintf ( stderr , "%s \n " , strerror ( errno ) ); return EXIT_FAILURE ; } printf ( "recsize: %d \n " , ( int ) recsize ); sleep ( 1 ); printf ( "datagram: %.*s \n " , ( int ) recsize , buffer ); } }

عميل

فيما يلي برنامج عميل لإرسال حزمة UDP تحتوي على السلسلة "Hello, world!" إلى العنوان الموجود 127.0.0.1على رقم المنفذ 7654.

#include <errno.h> #include <stdlib.h> #include <stdio.h> #include <string.h>#include <arpa/inet.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h>int main ( void ) { struct sockaddr_in sa = { // العنوان هو IPv4 . sin_family = AF_INET , // عناوين IPv4 هي uint32_t، يتم تحويل تمثيل السلسلة للثمانيات إلى القيمة المناسبة . sin_addr . s_addr = inet_addr ( "127.0.0.1" ), // المقابس هي أعداد صحيحة غير موقعة، htons(x) تضمن أن x بترتيب بايتات الشبكة، يتم تعيين المنفذ إلى 7654 . sin_port = htons ( 7654 ) }; char buffer [ 200 ]; strcpy ( buffer , "Hello, world!" );// إنشاء مقبس بيانات عبر الإنترنت باستخدام بروتوكول UDP int sock = socket ( PF_INET , SOCK_DGRAM , IPPROTO_UDP ); if ( sock == -1 ) { // إذا فشل تهيئة المقبس، يتم الخروج fprintf ( stderr , "فشل إنشاء المقبس! \n " ); return EXIT_FAILURE ; }// إرسال رسالة باستخدام sendto() int bytes_sent = sendto ( sock , buffer , strlen ( buffer ), 0 , ( struct sockaddr * ) & sa , sizeof ( sa ));إذا كانت قيمة ` bytes_sent` أقل من الصفر ، فسيتم طباعة رسالة خطأ إلى الخطأ القياسي ( stderr) تُفيد بوجود خطأ في إرسال الحزمة ، ثم يتم إرجاع قيمة ` EXIT_FAILURE` . بعد ذلك، يتم إغلاق المقبس ( sock ) وإرجاع القيمة صفر .

في هذا الكود، bufferيمثل مؤشرًا إلى البيانات المراد إرسالها، buffer_lengthويحدد حجم البيانات.

مراجع

  1. على سبيل المثال، في فئة Socket في لغة البرمجة Ruby
  2. "— مواصفات POSIX.1-2008" . Opengroup.org . تم الاطلاع عليه بتاريخ 26-07-2012 .
  3. "تنظيم الشبكات في الخطة 9" .
  4. "مجموعة بروتوكولات TCP/IP لنظام Linux كملحق VFS" .
  5. ستيفنز وراغو 2013 ، ص 607.
  6. POSIX.1-2004
  7. دليل وظائف مكتبة FreeBSDgethostbyname(3)  
  8. كونيل، أريادني (27 مارس 2022). "مأساة gethostbyname" . ariadne.space .
  9. دليل تنسيقات ملفات FreeBSDnsswitch.conf(5)  
  10. "netrom(4) — ax25-tools — Debian experimental — Debian Manpages" .
  11. برمجة شبكات يونكس المجلد 1، الطبعة الثالثة: واجهة برمجة تطبيقات الشبكات للمقابس، دبليو. ريتشارد ستيفنز، بيل فينير، أندرو إم. رودوف، أديسون ويسلي، 2003.
  12. "المواصفات الأساسية لمجموعة Open Group، الإصدار 7" . Pubs.opengroup.org . تم الاطلاع عليه بتاريخ 26-07-2012 .
  13. "مآخذ TCP/IP الخام - تطبيقات Win32" . 19 يناير 2022.
  14. "دليل بيج لبرمجة الشبكات" . Beej.us. 2007-05-05 . تم الاطلاع عليه بتاريخ 2012-07-26 .
  15. "إنهاء المقابس" . Softlab.ntua.gr . تم الاطلاع عليه بتاريخ 26-07-2012 .
  16. "ntua.gr - برمجة مقابس يونكس بلغة C - الأسئلة الشائعة: أسئلة تتعلق بكل من العملاء والخوادم (TCP/SOCK_STREAM)" . Softlab.ntua.gr . تاريخ الاسترجاع: 26-07-2012 .

يُعرّف معيار POSIX، المعروف باسم :

  • IEEE Std. 1003.1-2001 معيار تكنولوجيا المعلومات - واجهة نظام التشغيل المحمول (POSIX).
  • المعيار الفني لمجموعة Open Group: المواصفات الأساسية، الإصدار 6، ديسمبر 2001.
  • ISO/IEC 9945:2002

تتوفر معلومات حول هذا المعيار والعمل الجاري عليه على موقع أوستن الإلكتروني .

تم توثيق امتدادات IPv6 لواجهة برمجة تطبيقات المقبس الأساسية في RFC 3493 و RFC 3542.