نبذة عن الموضوع
توفر لغة بايثون "python" المكتبة البرمجية socket التي تمكنك من التعامل مع الشبكات أو ما يسمى "low socket programming" التي تمكنك من كتابة برامج تستطيع الأتصال عبر الشبكة .الشرح
لجميع برامج الشبكة مهما أختلفت لغات برمجتها خطوات متشابهة في برمجة الشبكات و تلخص الصورة التالية الخطوات بالنسبة لبرنامج الخادم "Server" و برنامج العميل "Client" :سأبدأ الشرح بالخطوة الأولى التي يشترك فيها برنامج الخادم "Server" و العميل "Client", و هي أنشاء القابس "Socket" و أسماء البرتوكولات في لغة البايثون :
socket(address_family,protocol)
address_family : طريقة كتابة العناوين الخاصة بالبرنامج وهذه قائمة بالطرق الأكثر أستخدما :
AF_INET : العناوين الخاصة ببرتوكول IP الأصدارة الرابعة (هذه الطريقة التي سنستخدمها في هذا الدرس) .
AF_INET6 : العناوين الخاصة ببرتوكول IP الأصدارة السادسة.
AF_UNIX :هذه العناوين خاصة بأنظمة لينكس .
protocol : البرتوكول الذي سيتم أستخدامه في الأتصال و القائمة توضح البرتوكولات الشائع أستخدمها :
SOCK_STREAM: برتوكول TCP .
SOCK_DGRAM : برتوكول UDP .
SOCK_RAW : القابس الخام يسمح لك ببرمجة برامج تنشأ حزم الأنترنت من الصفر مما يسمح لك بالتحكم بكل ما يتم أرساله (برامج التنصت على الشبكة تستخدم القابس الخام في برمجتها) .
و الأن بعد أن أصبحت لديك فكرة عن أساسيات القابس "Socket" ألى مثال عملي باللغة البايثون :
import socket
sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
يقوم السطر الأول بأستدعاء المكتبة socket و يجب كتابة هذا السطر في بداية أي برنامج يستدعي الدوال الخاصة ببرامج الشبكات التي ستعرض في هذا الموضوع , أما السطر الثاني فينشأ قابس بالأسم "sos" يعتمد على البرتوكول IP/TCP (هذا الأسم الذي سيتم التعامل به مع القابس في البرنامج ).
الأن بعد شرح الخطوة الأولى في برمجة الشبكات في بايثون سنبدأ في الشرح الخطوات الباقية في برمجة الخادم "Server" بأستخدام البرتوكول TCP:
bind : سيتم في هذه الخطوة تحديد العنوان الخاص بالخادم "Server" الذي يتم تنفيذ البرنامج عليه (في اغلب البرامج التي ستمر عليك سيكون العنوان "localhost" أو عنوان الأي بي 127.0.0.1 ) , كما يجب أن يقوم المبرمج بتحديد المنفذ الذي سيستقبل البرنامج عليه الأتصالات الواردة من الشبكة.
sock.bind(("localhost",80))
تم تحديد المنفذ في هذه المثال بالمنفذ 80 الخاص برتوكول HTTP .
listen : البدأ بأنتظار الأتصالات الواردة من الشبكة , تستطيع تحديد عدد الأتصالات المسموح بأستقبالها في نفس الوقت كما في المثال:
sock.listen(5)
في المثال سيتم تحديد عدد الأتصالات بخمسة .
accept : عند قبول الأتصال يتم الحصول على عنوان الأي بي الخاص بالعميل و أعطاء منفذ محلي (local port) للبرنامج ( يتم أعطاء المنفذ بدون تدخل من المبرمج):
sock,(raddr,rport)=sock.accept()
في بايثون تعيد الطريقة "accept" كائن أو "object" يتم التعامل معه للأرسال و أستقبال البيانات , في مثالنا سيكون "sock" , كما سيتم أعطاء قيمة الأي بي للعميل "raddr" و المنفذ المحلي "rport".
send & recv : تستخدم مع الكائن الذي تنشأه accept للأرسال البيانات و أستقبالها كما في المثال :
data=sock.recv(1024)
sock.send("Welcome to my Server
")
في الأمر "recv" يجب تحديد الذاكرة المستخدمة لحفظ البيانت بالبايت , في المثال تم تحديدها بـ1024 كما أنني في المثال حفظت البيانات المستلمة في المتغير "data" , أما الأمر "send" فيجب فقط أن تحدد البيانات التي يجب أرسالها في القوسين .
close : يتم بهذا أنهاء الأتصال بين الخادم و العميل و هذا مثال على أستخدامه :
sock.close()
و الأن سوف أعرض برنامج الخادم "Server" بأستخدام برتوكول TCP كامل بعد أن فهمت الكيفية التي يعمل بها :
import socket
sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.bind(("localhost",80))
sock.listen(5)
sock,(raddr,rport)=sos.accept()
data=sock.recv(1024)
sock.send("Welcome to my Server
")
sock.close()
و الأن ألى شرح برمجة الخادم بأستخدام برتوكول UDP , سيكون البرمجة مشابه للبرتوكول TCP مع أختلاف أنه في برمجة UDP لا يحتاج ألى عملية listen و accept .
أنشاء قابس خاص ببرتوكول UDP
import socket
sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
bind
sock.bind(("localhost",2255))
تلاحظ التشابه بين الخطوات السابقة و برتوكول TCP .
أستقبال البيانات من العميل
في برامج التي تعتمد على برتوكول UDP لا حاجة ألى listen كما في المثال التالي:
while 1:
data,addr = sock.recvfrom(1024)
print data.strip(),addr
سنقوم بأنشاء حلقة تكرارية لا يمكن أيقافها ألا بضغط "Ctrl+D" بأستخدام while لكي يتم أستقبال البيانات من قبل العميل :
while 1:
يتم أستخدام الطريقة البرمجية "recvfrom" للأستلام البيانات في برامج UDP بعد تحديد الحد الأقصى للذاكرة لكل حزمة :
data,addr = sock.recvfrom(1024)
تقوم هذا الطريقة البرمجية بحفظ البيانات في المتغير "data" و عنوان المرسل "IP" في المتغير addr , السطر الخير سيقوم بطباعة البيانات , الأن ألى البرنامج كامل :
import socket
sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sock.bind(("localhost",2255))
while 1:
data,addr = sock.recvfrom(1024)
print data.strip(),addr
الأن مع برمجة العميل , طبعا لن أتطرق ألى النقاط المشتركة مع الخادم "Server" بالتفصيل , وسأبدأ ببرمجة عميل ببرتوكول TCP :
أنشاء القابس خاص ببرتوكول TCP
import socket
sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
بدأ الأتصال مع الخادم "Server"
sock.connect(("example.com",80))
تحتاج الدالة connect ألى عنوان الموقع أو رقم الأي بي , ثم رقم المنفذ الذي يعمل عليه الخادم الذي يراد الأتصال به .
أرسال و أستقبال البيانات
sock.send("GET /index.html")
data=sock.recv(1024)
تستخدم نفس الأوامر الخاص بالخادم للأرسال و أستقبال البيانات .
أغلاق الأتصال
sock.close()
و الأن ألى البرنامج كامل , هذا البرنامج يتصل بالموقع "example.com" بأستخدام برتوكول HTTP الذي يعمل على المنفذ 80 .
import socket
sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect(("example.com",80))
sock.send("GET /index.html")
data=sock.recv(1024)
sock.close()
أما برمجة العميل ببرتوكول UDP فيجب كما في الأمثلة السابقة تعريف القابس كالتالي :
import socket
sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
ثم يمكنك أرسال البيانات مباشرة بأستخدام الأمر "sendto" :
sock.sendto("test", ("example.com", 8111))
سيتم أرسال البيانات "test" ألى الموقع "example.com" على المنفذ 8111 .
أغلاق الأتصال
sock.close()
و هنا البرنامج كاملا :
import socket
sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sock.sendto("test", ("example.com", 8111))
sock.close()
في نهاية هذه المقال , يجب أن أنبه أن الأمثلة الموجودة لا يوجد فيها أي ألية للتحقق من أخطاء الأتصال و قد تعمدت هذا لتجنب تعقيد الأمثلة.