برمجة المقبس في C

برمجة المقبس في C

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

مثال

في برنامج C هذا، نقوم بتبادل رسالة ترحيب واحدة بين الخادم والعميل لتوضيح نموذج العميل/الخادم.

الخادم. ج

C
   #include         #include         #include         #include         #include         #include         #define PORT 8080   int     main  (  int     argc       char     const  *     argv  [])   {      int     server_fd       new_socket  ;      ssize_t     valread  ;      struct     sockaddr_in     address  ;      int     opt     =     1  ;      socklen_t     addrlen     =     sizeof  (  address  );      char     buffer  [  1024  ]     =     {     0     };      char  *     hello     =     'Hello from server'  ;      // Creating socket file descriptor      if     ((  server_fd     =     socket  (  AF_INET       SOCK_STREAM       0  ))      <     0  )     {      perror  (  'socket failed'  );      exit  (  EXIT_FAILURE  );      }      // Forcefully attaching socket to the port 8080      if     (  setsockopt  (  server_fd       SOL_SOCKET        SO_REUSEADDR     |     SO_REUSEPORT       &  opt        sizeof  (  opt  )))     {      perror  (  'setsockopt'  );      exit  (  EXIT_FAILURE  );      }      address  .  sin_family     =     AF_INET  ;      address  .  sin_addr  .  s_addr     =     INADDR_ANY  ;      address  .  sin_port     =     htons  (  PORT  );      // Forcefully attaching socket to the port 8080      if     (  bind  (  server_fd       (  struct     sockaddr  *  )  &  address        sizeof  (  address  ))       <     0  )     {      perror  (  'bind failed'  );      exit  (  EXIT_FAILURE  );      }      if     (  listen  (  server_fd       3  )      <     0  )     {      perror  (  'listen'  );      exit  (  EXIT_FAILURE  );      }      if     ((  new_socket      =     accept  (  server_fd       (  struct     sockaddr  *  )  &  address        &  addrlen  ))       <     0  )     {      perror  (  'accept'  );      exit  (  EXIT_FAILURE  );      }          // subtract 1 for the null      // terminator at the end      valread     =     read  (  new_socket       buffer        1024     -     1  );         printf  (  '%s  n  '       buffer  );      send  (  new_socket       hello       strlen  (  hello  )     0  );      printf  (  'Hello message sent  n  '  );      // closing the connected socket      close  (  new_socket  );          // closing the listening socket      close  (  server_fd  );      return     0  ;   }   

Client.c

C
   #include          #include         #include         #include         #include         #define PORT 8080   int     main  (  int     argc       char     const  *     argv  [])   {      int     status       valread       client_fd  ;      struct     sockaddr_in     serv_addr  ;      char  *     hello     =     'Hello from client'  ;      char     buffer  [  1024  ]     =     {     0     };      if     ((  client_fd     =     socket  (  AF_INET       SOCK_STREAM       0  ))      <     0  )     {      printf  (  '  n   Socket creation error   n  '  );      return     -1  ;      }      serv_addr  .  sin_family     =     AF_INET  ;      serv_addr  .  sin_port     =     htons  (  PORT  );      // Convert IPv4 and IPv6 addresses from text to binary      // form      if     (  inet_pton  (  AF_INET       '127.0.0.1'       &  serv_addr  .  sin_addr  )       <=     0  )     {      printf  (      '  n  Invalid address/ Address not supported   n  '  );      return     -1  ;      }      if     ((  status      =     connect  (  client_fd       (  struct     sockaddr  *  )  &  serv_addr        sizeof  (  serv_addr  )))       <     0  )     {      printf  (  '  n  Connection Failed   n  '  );      return     -1  ;      }          // subtract 1 for the null      // terminator at the end      send  (  client_fd       hello       strlen  (  hello  )     0  );      printf  (  'Hello message sent  n  '  );      valread     =     read  (  client_fd       buffer        1024     -     1  );         printf  (  '%s  n  '       buffer  );      // closing the connected socket      close  (  client_fd  );      return     0  ;   }   


تجميع

 gcc client.c -o clientgcc server.c -o server  


الإخراج

 Client:Hello message sentHello from serverServer:Hello from clientHello message sent  

مكونات برمجة المقبس

1. المقابس

مآخذ هي أحد المكونات الأساسية التي يستخدمها البرنامج للوصول إلى الشبكة للتواصل مع العمليات/العقد الأخرى عبر الشبكة. إنه ببساطة مزيج من عنوان IP ورقم المنفذ الذي يعمل كنقطة نهاية للاتصال.
مثال: 192.168.1.1:8080 حيث يمثل الجزءان المفصولان بالنقطتين عنوان IP( 192.168.1.1 ) و رقم المنفذ (8080).

أنواع المقبس:

  • مقبس TCP (مقبس الدفق): يوفر اتصالاً موثوقًا قائمًا على الاتصال (أي بروتوكول TCP ).
  • مقبس UDP (مقبس مخطط البيانات): يوفر اتصالاً بدون اتصال بشكل أسرع ولكن غير موثوق به (على سبيل المثال. بروتوكول UDP ).

2. نموذج خادم العميل

ال نموذج خادم العميل يشير إلى البنية المستخدمة في برمجة المقبس حيث يتفاعل العميل والخادم مع بعضهما البعض لتبادل المعلومات أو الخدمات. تسمح هذه البنية للعميل بإرسال طلبات الخدمة وللخادم بمعالجة وإرسال الاستجابة لطلبات الخدمة هذه.

مخطط الحالة لنموذج الخادم والعميل

برمجة المقبس في Cمخطط الحالة لنموذج الخادم والعميل من المقبس

تعد برمجة المقبس في لغة C طريقة قوية للتعامل مع اتصالات الشبكة.

إنشاء عملية من جانب الخادم

يتم إنشاء الخادم بإتباع الخطوات التالية:

1. إنشاء المقبس

تتضمن هذه الخطوة إنشاء المقبس باستخدام وظيفة المقبس ().

حدود:

  • سوكفد: واصف المقبس عدد صحيح (مثل مقبض الملف)
  • اِختِصاص: عدد صحيح يحدد مجال الاتصال. نحن نستخدم AF_LOCAL كما هو محدد في معيار POSIX للتواصل بين العمليات على نفس المضيف. للتواصل بين العمليات على مضيفين مختلفين متصلين بواسطة IPV4، نستخدم AF_INET وAF_I NET 6 للعمليات المتصلة بواسطة IPV6.
  • يكتب: نوع الاتصال
    SOCK_STREAM: TCP (موجه نحو الاتصال الموثوق)
    SOCK_DGRAM: UDP (بدون اتصال غير موثوق به)
  • بروتوكول: قيمة البروتوكول لبروتوكول الإنترنت (IP) هي 0. وهذا هو نفس الرقم الذي يظهر في حقل البروتوكول في رأس IP للحزمة. (man Protocols لمزيد من التفاصيل)
C
   sockfd     =     socket  (  domain       type       protocol  )   

2. قم بتعيين اختيار المقبس

يساعد هذا في معالجة خيارات المقبس المشار إليها بواسطة واصف الملف sockfd. يعد هذا أمرًا اختياريًا تمامًا ولكنه يساعد في إعادة استخدام العنوان والمنفذ. يمنع الخطأ مثل: العنوان قيد الاستخدام بالفعل.

C
   setsockopt  (  sockfd       level       optname       optval       socklen_t     optlen  );   

3. ربط

بعد إنشاء المقبس، تقوم الدالة bind() بربط المقبس بالعنوان ورقم المنفذ المحدد في addr(بنية البيانات المخصصة). في رمز المثال، نربط الخادم بالمضيف المحلي ومن ثم نستخدم INADDR_ANY لتحديد عنوان IP.

C++
   bind  (  sockfd       sockaddr     *  addr       socklen_t     addrlen  );   

حدود:

  • com.sockfd : واصف ملف مأخذ التوصيل الذي تم إنشاؤه باستخدام وظيفة المقبس ().
  • عنوان : مؤشر إلى struct sockaddr الذي يحتوي على عنوان IP ورقم المنفذ لربط المقبس.
  • com.addrlen : طول بنية addr.

4. استمع

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

C
   listen  (  sockfd       backlog  );   

حدود :

  • com.sockfd : واصف ملف مأخذ التوصيل الذي تم إنشاؤه باستخدام وظيفة المقبس ().
  • تراكم : رقم يمثل حجم قائمة الانتظار التي تحتوي على الاتصالات المعلقة أثناء انتظار الخادم لقبول الاتصال.

5. قبول

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

C
   new_socket  =     accept  (  sockfd       sockaddr     *  addr       socklen_t     *  addrlen  );   

حدود:

  • com.sockfd : تم إرجاع واصف ملف المقبس بواسطة المقبس () والربط ().
  • عنوان : مؤشر إلى بنية sockaddr التي ستحتفظ بعنوان IP الخاص بالعميل ورقم المنفذ.
  • com.addrlen : مؤشر لمتغير يحدد طول بنية العنوان.

6. إرسال/استقبال

في هذه الخطوة يمكن للخادم إرسال أو استقبال البيانات من العميل.

يرسل(): لإرسال البيانات إلى العميل

C
   send  (  sockfd       *  buf       len       flags  );   

حدود:

  • com.sockfd : واصف ملف مأخذ التوصيل الذي يتم إرجاعه بواسطة وظيفة المقبس ().
  • buf : مؤشر إلى المخزن المؤقت الذي يحتوي على البيانات المراد إرسالها.
  • فقط : عدد بايتات البيانات التي سيتم إرسالها.
  • أعلام : عدد صحيح يحدد خيارات متنوعة لكيفية إرسال البيانات وعادةً ما يتم استخدام 0 للسلوك الافتراضي.

يستلم() : لتلقي البيانات من العميل.

C
   recv  (     sockfd       *  buf       len       flags  );   

حدود:

  • com.sockfd : واصف ملف مأخذ التوصيل الذي يتم إرجاعه بواسطة وظيفة المقبس ().
  • buf : مؤشر إلى المخزن المؤقت الذي يحتوي على البيانات المراد تخزينها.
  • فقط : عدد بايتات البيانات التي سيتم إرسالها.
  • أعلام : عدد صحيح يحدد خيارات متنوعة لكيفية إرسال البيانات وعادةً ما يتم استخدام 0 للسلوك الافتراضي.

6. إغلاق

بعد اكتمال تبادل المعلومات، يقوم الخادم بإغلاق المقبس باستخدام الدالة Close() وتحرير موارد النظام.

C
   close  (  fd  );   

حدود:

  • fd: واصف ملف المقبس.

إنشاء عملية من جانب العميل

اتبع الخطوات التالية لإنشاء عملية من جانب العميل:

1. اتصال المقبس

تتضمن هذه الخطوة إنشاء المقبس الذي يتم بنفس طريقة إنشاء المقبس الخاص بالخادم

2. الاتصال

يقوم استدعاء النظام Connect() بتوصيل المقبس المشار إليه بواسطة واصف الملف sockfd بالعنوان المحدد بواسطة addr. يتم تحديد عنوان الخادم والمنفذ في addr.

C++
   connect  (  sockfd       sockaddr     *  addr       socklen_t     addrlen  );   

حدود

  • com.sockfd : واصف ملف مأخذ التوصيل الذي يتم إرجاعه بواسطة وظيفة المقبس ().
  • عنوان : مؤشر لبناء sockaddr الذي يحتوي على عنوان IP الخاص بالخادم ورقم المنفذ.
  • com.addrlen : حجم addr.

3. إرسال/استقبال

في هذه الخطوة يمكن للعميل إرسال أو استقبال البيانات من الخادم، ويتم ذلك باستخدام وظائف الإرسال () والاستلام () المشابهة لكيفية إرسال/استقبال البيانات من العميل.

4. إغلاق

بمجرد اكتمال تبادل المعلومات، يحتاج العميل أيضًا إلى إغلاق المقبس الذي تم إنشاؤه وتحرير موارد النظام باستخدام وظيفة Close() بنفس الطريقة التي يفعلها الخادم.

المشكلات الشائعة وإصلاحاتها في برمجة المقبس

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