مقابس بيركلي
مقبس Berkeley ( BSD ) هو واجهة برمجة تطبيقات (API) لمقابس نطاق الإنترنت ومقابس نطاق يونكس ، تُستخدم للاتصال بين العمليات (IPC). يتم تنفيذه عادةً كمكتبة من الوحدات النمطية القابلة للربط. نشأ مع نظام التشغيل Unix 4.2BSD ، الذي تم إصداره في عام 1983.
المقبس هو تمثيل تجريدي ( مقبض ) لنقطة النهاية المحلية لمسار اتصال الشبكة. تمثله واجهة برمجة تطبيقات Berkeley sockets كوصف ملف ( مقبض ملف ) في فلسفة Unix التي توفر واجهة مشتركة للإدخال والإخراج لتدفقات البيانات.
تطورت مآخذ بيركلي مع القليل من التعديل من معيار فعلي إلى أحد مكونات مواصفات POSIX . مصطلح مآخذ POSIX مرادف بشكل أساسي لمآخذ بيركلي ، ولكنها تُعرف أيضًا بمآخذ BSD ، مما يشير إلى التنفيذ الأول في توزيع برمجيات بيركلي .
التاريخ والتنفيذ
نشأت مآخذ بيركلي مع نظام التشغيل 4.2BSD Unix ، الذي صدر عام 1983، كواجهة برمجة. ومع ذلك، لم تتمكن جامعة كاليفورنيا في بيركلي من إصدار إصدارات من نظام التشغيل ومكتبة الشبكات خالية من قيود الترخيص الخاصة بنظام التشغيل Unix الخاص بشركة AT&T إلا في عام 1989.
تطبق جميع أنظمة التشغيل الحديثة نسخة من واجهة Berkeley socket. وقد أصبحت الواجهة القياسية للتطبيقات التي تعمل على الإنترنت . حتى تطبيق Winsock لنظام التشغيل MS Windows، الذي أنشأه مطورون مستقلون، يتبع المعيار عن كثب.
تمت كتابة واجهة برمجة تطبيقات BSD sockets بلغة البرمجة C. توفر معظم لغات البرمجة الأخرى واجهات مماثلة، مكتوبة عادةً كمكتبة غلاف تعتمد على واجهة برمجة التطبيقات C. [1]
مآخذ BSD وPOSIX
مع تطور واجهة برمجة تطبيقات Berkeley socket وظهور واجهة برمجة تطبيقات POSIX socket في النهاية، [2] تم إهمال بعض الوظائف أو إزالتها واستبدالها بوظائف أخرى. كما تم تصميم واجهة برمجة تطبيقات POSIX لتكون قابلة لإعادة الدخول وتدعم IPv6.
| فعل | بي إس دي | بوسيكس |
|---|---|---|
| التحويل من عنوان نصي إلى عنوان معبأ | إنيت_آتون | إنت_بتون |
| التحويل من عنوان معبأ إلى عنوان نصي | إنت_نتوا | إنت_إنت توب |
| البحث الأمامي عن اسم المضيف/الخدمة | الحصول على اسم المضيف، الحصول على عنوان المضيف، الحصول على اسم الخادم، الحصول على منفذ الخادم | الحصول على معلومات عن الشراب |
| البحث العكسي عن اسم المضيف/الخدمة | الحصول على عنوان الاستضافة، الحصول على منفذ الخدمة | الحصول على اسم المعلومات |
البدائل
توفر واجهة برمجة التطبيقات (TLI) المستندة إلى STREAMS بديلاً لواجهة برمجة التطبيقات socket. توفر العديد من الأنظمة التي توفر واجهة برمجة التطبيقات TLI أيضًا واجهة برمجة تطبيقات Berkeley socket.
غالبًا ما تعرض الأنظمة غير التابعة لـ Unix واجهة برمجة تطبيقات Berkeley socket مع طبقة الترجمة إلى واجهة برمجة تطبيقات الشبكات الأصلية. تستخدم Plan 9 [3] و Genode [4] واجهات برمجة تطبيقات نظام الملفات مع ملفات التحكم بدلاً من أوصاف الملفات.
ملفات الرأس
يتم تعريف واجهة مقبس Berkeley في العديد من ملفات الرأس. تختلف أسماء ومحتوى هذه الملفات قليلاً بين التطبيقات. بشكل عام، تتضمن:
| ملف | وصف |
|---|---|
| sys/socket.h | وظائف المقبس الأساسي وهياكل البيانات. |
| نتنيت/in.h | عائلات عناوين AF_INET وAF_INET6 وعائلات البروتوكول المقابلة لها، PF_INET وPF_INET6. تتضمن هذه عناوين IP القياسية وأرقام منافذ TCP وUDP. |
| sys/un.h | عائلة عناوين PF_UNIX وPF_LOCAL. تُستخدم للاتصال المحلي بين البرامج التي تعمل على نفس الكمبيوتر. |
| arpa/inet.h | وظائف لمعالجة عناوين IP الرقمية. |
| netdb.h | وظائف لترجمة أسماء البروتوكولات وأسماء المضيفين إلى عناوين رقمية. البحث في البيانات المحلية بالإضافة إلى خدمات الأسماء. |
وظائف API للمقبس

توفر واجهة برمجة تطبيقات 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() نقطة نهاية للاتصال وتعيد موصوف ملف للمقبس. وهي تستخدم ثلاثة وسيطات:
- المجال ، الذي يحدد عائلة البروتوكول للمقبس الذي تم إنشاؤه. على سبيل المثال:
- النوع ، واحد من:
- SOCK_STREAM (خدمة موثوقة موجهة نحو التدفق أو مآخذ تدفق )
- SOCK_DGRAM (خدمة البيانات أو مآخذ البيانات )
- SOCK_SEQPACKET (خدمة حزم متسلسلة موثوقة)
- SOCK_RAW (بروتوكولات خام أعلى طبقة الشبكة)
- بروتوكول يحدد بروتوكول النقل الفعلي المراد استخدامه. أكثر البروتوكولات شيوعًا هي IPPROTO_TCP و IPPROTO_SCTP و IPPROTO_UDP و IPPROTO_DCCP . يتم تحديد هذه البروتوكولات في الملف netinet/in.h . يمكن استخدام القيمة 0 لتحديد بروتوكول افتراضي من المجال والنوع المحددين.
تعيد الدالة القيمة -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 في حالة حدوث خطأ. تتم الآن جميع الاتصالات الأخرى مع المضيف البعيد عبر هذا المقبس الجديد.
لا تتطلب مآخذ البيانات معالجة عن طريق القبول () حيث يمكن للمستقبل الاستجابة فورًا للطلب باستخدام مأخذ الاستماع.
يتصل
تعمل وظيفة connect() على إنشاء رابط اتصال مباشر إلى مضيف بعيد محدد يتم تحديده من خلال عنوانه عبر مقبس، يتم تحديده من خلال موصوف الملف الخاص به.
عند استخدام بروتوكول موجه نحو الاتصال ، يؤدي هذا إلى إنشاء اتصال. بعض أنواع البروتوكولات لا تتطلب اتصالاً، وأبرزها بروتوكول بيانات المستخدم . عند استخدامه مع بروتوكولات لا تتطلب اتصالاً، يحدد الاتصال العنوان البعيد لإرسال واستقبال البيانات، مما يسمح باستخدام وظائف مثل الإرسال والاستقبال . في هذه الحالات، تمنع وظيفة الاتصال استقبال بيانات من مصادر أخرى.
تعيد الدالة connect() عددًا صحيحًا يمثل رمز الخطأ: 0 يمثل النجاح، بينما يمثل -1 خطأً. تاريخيًا، في الأنظمة المشتقة من BSD، تكون حالة موصوف المقبس غير محددة إذا فشلت المكالمة للاتصال (كما هو محدد في مواصفات Unix الفردية)، وبالتالي، يجب على التطبيقات المحمولة إغلاق موصوف المقبس على الفور والحصول على موصوف جديد باستخدام socket()، في حالة فشل المكالمة للاتصال. [5]
gethostbyname وgethostbyaddr
تُستخدم الدالتان gethostbyname() و gethostbyaddr() لحل أسماء المضيفين والعناوين في نظام اسم المجال أو آليات الحل الأخرى للمضيف المحلي (على سبيل المثال، البحث في /etc/hosts). وتقوم الدالتان بإرجاع مؤشر إلى كائن من نوع struct hostent ، والذي يصف مضيف بروتوكول الإنترنت . تستخدم الدالتان الوسائط التالية:
- يحدد الاسم اسم المضيف.
- يحدد addr مؤشرًا إلى بنية in_addr تحتوي على عنوان المضيف.
- يحدد len طول addr بالبايت .
- يحدد النوع نوع عائلة العنوان (على سبيل المثال، AF_INET) لعنوان المضيف.
تعيد الوظائف مؤشر NULL في حالة حدوث خطأ، وفي هذه الحالة يمكن التحقق من العدد الصحيح الخارجي h_errno لمعرفة ما إذا كان هذا فشلًا مؤقتًا أو مضيفًا غير صالح أو غير معروف. وإلا فسيتم إرجاع بنية hostent صالحة * .
لا تعد هذه الوظائف مكونًا صارمًا لواجهة برمجة تطبيقات BSD socket، ولكنها تُستخدم غالبًا جنبًا إلى جنب مع وظائف واجهة برمجة التطبيقات للبحث عن مضيف. تُعتبر هذه الوظائف الآن واجهات قديمة لاستعلام نظام اسم المجال. تم تعريف وظائف جديدة لا تعتمد على بروتوكول معين (تدعم IPv6). هذه الوظائف الجديدة هي getaddrinfo() وgetnameinfo() ، وتستند إلى بنية بيانات addrinfo جديدة . [6]
ظهر هذا الزوج من الوظائف في نفس الوقت مع واجهة برمجة تطبيقات مأخذ BSD المناسبة في 4.2BSD (1983)، [7] وهو نفس العام الذي تم فيه إنشاء DNS لأول مرة. لم تستفسر الإصدارات المبكرة عن DNS وأجرت فقط بحثًا في /etc/hosts. أضافت نسخة 4.3BSD (1984) DNS بطريقة بدائية. يستمد التنفيذ الحالي باستخدام Name Service Switch من Solaris وNetBSD 1.4 (1999) اللاحق. [8] تم تعريف NSS في البداية لـ NIS+ ، مما يجعل DNS أحد الخيارات العديدة للبحث بواسطة هذه الوظائف ويمكن تعطيل استخدامه حتى اليوم. [9]
بروتوكول وعنوان العائلات
واجهة برمجة تطبيقات Berkeley socket هي واجهة عامة للشبكات والاتصالات بين العمليات، وتدعم استخدام بروتوكولات الشبكة المختلفة وهندسة العناوين.
تتضمن القائمة التالية عينة من عائلات البروتوكول (يسبقها معرف رمزي قياسي) المحددة في تنفيذ Linux أو BSD الحديث :
| معرف | الوظيفة أو الاستخدام |
|---|---|
| PF_APPLETALK | أبل توك |
| PF_ATMPVC | الدوائر الافتراضية الدائمة في وضع النقل غير المتزامن |
| PF_ATMSVC | الدوائر الافتراضية المبدلة في وضع النقل غير المتزامن |
| PF_AX25 | راديو الهواة AX.25 |
| PF_CAN | شبكة منطقة التحكم |
| PF_بلوتوث | مقابس البلوتوث |
| جسر PF_ | جسر متعدد البروتوكولات |
| شبكة PF_DEC | مخصص لمشروع DECnet |
| شبكة PF_ECONET | اكونت البلوط |
| PF_INET | بروتوكول الإنترنت الإصدار الرابع |
| PF_INET6 | بروتوكول الإنترنت الإصدار 6 |
| PF_IPX | تبادل حزم الإنترنت من Novell |
| PF_IRDA | مقابس الأشعة تحت الحمراء |
| مفتاح PF_ | واجهة برمجة تطبيقات إدارة المفاتيح PF_KEY |
| PF_LOCAL، PF_UNIX، PF_FILE | محليًا للمضيف (الأنابيب ونطاق الملف) |
| PF_NETROM | شبكة راديو الهواة/ROM (مرتبطة بـ AX.25) [10] |
| PF_NETBEUI | مخصص لمشروع 802.2LLC |
| PF_الأمن | استدعاء أمني زائف AF |
| رابط PF_NET، مسار PF_ | واجهة برمجة التطبيقات للتوجيه |
| حزمة PF_ | مآخذ التقاط الحزم |
| PF_PPPOX | PPP عبر مآخذ X |
| PF_SNA | مشروع هندسة شبكات أنظمة Linux (SNA) |
| أنبوب PF_WAN | مآخذ API الخاصة بـ Sangoma 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،close() قد يؤدي استخدام SO_LINGER إلى تجاهل البيانات. وقد يكون استخدام shutdown()SO_LINGER أو 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 <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include < stdlib.h> #include <string.h> #include <unistd.h> int main ( void ) { struct sockaddr_in sa ; int SocketFD = socket ( PF_INET , SOCK_STREAM , IPPROTO_TCP ); if ( SocketFD == -1 ) { perror ( "لا يمكن إنشاء مقبس" ); exit ( EXIT_FAILURE ); } memset ( & sa , 0 , sizeof sa ); sa . sin_family = AF_INET ; sa . sin_port = htons ( 1100 ); sa . sin_addr . s_addr = htonl ( INADDR_ANY )؛ إذا ( bind ( SocketFD ، ( struct sockaddr * ) & sa ، sizeof sa ) == -1 ) { perror ( "فشل الربط" )؛ إغلاق ( SocketFD )؛ الخروج ( EXIT_FAILURE )؛ } إذا ( listen ( SocketFD ، 10 ) == -1 ) { perror ( "فشل الاستماع" )؛ إغلاق ( SocketFD )؛ الخروج ( EXIT_FAILURE )؛ } لـ (;;) { int ConnectFD = قبول ( SocketFD ، NULL ، NULL )؛ إذا ( ConnectFD == -1 ) { perror ( "فشل القبول" )؛ إغلاق ( SocketFD )؛
الخروج ( EXIT_FAILURE )؛
} /* تنفيذ عمليات القراءة والكتابة ... read(ConnectFD, buff, size) */ if ( shutdown ( ConnectFD , SHUT_RDWR ) == -1 ) { perror ( "فشل إيقاف التشغيل" )؛ close ( ConnectFD )؛ close ( SocketFD )؛ exit ( EXIT_FAILURE )؛ } close ( ConnectFD )؛ }
إغلاق ( SocketFD )
؛ إرجاع EXIT_SUCCESS ؛
عميل
تتضمن برمجة تطبيق عميل TCP الخطوات التالية:
- إنشاء مقبس TCP.
- الاتصال بالخادم ( connect() )، عن طريق تمرير
sockaddr_inبنية معsin_familyالتعيين إلى AF_INET ، وتعيين المنفذ الذي تستمع إليه نقطة النهاية (حسب ترتيب بايتات الشبكة)، وتعيين عنوان IP الخاص بخادم المستمع (أيضًا حسب ترتيب بايتات الشبكة).sin_portsin_addr - التواصل مع المضيف البعيد باستخدام وظائف API send() و recv() ، بالإضافة إلى الوظائف العامة write() و read() .
- إغلاق كل مقبس تم فتحه بعد الاستخدام باستخدام الدالة close().
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include < stdlib.h> #include <string.h> #include <unistd.h> int main ( void ) { struct sockaddr_in sa ; int res ; int SocketFD ;
SocketFD = socket ( PF_INET ، SOCK_STREAM ، IPPROTO_TCP )؛ إذا ( SocketFD == -1 ) { perror ( "لا يمكن إنشاء مقبس" ) ؛ الخروج ( EXIT_FAILURE ) ؛ } memset ( & sa ، 0 ، حجم sa ) ؛ sa.sin_family = AF_INET ؛ sa.sin_port = htons ( 1100 ) ؛ res = inet_pton ( AF_INET ، " 192.168.1.3" ، & sa.sin_addr ) ؛
إذا ( connect ( SocketFD ، ( struct sockaddr * ) & sa ، sizeof sa ) == -1 ) { perror ( "فشل الاتصال" )؛ إغلاق ( SocketFD )؛ الخروج ( EXIT_FAILURE )؛ } /* تنفيذ عمليات القراءة والكتابة ... */ إغلاق ( SocketFD )؛ إرجاع EXIT_SUCCESS ؛ }
مثال على اتصال العميل بالخادم باستخدام UDP
بروتوكول بيانات المستخدم (UDP) هو بروتوكول بدون اتصال ولا يوجد ضمان للتسليم. قد تصل حزم بيانات المستخدم (UDP) بشكل غير منظم، أو عدة مرات، أو لا تصل على الإطلاق. وبسبب هذا التصميم البسيط، فإن تكلفة بروتوكول UDP أقل بكثير من تكلفة بروتوكول TCP. يعني عدم وجود اتصال أنه لا يوجد مفهوم لتدفق أو اتصال دائم بين مضيفين. يشار إلى مثل هذه البيانات باسم حزم البيانات ( مقابس حزم البيانات ).
مساحة عنوان UDP، مساحة أرقام منفذ UDP (في مصطلحات ISO، TSAPs )، منفصلة تمامًا عن مساحة منافذ TCP.
الخادم
قد يقوم أحد التطبيقات بإعداد خادم UDP على المنفذ رقم 7654 على النحو التالي. يحتوي البرنامج على حلقة لا نهائية تستقبل رسائل بيانات UDP باستخدام الدالة recvfrom() .
#include <stdio.h> #include <errno.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <unistd.h> /* لإغلاق () للمقبس */ #include <stdlib.h>
int main ( void ) { int sock ؛ struct sockaddr_in sa ؛ char buffer [ 1024 ]؛ ssize_t recsize ؛ socklen_t fromlen ؛
memset ( & sa , 0 , sizeof sa ); سا . sin_family = AF_INET ؛ سا . sin_addr . s_addr = htonl ( INADDR_ANY ); سا . sin_port = htons ( 7654 ); فروملين = حجم سا ;
جورب = مقبس ( PF_INET ، SOCK_DGRAM ، IPPROTO_UDP )؛
إذا ( ربط ( sock ، ( struct sockaddr * ) & sa ، حجم sa ) == -1 ) { perror ( "فشل ربط الخطأ" )؛ إغلاق ( sock )؛ الخروج ( EXIT_FAILURE )؛ }
بالنسبة إلى (;;) { recsize = recvfrom ( sock ، ( void * ) buffer ، حجم المخزن المؤقت ، 0 ، ( struct sockaddr * ) & sa ، & fromlen )؛ إذا كان ( recsize < 0 ) { fprintf ( stderr ، "%s \n " ، strerror ( errno ))؛ الخروج ( EXIT_FAILURE )؛ } printf ( "recsize: %d \n " ، ( int ) recsize )؛ السكون ( 1 )؛ printf ( "datagram: %.*s \n " ، ( int ) recsize ، buffer )؛ } }
عميل
فيما يلي برنامج عميل لإرسال حزمة UDP تحتوي على السلسلة "Hello World!" إلى العنوان 127.0.0.1 على رقم المنفذ 7654.
#include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <unistd.h> #include <arpa/inet.h>
int main ( void ) { int sock ; struct sockaddr_in sa ; int bytes_sent ; char buffer [ 200 ]; strcpy ( buffer , "hello world!" ); /* إنشاء إنترنت أو مخطط بيانات أو مقبس باستخدام UDP */ sock = socket ( PF_INET , SOCK_DGRAM , IPPROTO_UDP ); if ( sock == -1 ) { /* إذا فشل تهيئة المقبس، اخرج */ printf ( "خطأ في إنشاء المقبس" ); exit ( EXIT_FAILURE ); } / * عنوان المقبس صفر */ memset ( & sa , 0 , sizeof sa ); /* العنوان هو IPv4 */ sa.sin_family = AF_INET ; / * عناوين IPv4 هي uint32_t ، قم بتحويل تمثيل سلسلة من الثمانيات إلى القيمة المناسبة */ sa.sin_addr . s_addr = inet_addr ( "127.0.0.1" ); /* المقابس هي مقابس قصيرة غير موقعة، htons(x) تضمن أن x في ترتيب بايتات الشبكة، اضبط المنفذ على 7654 */ sa . sin_port = htons ( 7654 ); bytes_sent = sendto ( sock , buffer , strlen ( buffer ), 0 ,( struct sockaddr * ) & sa , sizeof sa ); if ( bytes_sent < 0 ) { printf ( "خطأ في إرسال الحزمة: %s \n " , strerror ( errno )); exit ( EXIT_FAILURE ); } close ( sock ); /* أغلق المقبس */ return 0 ; }
في هذا الكود، يعتبر buffer مؤشرًا للبيانات التي سيتم إرسالها، ويحدد buffer_length حجم البيانات.
مراجع
- ^ على سبيل المثال، في لغة برمجة Ruby ruby-doc::Socket
- ^ "— مواصفات POSIX.1-2008". Opengroup.org . تم الاسترجاع في 2012-07-26 .
- ^ "تنظيم الشبكات في الخطة 9".
- ^ "مجموعة TCP/IP لنظام Linux كمكون إضافي لـ VFS".
- ^ ستيفنز وراجو 2013، ص 607.
- ^ POSIX.1-2004
- ^ – دليل وظائف مكتبة FreeBSD
- ^ كونيل، أريادن (27 مارس 2022). "مأساة gethostbyname". ariadne.space .
- ^ – دليل تنسيقات ملفات FreeBSD
- ^ https://manpages.debian.org/experimental/ax25-tools/netrom.4.en.html.
{{cite web}}: مفقود أو فارغ|title=( مساعدة ) - ^ برمجة شبكات UNIX، المجلد 1، الطبعة الثالثة: واجهة برمجة تطبيقات شبكات المقابس، دبليو ريتشارد ستيفنز، بيل فينر، أندرو م. رودوف، أديسون ويسلي، 2003.
- ^ "المواصفات الأساسية لمجموعة Open Group العدد 7". Pubs.opengroup.org . تم الاسترجاع في 2012-07-26 .
- ^ "مقابس TCP/IP الخام - تطبيقات Win32". 19 يناير 2022.
- ^ "دليل Beej لبرمجة الشبكات". Beej.us. 2007-05-05 . تم الاسترجاع في 2012-07-26 .
- ^ "مقابس الإنهاء". Softlab.ntua.gr . تم الاسترجاع في 2012-07-26 .
- ^ "ntua.gr - برمجة مآخذ يونكس بلغة سي - الأسئلة الشائعة: الأسئلة المتعلقة بكل من العميل والخادم (TCP/SOCK_STREAM)". Softlab.ntua.gr . تم الاسترجاع في 2012-07-26 .
يرد التعريف القياسي القانوني لواجهة Sockets في معيار POSIX، المعروف باسم:
- IEEE Std. 1003.1-2001 معيار تكنولوجيا المعلومات - واجهة نظام التشغيل المحمول (POSIX).
- المعايير الفنية للمجموعة المفتوحة: المواصفات الأساسية، العدد 6، ديسمبر 2001.
- ايزو/اي اي سي 9945:2002
تتوفر معلومات حول هذا المعيار والعمل الجاري عليه من موقع أوستن الإلكتروني.
تم توثيق امتدادات IPv6 لواجهة برمجة تطبيقات المقبس الأساسي في RFC 3493 وRFC 3542.
- ستيفنز، دبليو ريتشارد؛ راجو، ستيفن أ. (24 مايو 2013). البرمجة المتقدمة في بيئة يونكس (الطبعة الثالثة). أديسون ويسلي بروفيشنال . رقم ISBN 978-0321637734تم الاسترجاع بتاريخ 27 فبراير 2015 .
روابط خارجية
- المستندات التكميلية لمبرمجي UNIX (PSD: 20-1)
- دليل Beej لبرمجة الشبكات - 2007
- نقل برامج Berkeley Socket إلى Winsock - وثائق Microsoft.
- برمجة مآخذ يونكس بلغة سي - الأسئلة الشائعة - 1996
- برمجة شبكات لينكس - مجلة لينكس ، 1998
