تقييم الموضوع :
  • 0 أصوات - بمعدل 0
  • 1
  • 2
  • 3
  • 4
  • 5
كيف نبرمج Proxy Server؟ من البداية حتى النهاية
#1
السلام عليكم ورحمة الله وبركاته
أهلا بكم أحبتي في الله


بعد أن تطرقت بالشرح لمبدأ عمل الـ Proxy Switcher في سين جيم
حول الـ Proxy Server


سأمر اليوم بحول الله للجانب التطبيقي و أبدأ بأول تطبيق و هو حول الإعداد و التنصت و الرد . أي أننا سنرى كيف نعد البرنامج ليصبح Proxy Switcher ثم نرى الجزء الأول و هو جزء التنصت على الطلبات و نظهر فحواها. بالإضافة إلى طريقة الرد على المتصفحات.
اخترت استعمال الـ Socket لأنها بسيطة و متوفرة في كل اللغات تقريبا بالإضافة إلى أنه يمكن استعمالها عن طريق API التي يوفرها Windows رغم أن هناك كلاسات خاصة في VB تسمح لي بالتنصت على نوع معين من أنواع الاتصالات. هذا بالإضافة أن الـ Socket تمكنني من تلقي كل أنواع الاتصالات عكس الأنواع المخصصة التي يختص كل واحد منها في نوع معين.
غير أنه لا يمنعنا لاحقا من استعمال نوع معين فقط مثل httprequest حين نريد تصميم برنامج حماية من المواقع غير المرغوبة لسبيين هما أن التصفح عادة يتم باستعمال البروتوكول http أوhttps و بالتالي سيكفينا النوع httprequest و سيوفر لنا الكثير من البيانات المفصلة مثل العنوان، نوع الاتصال، الضغط، الحجم، المتصفح، الكوكيز...إلخ عكس الـ socket التي ستعطينا ما تم إرساله أو استقباله بشكل نص واحد يصعب تفسيره و فصله و بالتالي تصعب معالجته.
لكن كما قلنا سنبدأ فقط باستعمال الـ Socket بشكل مبسط فقط لهضم الفكرة و معرفة كيف تعمل الأشياء

نبدأ بتصميم واجهة بسيطة تمكننا من:
  • مربع نص إدخال الـ IP للجهاز الذي سنضع فيه البروكسي سيرفر: في حالتي أنا سأدخل 127.0.0.1 لأنني سأستعمل البرنامج من جهازي و أشغل المتصفحات من نفس الجهاز.

  • مربع نص إدخال رقم المنفذ و الذي يكون من نوع Short أي محصورا بين 0 و 32000 من دون أن ننسى أن هناك بعض المنافذ المحجوزة

  • زر لتفعيل البروكسي سيرفر: يقوم هذا الزر بإدخال القيم التي كتبناها في مربعات النصوص في قاعدة الريجيستري و إعلان بداية التنصت

  • زر لتعطيل البروكسي سيرفر: يقوم هذا الزر بانهاء حالة التنصت و حذف القيم من قاعدة الريجيستري. لنعود للوضع الطبيعي.

  • ListBox نعرف فيه تاريخ كل اتصال و محتواه

إذن فواجهتنا ستكون بالشكل التالي:
[ATTACH=CONFIG]1960[/ATTACH]
كما هو ملاحظ فالواجهة بسيطة تبدأ بزر التعطيل "معطل" لأنه لا يوجد شيء في البداية.

الآن ننتقل لأهم خطوات الكود و كما قلنا سنستخدم الـ Threading في التنصت و المعالجة. و هنا السؤال: لماذا لا أبدأ في التنصت من البرنامج الرئيسي مباشرة من البرنامج الرئيسي؟
الجواب: لسبب بسيط جدا و هو أن عملية التنصت ستكون حلقة لا نهائية كما أنها هي عملية توقف أي شيء آخر أثناء التنصت و بالتالي سيستحيل علي ضغط زر أو إيقاف العملية. لكن حين نعمل Thread فرعي فيمكنني قتله أو توقيفه مؤقتا كما يمكنني التفاعل مع الواجهة من دون انتظار انتهاء تنصت لأنني سأعمل على التوازي و ليس على التسلسل.
في VB.Net في الحالة التلقائية. الثريدات الفرعية لا يمكنها تغيير خصائص المكونات الرسومية التي ينشؤها thread آخر بما فيها مكونات النافذة. لكننا نريد من ثريدات المعالجة أن يكتب لنا محتوى كل اتصال داخل الـ ListBox و بالتالي سيقع مشكل لأنها ممنوعة من ذلك. لكن يمكننا السماح لها بالتعامل مع المكونات و تغيير خصائصها بان نطلب من البرنامج تجاهل ايقاف الثريدات الفرعية من التغيير على المكونات الرسومية.
الآن أضع أسماء المكونات و أنواعها
مربع نص لكتابة الايبي txtIP
مربع نص لكتابة الـمنفذ txtPort
زر التفعيلbtnEnable
زر التعطيلbtnDisable
لائحة إظهار الاتصالات listResult
نمر للكود البرمجي لكل حالة مع الشرح
المتغيرات و الإعدادات العامة
اتفقنا أنه يلزمنا استعمال الـ Socket و يلزمنا استعمال الـ Threads . بالإضافة إلى حاجتنا لاستعمال الريجيستري .لذا في لغات الدوت نت سيلزمنا إضافة الريفيرنسات اللازمة لذلك

كود :
[COLOR=#000000][FONT=arabic transparent][COLOR=#000000][FONT=arabic transparent][COLOR=#000000][FONT=arabic transparent][FONT=courier new][SIZE=2][FONT=courier new][SIZE=2][FONT=courier new][SIZE=2]Imports System.Net
[color=black]Imports System.Net.Sockets[/color][/SIZE][/FONT][/SIZE][/FONT][/SIZE][/FONT][/FONT][/COLOR][/FONT][/COLOR][/FONT][/COLOR]
[COLOR=#000000][FONT=arabic transparent][COLOR=#000000][FONT=arabic transparent][COLOR=#000000][FONT=arabic transparent][FONT=courier new][SIZE=2][FONT=courier new][SIZE=2] [SIZE=2]Imports System.Threading[/SIZE]
[FONT=courier new][SIZE=2][color=black]Imports Microsoft.Win32[/color][/SIZE][/FONT]
[/SIZE][/FONT][/SIZE][/FONT][/FONT][/COLOR][/FONT][/COLOR][/FONT][/COLOR]

يلزمنا الآن أن نصرح بمتغير ListeningThread من نوع Thread و ظيفيته التنصت حيث سنربطه لاحقا مع إجراء التنصت لينفذه بشكل لانهائي و متوازي مع البرنامج الرئيسي.

يلزمنا أيضا Socket من نوع TcpListener لكي نوضفها في إجراء التنصت. و قد جعلناها متغيرا عاما لأننا نحتاج لإيقاف التنصت حين نضغط على زر التعطيل. فلو جعلناها متغيرا محليا في إجراء التنصت لن يكون بإمكاننا إيقاف التنصت من زر الإيقاف لأنه لن يستطيع الوصول إلى المتغيرات المحلية لإجراء آخر. و بالتالي لن نتمكن من تحرير المنفذ ليستعمل مرة أخرى.

كما يلزمنا أيضا أن نفتتح النافذة الرئيسية باعلان السماح للثريدات الفرعية بالتعامل مع المكونات الرسومية للنافذة ( نقصد بالتحديد القدرة على إضافة أسطر في لائحة إظهار الإتصلات).
إقتباس :[SIZE=2]'Declarer les variables globales
Dim ListeningThread As Thread

Dim Listener As TcpListener

Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
'Autoriser les "Threads"
CheckForIllegalCrossThreadCalls = False
End Sub

[/SIZE]
التفعيل و التعطيل
لنبدأ مع أول زر و هو الزر btnEnable وظيفته هي:
  • تعطيل الزر لنفسه
  • وضع القيم في الريجيستري
  • إنشاء Thread التنصت و ربطه بإجراء التنصت الذي نكون كتبناه مسبقا (سنراه لاحقا) و ليكن اسمه ProxyListen
  • إطلاق البروكسي ليبدأ عمله في التنصت
  • تفعيل الزر BtnDisable حتي يكون بمقدرونا ايقاف البروكسي لاحقا.
و كوده يكون كالآتي:
إقتباس :[SIZE=2]Private Sub btnEnable_Click(ByVal sender As System.Object, _[/SIZE]
[SIZE=2] ByVal e As System.EventArgs) Handles btnEnable.Click
'1.Desactiver le bouton "btnEnable"
btnEnable.Enabled = False
'2.Ajouter les valeurs de registry pour Activer le proxy server
Dim reg As RegistryKey = Registry.CurrentUser.OpenSubKey _

("Software\Microsoft\Windows\CurrentVersion\Internet Settings", True)
reg.SetValue("ProxyEnable", 1)
reg.SetValue("ProxyServer", txtIP.Text & ":" & txtPort.Text)
'3. Creer un thread d'ecout
ListeningThread = New Thread(AddressOf ProxyListen)
'4. Lancer le thread
ListeningThread.Start()
'4. Activer le bouton"btnDisable"
btnDisable.Enabled = True
End Sub


[/SIZE]
أما الزر btnDisable أولا. فوظيفته هي:
  • تعطيل الزر لنفسه
  • إيقاف عمل ListeningThread إن كان يعمل
  • "قتل" ListeningThread إن كان "حيا"
  • تحرير المنفذ عن طريق أيقاف و قتل Listener التنصت
  • حذف القيم من الريجيستري
  • تفعيل الزر btnEnable حتى يكون بمقدورنا بداية بروكسي جديد لاحقا.

إذن سيكون كوده كالآتي:
إقتباس :[SIZE=2]Private Sub btnDisable_Click(ByVal sender As System.Object, _[/SIZE]
[SIZE=2] ByVal e As System.EventArgs) Handles btnDisable.Click
'1.Desactiver le bouton "btnDisable"
btnDisable.Enabled = False
'2.Arreter et tuer le "Thread" d'ecoute
If ListeningThread IsNot Nothing Then
If ListeningThread.IsAlive Then
'2.1.Arret s'il est en marche
ListeningThread.Abort()
End If
'2.2.Tuer s'il existe
ListeningThread = Nothing
End If

[SIZE=2] '3. Tuer le listener
If listener IsNot Nothing Then
listener.Stop()
listener = Nothing
End If
[/SIZE]
'3.Supprimer les valeurs de registry pour supprimer le proxy server
Dim reg As RegistryKey = Registry.CurrentUser.OpenSubKey _



("Software\Microsoft\Windows\CurrentVersion\Internet Settings", True)
reg.SetValue("ProxyEnable", 0)
reg.SetValue("ProxyServer", String.Empty)
'4. Activer le bouton"btnEnable"
btnEnable.Enabled = True
End Sub


[/SIZE]
هذه هي إعدادات تفعيل و تعطيل البروكسي سيرفر و هي عادة لا تكون من إعدادات البرنامج الاعتيادية إذ يكفي أن تضاف في أول مرة عند التثبيت و تزال عند إزالة التثبيت أو تضاف في قائمة الإعدادات لتغيير المنفذ أو الـ IP حين يريد الإستعمال ذلك.
لو جعلناها كذلك فيجب أن نضع الكود الخاص بإنشاء الـ Thread و إطلاقه و إنهائه في البرنامج الرئيسي إذ يجب أن يتم إطلاقه عند تشغيل الجهاز/ البرنامج و ينتهي بإغلاقه.

التنصت

عندما ينطلق الـ Thread الخاص بالتنصت فإنه سينفذ إجراء التنصت الذي أسميناه سابقا ProxyListen. هذا الإجراء اتفقنا أنه إجراء لا نهائي بين التنصت و انشاء thread فرعي لكل طلب يردنا و إطلاقه. إذن فبالتأكيد سيكون هناك حلقة تكرارية من نوع While True أو في لغات أخرى Repeat Until False.

أما التنصت فيحتاج إلى Socket و لنحدد أكثر يجب أن تكون من نوع TcpListener لأنها تفي بالغرض لكنها لن تلاحظ الطلبات التي تأتي من البرامج التي تستعمل البروتوكول Udp على غرار برامج التورنت فهنا سيلزمني UdpListener.
إذن عمليا إجراء التنصت يسكون كما يلي:
  • ينصت عبر المنفذ المحدد في العنوان IP المحدد
  • حين يصله طلب يقبله و نتيجة ذلك تكون Socket يمكن استقبالها بشكل Socket بسيطة أو بشكل TcpClient.
  • ينشئ Thread لإجراء التواصل مع الطلب و يطلقه و لنفرض أن إجراء التواصل مع الطلب (معالجة الطلب) اسمه ProxyExecute يجب أن نمرر له كباراميتر الـ Socket التي قبلناها كطلب و نريد معالجتها و التواصل معها. و هكذا سيكون كل Thread يتعامل مع طلب منفصل
  • يعود للانصات من جديد.

[TABLE]
[TR]
[TD="bgcolor: #ff9900"]دالة جانبية[/TD]
[/TR]
[TR]
[TD] في VB.Net الـ IP الذي نمرره للـ TcpListenerيجب أن يكون من نوع IPaddress الذي بدوره يحتاج إلى الـ IP بشكل جدول بايتات و ليس بشكل جملة أي أن القيمة 127.0.0.1 يجب أن تمرر بشكل جدول بايتات فيه 4 خانات فيها على التوالي: 127 ثم 0 ثم 0 ثم 1. لفعل هذا الأمر سننشئ دالة نسميها GetIP مثلا تحول الجملة إلى جدول بايتات و ذلك بالشكل التالي:
  • نحول الجملة إلى جدول جمل مفصولة بالنقطة باستعمال الدالة Split التي نمرر لها جملة و حرفا فتقسم الجملة إلى جدول جمل حسب وجود ذلك
  • ننشء جدول بايتات بنفس طول جدول الجمل و الذي يفترض أن طوله هو 4 خانات
  • الحرف.نحول كل حرف من الجملة إلى بايت باستعمال الدالة CByte
الكود يكون بالشكل التالي:
[/TD]
[/TR]
[/TABLE]
إقتباس :[SIZE=2]Private [SIZE=3]Function GetIP(ByVal ipString As String) As Byte()
'1.Deviser la phrase
Dim t1() As String = ipString.Split(".")
'2.Créer un tableau de type Byte
Dim t2(t1.Length - 1) As Byte
'3. Convertir et Copier les valeurs
For i As Integer = 0 To t1.Length - 1
t2(i) = CByte(t1(i))
Next
'4.Retourner le tableau de bytes comme resultat
Return t2
End Function
[/SIZE]
[/SIZE]


إذن فكود الإجراء ProxyListen سيكون بالشكل:
إقتباس :[SIZE=2]Private Sub ProxyListen()
'1. Creer un Tableau de Byte
Dim IPTable As Byte() = GetIP(txtIP.Text)
'2. Créer une Adresse IP
Dim IP As New IPAddress(IPTable)
'3. Créer un Port
Dim port As Integer = CInt(txtPort.Text)
'4. Créer un listener
listener = New TcpListener(IP, port)
'5. Ouvrir le listener
listener.Start()
'6. Lancer une operation d'ecout indifiniment
Do While True
'6.1. Accepter chaque demander
Dim request As Socket = listener.AcceptSocket()
'6.2 Créer un Thread de traitement de la demande
Dim th As New Thread(AddressOf ProxyExecute)
'6.3 Lancer le thread avec la demande comme parametre
th.Start(request)
Loop
End Sub


[/SIZE]


معالجة الطلبات
بالنسبة للكود السابق كله مشترك بين كل البرامج و متشابه لأنه لا يقوم بأي عمل عدى استقبال الطلبات. أما كود المعالجة فهو الذي يحدد عمل البرنامج.
في برنامجنا هذا سنجعل كود المعلجة بسيطا جدا يقوم بوظيفتين هما:
  • إظهار فحوى الطلب كاملة في الائحة التي اسمينها ListResult

  • من دون معالجة الفحوى سنعيد له جوابا هو عبارة عن كود html يجعله يظهر الجملة Hello World و العنوان هو Startimes Proxy

إذن الكود html الذي سنعيده سيكون بالشكل التالي:
[TABLE="align: center"]
[TR]
[TH="bgcolor: red, align: center"]HTML[/TH]
[/TR]
[TR]
[TD][SIZE=2]<html>

<header>
<title>Startimes proxy</html>
</header>
<body>
<h1>Hello World</h1>
</body>
</html>[/TD]
[/TR]
[/TABLE]
[/SIZE]

و هنا بدل أن يتصل المتصفح و يحصل على html خاص بصفحة ما فسوف يصله كود html الذي سنعيده له و بدل أن يظهر محتوى موقع google مثلا سيظهر محتوى الصفحة التي انشأناها له.

[TABLE]
[TR]
[TD="bgcolor: #ff9900"]دالة جانبية[/TD]
[/TR]
[TR]
[TD]مع الـ Socket لا نتعامل بالجمل النصحية بل نتعامل بجداول البايتات. و لهذا وجب تحويل الكود html إلى جدول بايتات قبل إرساله. و عند استقبال طلب يجب تحويله إلى جملة أيضا.
لفعل ذلك يجب أن نكتب دالتين هما StringToBytes و BytesToString بالشكل التالي:[/TD]
[/TR]
[/TABLE]
إقتباس :[SIZE=2][SIZE=2]Private [SIZE=3]Function BytesToString(ByVal buffer() As Byte) As String
'1.Creer une chaine vide s
Dim s As String = String.Empty
'2.Pour chaque Byte dans le buffer
For i As Integer = 0 To buffer.Length - 1
'2.1.Convertir au caractère et Concatiner le dans s
s &= Chr(buffer(i))
Next
'3.Retourner la chaine s
Return s
End Function
[/SIZE]
[/SIZE]
[/SIZE]

إقتباس :[SIZE=2]Private [SIZE=3]FunctionStringToBytes(ByVal s As String) As Byte()
'1.Convertire la chaine en tableau de caractere
Dim t() As Char = s.ToCharArray
'2.Créer un tableau de Byte
Dim buffer(t.Length - 1) As Byte
For i As Integer = 0 To t.Length - 1
'3. Convertir chaque caractere en Byte par la fonction Asc (Code ascii)
buffer(i) = Asc(t(i))
Next
'4. Retourner le tableau de bytes
Return buffer
End Function
[/SIZE]
[/SIZE]


إذن كود الإجراء ProxyExecute الذي يقبل باراميترا هو الـ Socket التي سيتعامل معها ينفذ الخطوات التالية:
  • يقرأ محتوى الطلب

  • يحوله إلى نص

  • يظهر النص في سطر جديد في listConnection

  • يحول الكود Html الذي نريد أن نجعله ردا من نص إلى جدول بيتات

  • يرسل الجدول كرد

  • يثطع الإتصال (يموت هنا الـ Thread)

بلغة الـVB.Net نترجم الأمر بالشكل التالي:
إقتباس :[SIZE=2]Private Sub ProxyExecute(ByVal request As Socket)
'1. Obtenir la taill de la demande
Dim bSize As Integer = request.ReceiveBufferSize
'2. Créer un tableau de byte
Dim Requestbuffer(bSize - 1) As Byte
'3. Recevoire la demande dans le tebleau
request.Receive(Requestbuffer)
'4. Convertir le tableau à une chaine de caracteres
Dim s As String = BytesToString(Requestbuffer)
'5. Afficher la demander dans la listConnections
Me.listResult.Items.Add(s)
'6. Preparer le code html de la reponse
Dim htmlcode As String = "<html>" & _

"<header>" & _
"<title>Startimes Proxy</title>" & _
"</header>" & _
"<body>" & _
"<h1>Hello World</h1>" & _
"</body>" & _
"</html>"
'7. convertire le code html à un tableau de bytes
Dim responsebuffer() As Byte = StringToBytes(htmlcode)
'8. Envoyer la reponse
request.Send(responsebuffer)
'9. Couper la laison pour signaler la fin de la reponse
request.Disconnect(False)
End Sub


[/SIZE]
التنفيذ
نصل أخيرا لمرحلة التنفيذ فالآن نشغل البرنامج بوضع المدير لأنه سيتطلب ذلك من أجل التعديل على الريجيستري ثم نضغط زر التفعيل.
بعدها نفتح متصفحا و نكتب www.google.com مثلا أو نحاول تصفح موقع أو الضغط على رابط ما سنرى النتائج التاليةعلى مستوى المتصفحات (Internet Explorer, FireFox,Safari,Chrom,Opra)
[ATTACH=CONFIG]1961[/ATTACH]
على مستوى البرنامج
[ATTACH=CONFIG]1962[/ATTACH]
قبل الإغلاق تأكدوا من تعطيل البروكسي حتى يعيد كل شيء كما كان سابقا.
في مثالنا لم أهتم بما يريده المتصفح و قمت بارجاع نتيحة له مباشرة. في البرامج الحقيقية نقوم بالرد على المتصفح بعد دراسة طلبه عن طريق معرفة ماذا يريد فإن كان مثلا يريد موقعا ممنوعا نوجه له رسالة أو نوجهه لموقع آخر أو يمكننا أن نمرر الطلب للنت و نعيد له النتيجة الحقيقية أو مع التعديل.
في الختام أتمنى أنكم استفدتم أستودعكم الله الذي لا تضيع ودائعه و السلام عليكم و رحمة الله و بركاته
موضوع منقول من منتديات ستار تيمز لللاستاذ
[b]Samir_Aloui
[/b]









الملفات المرفقة صورة/صور
           

.zip   Proxy-Server.zip (الحجم : 75.38 ك ب / التحميلات : 80)
الرد }}}}
تم الشكر بواسطة:
#2
ياأخي ماذا أقول والله ابدعت انت ايضاً جزاك الله عنا خير الجزاء وجزاء الخير وجعله في ميزان حسناتك ياأخي العزيز

أخي ارجوا منك "فضلاً لا أمراً " أن ترفق أيضاً لو تكرمت اكواد c# وجزاك الله خيراً

انا متابع إن شاء الله


وشكرا إلك مرة اخرى
الرد }}}}
تم الشكر بواسطة:
#3
إقتباس :ياأخي ماذا أقول والله ابدعت انت ايضاً جزاك الله عنا خير الجزاء وجزاء الخير وجعله في ميزان حسناتك ياأخي العزيز

أخي ارجوا منك "فضلاً لا أمراً " أن ترفق أيضاً لو تكرمت اكواد c# وجزاك الله خيراً
بارك الله فيك وزادك من علمه
حاول اخي العزيز تحويل الاكواد من vb.netالى c#
وهذا الموقع يتيح لك تحويل الاكواد الى كلتا اللغتين
http://www.developerfusion.com/tools/con...fd7865fe0e
ونرجوا من احد الاعضاء تعديل الكود لc#
بالتوفيق
الرد }}}}
تم الشكر بواسطة:
#4
شكرا .............
الرد }}}}
تم الشكر بواسطة:


المواضيع المحتمل أن تكون متشابهة .
الموضوع : الكاتب الردود : المشاهدات : آخر رد
  إنشاء مشروع باستخدام sql server و linq وتقارير crystal report ربيع 69 12,745 10-10-15, 01:26 PM
آخر رد: ربيع
  سين جيم حول الـ Proxy Server 3amo 4 1,250 19-03-13, 05:51 PM
آخر رد: 3amo
  شرح (فيديو) رفع موقع ASP.Net وقاعدة بيانات SQL Server RaggiTech 0 887 27-10-12, 04:27 PM
آخر رد: RaggiTech
  شرح (فيديو) رفع موقع ASP.Net وقاعدة بيانات SQL Server RaggiTech 0 637 09-10-12, 06:25 PM
آخر رد: RaggiTech

التنقل السريع :


يقوم بقرائة الموضوع: بالاضافة الى ( 1 ) ضيف كريم