انتقل إلى المحتوى الرئيسي

📧 الاتصال بين العمليات: العميل والخادم

🎯 الهدف التعليمي​

  • فهم آليات الاتصال بين العمليات (IPC) باستخدام المقابس.
  • إنشاء خادم وعميل باستخدام بايثون.
  • تحديد دور نظام التشغيل في إدارة اتصالات الشبكة.

📜 البيان​

في هذا التمرين، ستقوم بإنشاء تطبيق بسيط للاتصال بين عميل وخادم باستخدام المقابس في بايثون.

🔧 الخطوة 1 – إنشاء خادم بسيط​

الخادم:

  • يفتح "بابًا" (يسمى منفذ) على جهاز الكمبيوتر الخاص بك.
  • ينتظر اتصال العميل به.
  • يستقبل الرسائل من العميل.

كود الخادم​

قم بإنشاء ملف يسمى serveur.py ونسخ الكود التالي:

import socket

# Crée un socket TCP
serveur = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# L'adresse IP et le port à utiliser
hote = 'localhost'
port = 12345

# Lier le socket à l'adresse
serveur.bind((hote, port))

# Écoute jusqu'à 1 connexion
serveur.listen(1)
print("[Serveur] En attente de connexion...")

# Accepter la connexion
connexion, adresse = serveur.accept()
print(f"[Serveur] Connexion de {adresse}")

# Réception des messages
while True:
message = connexion.recv(1024).decode()
if message.lower() == "exit":
print("[Serveur] Fin de la communication.")
break
print(f"[Client] {message}")

# Fermer les connexions
connexion.close()
serveur.close()

اختبار​

هذا البرنامج لا يفعل شيئًا طالما لم يتصل به عميل. ابدأ الخادم بالأمر:

python serveur.py

واتصل به باستخدام متصفح الويب الخاص بك بالذهاب إلى http://localhost:12345.

تلميح

يمكنك إيقاف الخادم بالضغط على Ctrl+C في الطرفية.

💬 الخطوة 2 – إنشاء عميل​

العميل:

  • يتصل بخادم (هنا عبر localhost والمنفذ).
  • يرسل الرسائل التي تكتبها في وحدة التحكم.

2.1 كود العميل​

قم بإنشاء ملف client.py:

import socket

# Crée un socket TCP
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Se connecter au serveur
client.connect(('localhost', 12345))
print("[Client] Connecté au serveur. Tapez vos messages :")

# Lire et envoyer les messages
while True:
message = input(">>> ")
client.sendall(message.encode())
if message.lower() == "exit":
break

# Fermer la connexion
client.close()

2.2 اختبار​

  1. في طرفية: شغل python serveur.py
  2. في طرفية أخرى: شغل python client.py
  3. تبادل بعض الرسائل.
  4. اكتب exit للإيقاف.

🔁 الخطوة 3 – الاتصال في الاتجاهين​

حاليًا، يتحدث العميل فقط ويستمع الخادم. سنسمح الآن لـ الخادم بالرد.

3.1 استخدام threading للاستماع والكتابة في نفس الوقت​

قم بإنشاء ملف يسمى serveur2.py ونسخ الكود التالي:

import socket
import threading

def recevoir(connexion):
while True:
try:
message = connexion.recv(1024).decode()
if not message or message.lower() == "exit":
print("[Client] a quitté la conversation.")
print("[Serveur >>>] ", end="", flush=True)
break
print(f"[Client] {message}")
print("[Serveur >>>] ", end="", flush=True)
except: # noqa: E722
break

def envoyer(connexion):
while True:
try:
message = input("[Serveur >>>] ")
connexion.sendall(message.encode())
if message.lower() == "exit":
break
except: # noqa: E722
break

# Création du serveur
serveur = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serveur.bind(('localhost', 12345))
serveur.listen(1)

print("[Serveur] En attente de connexion...")
connexion, adresse = serveur.accept()
print(f"[Serveur] Connecté à {adresse}")

# Démarrage des threads
thread1 = threading.Thread(target=recevoir, args=(connexion,))
thread2 = threading.Thread(target=envoyer, args=(connexion,))
thread1.start()
thread2.start()

thread1.join()
thread2.join()
connexion.close()
serveur.close()

قم بإنشاء ملف يسمى client2.py ونسخ الكود التالي:

import socket
import threading

def recevoir(sock):
while True:
message = sock.recv(1024).decode()
if message.lower() == "exit":
print("[Serveur] a quitté la conversation.")
print("[Client >>>] ", end="", flush=True)
break
print(f"[Serveur] {message}")
print("[Client >>>] ", end="", flush=True)

def envoyer(sock):
while True:
message = input("[Client >>>] ")
sock.sendall(message.encode())
if message.lower() == "exit":
break

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('localhost', 12345))
print("[Client] Connecté au serveur.")

# Démarrage des threads
thread1 = threading.Thread(target=recevoir, args=(client,))
thread2 = threading.Thread(target=envoyer, args=(client,))
thread1.start()
thread2.start()

thread1.join()
thread2.join()
client.close()

✅ النتيجة المتوقعة​

  1. شغل serveur2.py في طرفية.
  2. شغل client2.py في طرفية أخرى.
  3. حاول تبادل الرسائل في الاتجاهين.
  4. أغلق بشكل صحيح باستخدام exit.

مثال على الاتصال بين العميل والخادم

📝 الخطوة 4 – نظام التشغيل​

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

أجب عن الأسئلة التالية:

  • في كود الخادم والعميل، في أي لحظة يكون نظام التشغيل متورطًا؟
  • هل يتم إدارة الخيوط بواسطة نظام التشغيل (OS thread) أم بواسطة بايثون (application thread)؟
  • لماذا من المهم إغلاق الاتصالات بشكل صحيح؟
  • ماذا يحدث إذا حاولت تشغيل الخادم عدة مرات على نفس المنفذ دون إغلاقه؟
  • ماذا يحدث إذا حاولت تشغيل العميل قبل الخادم؟