[COLOR="#0000FF"]بسم الله الرحمن الرحيم
وصل اللهم على محمد وآله الطاهرين
السلام عليكم ورحمة الله وبركاته[/COLOR]
--
[COLOR="#800000"]اليوم نبدأ ندخل في دروس حلوة ولطيفة وهي دروس حصرية للمنتدى ايضا
هذا الدرس يعتبر تمهيد للدرسين القادمين حيث انه في الدرس القادم راح نسوي
خدمة ويب للتعامل مع 2 يتسخدمون البرنامج (في اي مكان في العالم) او حتى اكثر من 2
سابقا قلت اني سأشرح الWCF لعمل اتصال مباشر بين جهازين (P2P) ولكن حدثت مشاكل كثيرة
مما جعلني اعتمد على الWeb Service كحل آمن وبديل ، والسطور القادمة إن شاء الله سأشرح لماذا فعلت ذلك[/COLOR]
--
[COLOR="#FF0000"]
الكثير من المقدمون على تعلم الشبكات ونظم التوزيع يعانون ويسآلون من كيفية الاتصال بجهاز اخر عبر الPublic IP مباشرة
وهي مايسمى بمدأ Peer to peer .
العتب ليس على الذي يريد التعلم وإنما العتب على مؤليفي الكتب الذين لم يوضحوا هذا الشيئ
فأكثر الشروحات والامثلة حول الشبكات هي كلها تطبق على الشبكة المحلية (LAN)
اي المتصلين معك بنفس الروتر ،ف 98% من الامثلة الموجودة على النت تتحدث ذلك .
بحثت كثيرا لاسابيع ، وجدت بعض التوضيحات والمشاكل التي تحدث عندما تريد الاتصال بشخص خارج روترك عبر ادخال الايبي العالمي (Public)[/COLOR]
--
[COLOR="#2F4F4F"]طريقة الاتصال بالايبي العالمي سهلة وغير مختلفة من الطريقة العادية المعروفة لكل من تعلم الشبكات
فلو قررنا استخدام بروتوكول TCP للإتصال بشخص على الانترنت يكفي ان تقوم بهذا [/COLOR]
برنامج العميل :
علما بأن المثال فوق لأيبي عالمي
وبرنامج السيرفر :
يكفي ان يقوم السيرفر بالنصت على بورت معين حتى يستقبل اتصال من العميل حتى لو كان العميل على شبكة اخرى
--
[COLOR="#008080"]بتقولي انا جربت الي تقول عنه آلاف المرات ولكن لايمكنني الاتصال مع اي شخص خارج شبكتي
فلماذا حصل ذلك ؟؟[/COLOR]
--
[COLOR="#FF0000"]عندما تريد الاتصال بشخص خلف الروتر (يعني جهاز موصل بروتر في شبكة غير)
لابد لك ان تحدد ايبي جهازه العالمي ، ولكن تقنية NAT اللعينة تعطي جميع الاجهزة التي خلف الروتر آيبي عالمي واحد!![/COLOR]
--
[COLOR="#006400"]تقنية الNat
الـNAT يستخدم لمشاركة Public IP واحد على مجموعة من الأجهزة الموجودة خلف هذا الـRouter ويملك كل منها Private IP بمعنى انه لو كان لديك 50 جهاز تتصل بالإنترنت من خلال الـRouter فالـ50 جهاز يتشاركون في Public IP وحيد أي ان Packet المرسل من جميع اأجهزة والتي ستخرج من الـ Router ستحتوي في جزء الـSource IP على عنوان واحد وهو الـPublic IP إذا السؤال هنا كيف يميز الـRouter عند استقبال الـResponse ان هذا الـPacket لذلك الجهاز . الحل الوحيد هنا هو استخدام الطبقة الأخرى لتميز وهي الـTransport والتي تحتوي في مكوناتها على Port المصدر وهذا البورت سيكون في الأغلب Dynamic بمعنى ان توليده سيكون من قبل نظام التشغيل عند الإرسال وسيتغير عند مروره من الراوتر ويضع الراوتر جدول يبين كل Port مستخدم بما يقابلها من عنوان ومن هنا يميز الراوتر عند استقبال الـPacket ان هذه البيانات لذلك الجهاز من خلال فحص الـPort Number , اردت ان اقول كل ذلك لأوضح نقطة هامة وهي
(لا يمكن ان يمرر الـRouter بيانات إلى جهاز موجود خلفه إلا في حالة انك قمت بإرسال الطلب اولا بحيث يعرف الـRouter في جدول الـNAT على عنوان جهازك والبورت الذي تستخدمه للستقبال) وهذا يعني ان جميع تلك البرامج لا يمكن ان تتصل مع بعضها بشكل Peer-to-Peer بدون وجود Service ترسل الطلب اليها بمعنى ان المرسل لا يمكنه ان يرسل رسالة إلى الجهاز الآخر بدون ان يتم إضافة الـPort والـIP في الـNAT Table والتي تتم بصورة اوتوماتيكية عند قيام الجهاز الآخر أي المستقبل بالإتصال مع الـService اولا.[/COLOR]
--
بما ان الكل يتشاركون بآيبي عالمي واحد
فكيف تحدد من تريد إرسال البيانات له !
الحل .. يتبع ....
--
[COLOR="#4B0082"]حتى لو كان لكل جهاز خلف الروتر آيبي عالمي محدد
فالاتصال سيكون صعبا ايضا لوجود الجدران النارية
[COLOR="#FF0000"]فالروتر له جدار ناري خاص
والوندوز له جدار ناري ايضا
ومكافح الفيروسات له جدار ناري لعين ايضا[/COLOR]
فهاؤلاء سيمنعون الاتصال باخادمك على جهاز اخر حتى لو كان الايبي العالمي هو لشخص واحد ولايشترك معاه احد .
فالحل هو فتح بورت في الروتر (port forwarding) وتضع فيه البورت الذي تريد فتحه
بجانب ايبي الجهاز المحلي الذي تريد منه ان يستقبل او يرسل البيانات على هذا البورت
فالقيام بهذا الحل يقوم بتخطي مشكلة الNAT وتوزيعها الايبي الموحد لكل الاجهزة
فالان عند تلقي اي بيانات من شبكة خارجية على البورت 455
يقوم الروتر مباشر بإرسال البيانات لجهازك
لاننا وضعنا الIp جهازنا (المحلي) في الاعدادات
والايبي المحلي لايمكن ان يشترك فيه جهازين على نفس الشبكة
لنقول للروتر : اذا استقبلت اي بيانات على بورت 455 قم بإرسالها الى ايبي جهازنا على الشبكة المحلية
وهو في مثالنا 192.168.15.9
فأي packet او بيانات قادمة من الخارج (على البورت 455) سيتم إرساله فقط للأيبي المحلي المحدد
فهكذا تجاوزنا تقنية NAT المزعجة[/COLOR]
--
[COLOR="#B22222"]ولكن هذه الطريقة صعبة على المستخدم
فهل ستقول لكل مستخدم ان يفتح بورت في جهازه ؟؟؟ََ
ربما لايعرف معلومات دخول الروتر (يوزر وباسوورد)
وايضا القيام بذلك ليس عمليا من الاساس
لو كانت هناك طريقة لفتح بورت لجميع الرواتر
لأمكننا فتح بورت في جهاز المستخدم من خلال كود في برنامجنا
وبالتالي يمكن الاتصال P2P معه
ولكن للأسف لاتوجد طريقة لفتح بورت لجميع الرواتر
إذن مالحل ...؟؟[/COLOR]
--
[COLOR="#006400"]بعد بحثي المطول في جميع المنتديات العربية والاجنبية
استنتجت 4 حلول لنستطيع التواصل مع برنامجنا في الطرف الاخر
-
[COLOR="#FF0000"]1- فتح بورت في روتر يستعمل تقنية UpNp
2- عمل شبكة افتراضية VPN بين المستخدمين
3- استخدام سيرفر وسيط[/COLOR]
4-NAT Traverse (سيوضح في مقالة اخرى)
-
سنعود بعد قليل ...
[/COLOR]
--
عدنا ..
1- فتح بورت في روتر يستعمل تقنية UpNp
بعض الرواتر تدعم هذه التقنية تقوم بفتح بورت في الوتر
وتوجد فئة خاصة تتعامل مع UpNp :
[COLOR="#800080"]
مايهمنا في هذه الفئة هي الدالة ForwardPort التي تقوم بفتح بورت في الروتر
ولكن رغم ذلك لايتعبر الحل الامثل
فابعض الروترات لاتدعم UPnP
وإنما الحديثة فقط تدعم ذلك[/COLOR]
--
[COLOR="#FF0000"]
2- استخدام شبكة افتراضية VPN[/COLOR]
[COLOR="#800080"]
VPN باعتبارها آمنة (يعني مايقدر يتحجج عليك الانتي فايروس)
وباعتبارها شبكة بين مستخدمين برنامجك . إذن يمكنك نقل البيانات واستخدامها بسهولة
بين أعضاء شبكتك . حيث سيكون لكل واحد فيهم ايبي خاص تستطيع إرسال البيانات إليه
ولكن الحصول على حساب VPN server ليس سهلا
فعليك الاشتراك بمواقع تقدم هذه الخدمة للحصول على vpn جديد ومحترم
وفي وندوز 7 هناك خاصية تخلي جهازك VPN server
ولكن يجب ان يكون جهازك شغال 24 ساعة عشان الشبكة ماتنقطع هههههههه
لأنه سيكون head of network[/COLOR]
--
3- استخدام سيرفر وسيط
[COLOR="#800080"]وهو الحل الذي افضله واغلب البرامج حاليا تستعمله
فلا حاجة لمعرفة الايبي للجهاز الذي تريد التواصل معه
ولن يقوم الروتر بمنع الاتصال (لأنك ستتصل عبر بورت 80 وهو البورت الوحيد الذي
لايمنعه الروتر - حسب معلوماتي)
المهم
من المستحيل ان يمنع الروتر الاتصال لأنك الروتر سيتصل ليس مباشرة بجاهزك !!
بل سيتصل بموقع يكون هو وسيط بينك وبينه[/COLOR]
شاهد الصورة :
[COLOR="#2F4F4F"]مثلا عنما تريد ارسال رسالة لجهاز اخر (سيرفر)
سيقوم جهازك مثل (العميل) بالاتصال بالوسيط وإرسال نص الرسالة له
فيقوم الوسيط بإعادة ارسال الرسالة الى السيرفر
يتلقى السيرفر الرسالة ويعرضها للمستخدم[/COLOR]
بسيط صح !!
[COLOR="#FF0000"]هذا الوسيط هو عبارة عن web service نصممها ونرفعها على استضافة
يمكننا تطوير الوسيط لجعله يتصل بقاعدة بيانات
فممكن للعميل ان يرسل نصا للوسيط قيقوم الوسيط بحفظ النص في قاعدة البيانات
ويقوم السيرفر على الطرف الاخر بالاتصال بالوسيط مرة اخرى والاستعلام عن النص [/COLOR]
--
[COLOR="#006400"]تقنية الWeb service جمييلة للغاية
وهي احدى تقنيات مايكروسوفت المتوفرة عن Visual studio
او Visual web developer
تقدر تعتبر الweb service كبرنامج على الويب
فمثلا يمكننا وضع دالة تقوم بجمع رقمين في الweb service
ويقوم برنامجنا على سطح المكتب بإستعامل الدالة للحصول على الناتج أو حتى لصفحة asp.net يمكنها فعل ذلك[/COLOR]
[COLOR="#800080"]فمثلا بإضافة web service قمنا بتصميها
نذهب لبرنامجنا ونقوم بإضافة web service refrance[/COLOR]
ستظهر لنا هذه النافذة
نقوم بوضع رابط الخدمة
[COLOR="#0000CD"]ممكن ان يكون على localhost او على استضافة
عشان تجرب الخدمة قم بتوصل البرنامج بها عبر localhost
[COLOR="#FF0000"]ثم اذا خلصت البرنامج وقررت تنشره للمستخديم
قم برفع الخدمة على استضافة تدعم asp
وقم باستبدال رابط الlocalHost برابط الخدمة على الويب
عشان يستطيع المستخدمون الوصول لها[/COLOR][/COLOR]
[COLOR="#B22222"]هذي الدوال الى قمت بكتابتها في ويب سيرفس
يمكنني اسدعائها وتنفيذها بسهووووولة
كأنك تتعامل مع ملف dll عادي[/COLOR]
--
[COLOR="#B22222"]لن اقول المزيد حول هذه التقنية الرائعة
بل قد خصصت لها مقالة كاملة ساضعها فيما بعد
إلي ماشاف حلقات السلسلة السابقة :
[سلسلة التعامل مع الويب] - ارسال الطلبات HttpWebRequset
[سلسلة التعامل مع الويب] تحليل الصفحات باستخدام HtmlAgilityPack
تحياتي لكم "الشاكي لله" [/COLOR]
وصل اللهم على محمد وآله الطاهرين
السلام عليكم ورحمة الله وبركاته[/COLOR]
--
[COLOR="#800000"]اليوم نبدأ ندخل في دروس حلوة ولطيفة وهي دروس حصرية للمنتدى ايضا
هذا الدرس يعتبر تمهيد للدرسين القادمين حيث انه في الدرس القادم راح نسوي
خدمة ويب للتعامل مع 2 يتسخدمون البرنامج (في اي مكان في العالم) او حتى اكثر من 2
سابقا قلت اني سأشرح الWCF لعمل اتصال مباشر بين جهازين (P2P) ولكن حدثت مشاكل كثيرة
مما جعلني اعتمد على الWeb Service كحل آمن وبديل ، والسطور القادمة إن شاء الله سأشرح لماذا فعلت ذلك[/COLOR]
--
[COLOR="#FF0000"]
الكثير من المقدمون على تعلم الشبكات ونظم التوزيع يعانون ويسآلون من كيفية الاتصال بجهاز اخر عبر الPublic IP مباشرة
وهي مايسمى بمدأ Peer to peer .
العتب ليس على الذي يريد التعلم وإنما العتب على مؤليفي الكتب الذين لم يوضحوا هذا الشيئ
فأكثر الشروحات والامثلة حول الشبكات هي كلها تطبق على الشبكة المحلية (LAN)
اي المتصلين معك بنفس الروتر ،ف 98% من الامثلة الموجودة على النت تتحدث ذلك .
بحثت كثيرا لاسابيع ، وجدت بعض التوضيحات والمشاكل التي تحدث عندما تريد الاتصال بشخص خارج روترك عبر ادخال الايبي العالمي (Public)[/COLOR]
--
[COLOR="#2F4F4F"]طريقة الاتصال بالايبي العالمي سهلة وغير مختلفة من الطريقة العادية المعروفة لكل من تعلم الشبكات
فلو قررنا استخدام بروتوكول TCP للإتصال بشخص على الانترنت يكفي ان تقوم بهذا [/COLOR]
برنامج العميل :
PHP كود :
public void Connect(string ip, int port)
{
ip = "215.232.45.22"; //public ip
_tcp = new TcpClient();
_tcp.Connect(ip,port);
MessageBox.Show("Connected");
}
علما بأن المثال فوق لأيبي عالمي
وبرنامج السيرفر :
PHP كود :
public void StartListening(object port)
{
_listner = new TcpListener(IPAddress.Any, (int)port);
_listner.Start();
while (true)
{
TcpClient client = _listner.AcceptTcpClient();
MessageBox.Show("Connected");
break;
}
}
يكفي ان يقوم السيرفر بالنصت على بورت معين حتى يستقبل اتصال من العميل حتى لو كان العميل على شبكة اخرى
--
[COLOR="#008080"]بتقولي انا جربت الي تقول عنه آلاف المرات ولكن لايمكنني الاتصال مع اي شخص خارج شبكتي
فلماذا حصل ذلك ؟؟[/COLOR]
--
[COLOR="#FF0000"]عندما تريد الاتصال بشخص خلف الروتر (يعني جهاز موصل بروتر في شبكة غير)
لابد لك ان تحدد ايبي جهازه العالمي ، ولكن تقنية NAT اللعينة تعطي جميع الاجهزة التي خلف الروتر آيبي عالمي واحد!![/COLOR]
--
[COLOR="#006400"]تقنية الNat
الـNAT يستخدم لمشاركة Public IP واحد على مجموعة من الأجهزة الموجودة خلف هذا الـRouter ويملك كل منها Private IP بمعنى انه لو كان لديك 50 جهاز تتصل بالإنترنت من خلال الـRouter فالـ50 جهاز يتشاركون في Public IP وحيد أي ان Packet المرسل من جميع اأجهزة والتي ستخرج من الـ Router ستحتوي في جزء الـSource IP على عنوان واحد وهو الـPublic IP إذا السؤال هنا كيف يميز الـRouter عند استقبال الـResponse ان هذا الـPacket لذلك الجهاز . الحل الوحيد هنا هو استخدام الطبقة الأخرى لتميز وهي الـTransport والتي تحتوي في مكوناتها على Port المصدر وهذا البورت سيكون في الأغلب Dynamic بمعنى ان توليده سيكون من قبل نظام التشغيل عند الإرسال وسيتغير عند مروره من الراوتر ويضع الراوتر جدول يبين كل Port مستخدم بما يقابلها من عنوان ومن هنا يميز الراوتر عند استقبال الـPacket ان هذه البيانات لذلك الجهاز من خلال فحص الـPort Number , اردت ان اقول كل ذلك لأوضح نقطة هامة وهي
(لا يمكن ان يمرر الـRouter بيانات إلى جهاز موجود خلفه إلا في حالة انك قمت بإرسال الطلب اولا بحيث يعرف الـRouter في جدول الـNAT على عنوان جهازك والبورت الذي تستخدمه للستقبال) وهذا يعني ان جميع تلك البرامج لا يمكن ان تتصل مع بعضها بشكل Peer-to-Peer بدون وجود Service ترسل الطلب اليها بمعنى ان المرسل لا يمكنه ان يرسل رسالة إلى الجهاز الآخر بدون ان يتم إضافة الـPort والـIP في الـNAT Table والتي تتم بصورة اوتوماتيكية عند قيام الجهاز الآخر أي المستقبل بالإتصال مع الـService اولا.[/COLOR]
--
بما ان الكل يتشاركون بآيبي عالمي واحد
فكيف تحدد من تريد إرسال البيانات له !
الحل .. يتبع ....
--
[COLOR="#4B0082"]حتى لو كان لكل جهاز خلف الروتر آيبي عالمي محدد
فالاتصال سيكون صعبا ايضا لوجود الجدران النارية
[COLOR="#FF0000"]فالروتر له جدار ناري خاص
والوندوز له جدار ناري ايضا
ومكافح الفيروسات له جدار ناري لعين ايضا[/COLOR]
فهاؤلاء سيمنعون الاتصال باخادمك على جهاز اخر حتى لو كان الايبي العالمي هو لشخص واحد ولايشترك معاه احد .
فالحل هو فتح بورت في الروتر (port forwarding) وتضع فيه البورت الذي تريد فتحه
بجانب ايبي الجهاز المحلي الذي تريد منه ان يستقبل او يرسل البيانات على هذا البورت
فالقيام بهذا الحل يقوم بتخطي مشكلة الNAT وتوزيعها الايبي الموحد لكل الاجهزة
فالان عند تلقي اي بيانات من شبكة خارجية على البورت 455
يقوم الروتر مباشر بإرسال البيانات لجهازك
لاننا وضعنا الIp جهازنا (المحلي) في الاعدادات
والايبي المحلي لايمكن ان يشترك فيه جهازين على نفس الشبكة
لنقول للروتر : اذا استقبلت اي بيانات على بورت 455 قم بإرسالها الى ايبي جهازنا على الشبكة المحلية
وهو في مثالنا 192.168.15.9
فأي packet او بيانات قادمة من الخارج (على البورت 455) سيتم إرساله فقط للأيبي المحلي المحدد
فهكذا تجاوزنا تقنية NAT المزعجة[/COLOR]
--
[COLOR="#B22222"]ولكن هذه الطريقة صعبة على المستخدم
فهل ستقول لكل مستخدم ان يفتح بورت في جهازه ؟؟؟ََ
ربما لايعرف معلومات دخول الروتر (يوزر وباسوورد)
وايضا القيام بذلك ليس عمليا من الاساس
لو كانت هناك طريقة لفتح بورت لجميع الرواتر
لأمكننا فتح بورت في جهاز المستخدم من خلال كود في برنامجنا
وبالتالي يمكن الاتصال P2P معه
ولكن للأسف لاتوجد طريقة لفتح بورت لجميع الرواتر
إذن مالحل ...؟؟[/COLOR]
--
[COLOR="#006400"]بعد بحثي المطول في جميع المنتديات العربية والاجنبية
استنتجت 4 حلول لنستطيع التواصل مع برنامجنا في الطرف الاخر
-
[COLOR="#FF0000"]1- فتح بورت في روتر يستعمل تقنية UpNp
2- عمل شبكة افتراضية VPN بين المستخدمين
3- استخدام سيرفر وسيط[/COLOR]
4-NAT Traverse (سيوضح في مقالة اخرى)
-
سنعود بعد قليل ...
[/COLOR]
--
عدنا ..
1- فتح بورت في روتر يستعمل تقنية UpNp
بعض الرواتر تدعم هذه التقنية تقوم بفتح بورت في الوتر
وتوجد فئة خاصة تتعامل مع UpNp :
PHP كود :
using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Xml;
using System.IO;
namespace UPnP
{
public delegate void Method();
public class NAT
{
public static string Discover()
{
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
string req = "M-SEARCH * HTTP/1.1" + Constants.vbCr + Constants.vbLf + "HOST: 239.255.255.250:1900" + Constants.vbCr + Constants.vbLf + "ST:upnp:rootdevice" + Constants.vbCr + Constants.vbLf + "MAN:\"ssdp:discover\"" + Constants.vbCr + Constants.vbLf + "MX:3" + Constants.vbCr + Constants.vbLf + Constants.vbCr + Constants.vbLf;
byte[] data = Encoding.ASCII.GetBytes(req);
byte[] buffer = new byte[4096];
string resp = null;
do {
s.SendTo(data, new IPEndPoint(IPAddress.Broadcast, 1900));
s.SendTo(data, new IPEndPoint(IPAddress.Broadcast, 1900));
s.SendTo(data, new IPEndPoint(IPAddress.Broadcast, 1900));
int l = s.Receive(buffer);
resp = Encoding.ASCII.GetString(buffer, 0, l);
if (resp.ToLower().Contains("upnp:rootdevice")) {
resp = resp.Substring(resp.ToLower().IndexOf("location:") + "Location:".Length);
resp = resp.Substring(0, resp.IndexOf(Constants.vbCr)).Trim();
if (resp.ToLower().Contains(".xml")) {
descUrl = resp;
GetServiceUrl();
break; // TODO: might not be correct. Was : Exit Do
}
}
} while (true);
return resp;
}
static string descUrl;
static string serviceUrl;
private static void GetServiceUrl()
{
XmlDocument desc = new XmlDocument();
desc.Load(WebRequest.Create(descUrl).GetResponse().GetResponseStream());
XmlNamespaceManager nsMgr = new XmlNamespaceManager(desc.NameTable);
nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0");
XmlNode node = desc.SelectSingleNode("//tns:service[tns:serviceType=\"urn:schemas-upnp-org:service:WANIPConnection:1\"]/tns:controlURL/text()", nsMgr);
if (node != null) {
string relurl = node.Value;
int n = 0;
if (relurl[0] == '/') {
n = descUrl.IndexOf("://");
if (n > -1) {
n = descUrl.IndexOf('/', n + 3);
if (n > -1) {
serviceUrl = descUrl.Substring(0, n) + relurl;
}
}
} else {
serviceUrl = descUrl.Substring(0, descUrl.LastIndexOf("/")) + relurl;
}
// serviceUrl = descUrl.Substring(0, descUrl.LastIndexOf("/")) + relurl;
}
if (serviceUrl == null) {
serviceUrl = GetUrl();
}
}
public static IPAddress GetExternalIP()
{
if (string.IsNullOrEmpty(serviceUrl)) {
throw new Exception("No UPnP service available or Discover() has not been called");
}
XmlDocument xdoc = SOAPRequest(serviceUrl, "<u:GetExternalIPAddress xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" + Constants.vbCr + Constants.vbLf + "</u:GetExternalIPAddress>" + Constants.vbCr + Constants.vbLf, "GetExternalIPAddress");
XmlNamespaceManager nsMgr = new XmlNamespaceManager(xdoc.NameTable);
nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0");
string IP = xdoc.SelectSingleNode("//NewExternalIPAddress/text()", nsMgr).Value;
return IPAddress.Parse(IP);
}
public static void ForwardPort(int port, ProtocolType protocol, string description)
{
if (string.IsNullOrEmpty(serviceUrl)) {
throw new Exception("No UPnP service available or Discover() has not been called");
}
XmlDocument xdoc = SOAPRequest(serviceUrl, ((((("<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" + Constants.vbCr + Constants.vbLf + "<NewRemoteHost></NewRemoteHost><NewExternalPort>") + port.ToString() + "</NewExternalPort><NewProtocol>") + protocol.ToString().ToUpper() + "</NewProtocol>" + "<NewInternalPort>") + port.ToString() + "</NewInternalPort><NewInternalClient>") + Dns.GetHostAddresses(Dns.GetHostName())[0].ToString() + "</NewInternalClient><NewEnabled>1</NewEnabled><NewPortMappingDescription>") + description + "</NewPortMappingDescription><NewLeaseDuration>0</NewLeaseDuration></u:AddPortMapping>" + Constants.vbCr + Constants.vbLf, "AddPortMapping");
}
//warning: experimental - does not work for my router
public static void DeleteForwardingRule(int port, ProtocolType protocol)
{
if (string.IsNullOrEmpty(serviceUrl)) {
throw new Exception("No UPnP service available or Discover() has not been called");
}
XmlDocument xdoc = SOAPRequest(serviceUrl, ("<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" + Constants.vbCr + Constants.vbLf + "<NewRemoteHost>" + Constants.vbCr + Constants.vbLf + "</NewRemoteHost>" + Constants.vbCr + Constants.vbLf + "<NewExternalPort>1234</NewExternalPort>" + Constants.vbCr + Constants.vbLf + "<NewProtocol>" + Constants.vbCr + Constants.vbLf) + protocol.ToString().ToUpper() + "</NewProtocol>" + Constants.vbCr + Constants.vbLf + "</u:DeletePortMapping>" + Constants.vbCr + Constants.vbLf, "DeletePortMapping");
}
public static XmlDocument GetPortMappingEntry(int index)
{
return SOAPRequest(serviceUrl, ("<u:GetGenericPortMappingEntry xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" + Constants.vbCr + Constants.vbLf + "<NewPortMappingIndex>") + index + "</NewPortMappingIndex>" + Constants.vbCr + Constants.vbLf + "</u:GetGenericPortMappingEntry>" + Constants.vbCr + Constants.vbLf, "GetGenericPortMappingEntry");
}
private static XmlDocument SOAPRequest(string url, string soap, string function)
{
string req = ("<?xml version=\"1.0\"?>" + Constants.vbCr + Constants.vbLf + "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" + Constants.vbCr + Constants.vbLf + "<s:Body>" + Constants.vbCr + Constants.vbLf) + soap + "</s:Body>" + Constants.vbCr + Constants.vbLf + "</s:Envelope>";
WebRequest r = HttpWebRequest.Create(url);
r.Method = "POST";
byte[] b = Encoding.UTF8.GetBytes(req);
r.Headers.Add("SOAPACTION", "\"urn:schemas-upnp-org:service:WANIPConnection:1#" + function + "\"");
r.ContentType = "text/xml; charset=\"utf-8\"";
r.ContentLength = b.Length;
r.GetRequestStream().Write(b, 0, b.Length);
XmlDocument resp = new XmlDocument();
WebResponse wres = r.GetResponse();
Stream ress = wres.GetResponseStream();
resp.Load(ress);
return resp;
}
private static string getServicesFromDevice(string firewallIP)
{
//To send a broadcast and get responses from all, send to 239.255.255.250
string queryResponse = "";
string query = ("M-SEARCH * HTTP/1.1" + Constants.vbCr + Constants.vbLf + "Host:") + firewallIP + ":1900" + Constants.vbCr + Constants.vbLf + "ST:upnp:rootdevice" + Constants.vbCr + Constants.vbLf + "Man:\"ssdp:discover\"" + Constants.vbCr + Constants.vbLf + "MX:3" + Constants.vbCr + Constants.vbLf + Constants.vbCr + Constants.vbLf + Constants.vbCr + Constants.vbLf;
//use sockets instead of UdpClient so we can set a timeout easier
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
try {
IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(firewallIP), 1900);
//1.5 second timeout because firewall should be on same segment (fast)
client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 1500);
byte[] q = Encoding.ASCII.GetBytes(query);
client.SendTo(q, q.Length, SocketFlags.None, endPoint);
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint senderEP = (EndPoint)sender;
byte[] data = new byte[1024];
int recv = client.ReceiveFrom(data, ref senderEP);
queryResponse = Encoding.ASCII.GetString(data);
} catch {
} finally {
if (client != null) {
client.Close();
}
}
if (queryResponse.Length == 0) {
return "";
}
string location = "";
string[] parts = queryResponse.Split(new string[] { System.Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
foreach (string part in parts) {
if (part.ToLower().StartsWith("location")) {
location = part.Substring(part.IndexOf(':') + 1);
break; // TODO: might not be correct. Was : Exit For
}
}
if (location.Length == 0) {
return "";
}
//then using the location url, we get more information:
System.Net.WebClient webClient = new WebClient();
try {
string ret = webClient.DownloadString(location);
Debug.WriteLine(ret);
return ret;
//return services
} catch (System.Exception ex) {
Debug.WriteLine(ex.Message);
} finally {
webClient.Dispose();
}
return "";
}
private static string GetFirWallIP()
{
System.Net.NetworkInformation.NetworkInterface[] nics = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();
//for each nic in computer...
foreach (System.Net.NetworkInformation.NetworkInterface nic in nics) {
try {
string machineIP = nic.GetIPProperties().UnicastAddresses[0].Address.ToString();
//send msg to each gateway configured on this nic
foreach (System.Net.NetworkInformation.GatewayIPAddressInformation gwInfo in nic.GetIPProperties().GatewayAddresses) {
try {
return gwInfo.Address.ToString();
} catch {
}
}
} catch {
}
}
}
public static string GetUrl()
{
string functionReturnValue = null;
string services = getServicesFromDevice("192.168.1.254");
if (services.Length == 0) {
return functionReturnValue;
}
int svcIndex = services.IndexOf("urn:schemas-upnp-org:service:WANPPPConnection:1");
if (svcIndex == -1) {
return functionReturnValue;
}
string controlUrl = services.Substring(svcIndex);
string tag1 = "<controlURL>";
string tag2 = "</controlURL>";
controlUrl = controlUrl.Substring(controlUrl.IndexOf(tag1) + tag1.Length);
controlUrl = controlUrl.Substring(0, controlUrl.IndexOf(tag2));
return (("http://" + GetFirWallIP() + ":") + "80/") + controlUrl.TrimStart('/');
return functionReturnValue;
}
}
}
مايهمنا في هذه الفئة هي الدالة ForwardPort التي تقوم بفتح بورت في الروتر
ولكن رغم ذلك لايتعبر الحل الامثل
فابعض الروترات لاتدعم UPnP
وإنما الحديثة فقط تدعم ذلك[/COLOR]
--
[COLOR="#FF0000"]
2- استخدام شبكة افتراضية VPN[/COLOR]
[COLOR="#800080"]
VPN باعتبارها آمنة (يعني مايقدر يتحجج عليك الانتي فايروس)
وباعتبارها شبكة بين مستخدمين برنامجك . إذن يمكنك نقل البيانات واستخدامها بسهولة
بين أعضاء شبكتك . حيث سيكون لكل واحد فيهم ايبي خاص تستطيع إرسال البيانات إليه
ولكن الحصول على حساب VPN server ليس سهلا
فعليك الاشتراك بمواقع تقدم هذه الخدمة للحصول على vpn جديد ومحترم
وفي وندوز 7 هناك خاصية تخلي جهازك VPN server
ولكن يجب ان يكون جهازك شغال 24 ساعة عشان الشبكة ماتنقطع هههههههه
لأنه سيكون head of network[/COLOR]
--
3- استخدام سيرفر وسيط
[COLOR="#800080"]وهو الحل الذي افضله واغلب البرامج حاليا تستعمله
فلا حاجة لمعرفة الايبي للجهاز الذي تريد التواصل معه
ولن يقوم الروتر بمنع الاتصال (لأنك ستتصل عبر بورت 80 وهو البورت الوحيد الذي
لايمنعه الروتر - حسب معلوماتي)
المهم
من المستحيل ان يمنع الروتر الاتصال لأنك الروتر سيتصل ليس مباشرة بجاهزك !!
بل سيتصل بموقع يكون هو وسيط بينك وبينه[/COLOR]
شاهد الصورة :
[COLOR="#2F4F4F"]مثلا عنما تريد ارسال رسالة لجهاز اخر (سيرفر)
سيقوم جهازك مثل (العميل) بالاتصال بالوسيط وإرسال نص الرسالة له
فيقوم الوسيط بإعادة ارسال الرسالة الى السيرفر
يتلقى السيرفر الرسالة ويعرضها للمستخدم[/COLOR]
بسيط صح !!
[COLOR="#FF0000"]هذا الوسيط هو عبارة عن web service نصممها ونرفعها على استضافة
يمكننا تطوير الوسيط لجعله يتصل بقاعدة بيانات
فممكن للعميل ان يرسل نصا للوسيط قيقوم الوسيط بحفظ النص في قاعدة البيانات
ويقوم السيرفر على الطرف الاخر بالاتصال بالوسيط مرة اخرى والاستعلام عن النص [/COLOR]
--
[COLOR="#006400"]تقنية الWeb service جمييلة للغاية
وهي احدى تقنيات مايكروسوفت المتوفرة عن Visual studio
او Visual web developer
تقدر تعتبر الweb service كبرنامج على الويب
فمثلا يمكننا وضع دالة تقوم بجمع رقمين في الweb service
ويقوم برنامجنا على سطح المكتب بإستعامل الدالة للحصول على الناتج أو حتى لصفحة asp.net يمكنها فعل ذلك[/COLOR]
[COLOR="#800080"]فمثلا بإضافة web service قمنا بتصميها
نذهب لبرنامجنا ونقوم بإضافة web service refrance[/COLOR]
ستظهر لنا هذه النافذة
نقوم بوضع رابط الخدمة
[COLOR="#0000CD"]ممكن ان يكون على localhost او على استضافة
عشان تجرب الخدمة قم بتوصل البرنامج بها عبر localhost
[COLOR="#FF0000"]ثم اذا خلصت البرنامج وقررت تنشره للمستخديم
قم برفع الخدمة على استضافة تدعم asp
وقم باستبدال رابط الlocalHost برابط الخدمة على الويب
عشان يستطيع المستخدمون الوصول لها[/COLOR][/COLOR]
[COLOR="#B22222"]هذي الدوال الى قمت بكتابتها في ويب سيرفس
يمكنني اسدعائها وتنفيذها بسهووووولة
كأنك تتعامل مع ملف dll عادي[/COLOR]
--
[COLOR="#B22222"]لن اقول المزيد حول هذه التقنية الرائعة
بل قد خصصت لها مقالة كاملة ساضعها فيما بعد
إلي ماشاف حلقات السلسلة السابقة :
[سلسلة التعامل مع الويب] - ارسال الطلبات HttpWebRequset
[سلسلة التعامل مع الويب] تحليل الصفحات باستخدام HtmlAgilityPack
تحياتي لكم "الشاكي لله" [/COLOR]