27-11-13, 05:27 PM
(آخر تعديل لهذه المشاركة : 11-09-19, 12:27 AM {2} بواسطة الشاكي لله.)
بسم الله الرحمن الرحيم
السلام عليكم ورحمة الله وبركاته
والصلاة والسلام على اشرف الخلق محمد وعلى اله الطيبين الطاهرين
--
اهلا وسهلا بكم اخواني في المقالة الرابعة من [سلسلة التحكم]
صفر :- اجزاء المقالة
اولا :- كتابة Remote object
ثانيا :- كتابة Web Service
ثالثا :- كتابة برنامج Server
رابعا :- كتابة برنامج Client
خامسا :- فيديو نتيجة الدرس (من تصويري)
سادسا :- المراجع & الخاتمة
اولا :- Remote object
الRemote object هو مصطلح يعلمه كل من تابع المقالة الثانية من هذه السلسلة
هذا الRemote object يحتوي على دوال تتحكم في الحاسوب فور استدعائها
ولكن من الذي يستدعي هذه الدالة ؟؟
البرنامج Client هو من يعطي اشارة للServer ليقوم باستدعاء الدالة من الRemote object
فيتم اسدعاء الدالة وتنفيذها على الServer ، كل هذا تم بأمر من برنامج الClient.
في البداية علينا إنشاء Remote object وهو عبارة عن Class library
--
خطوات انشاء Remote object :-
1-
سنسمي الRemote object باسم Control وسنقوم بانشاء مشروع جديد من نوع Class Library
2-
دائما اول شيئ تفكر فيه هو "الاوامر" ، فتسأل نفسك : ماهي الاوامر التي اريد من الServer ان ينفذها ؟؟
وتكتب كل الاوامر داخل enum
الاوامر التي اريد من الجهاز الاخر (Server) ان ينفذها هي :
- اطفاء الحاسوب ، عرض رسالة ، تشغيل صوت ، اضافة قيمة للحافظة ، جلب قيمة الحافظة ، جلب صورة سطح المكتب الحالية - جلب اسم جهاز السيرفر
هذه الاوامر التي خطرت في بالي فقمت بكتابتها في enum.
3-
الان علينا كتابة دالة لكل امر كتبته فوق :-
--
وهكذا قمنا بكتابة الدوال التي ستقوم بتنفيذ طموحاتنا على جهاز الServer
والكود النهائي :-
ثانيا :- كتابة Web Service
الان يجب علينا التفكير مليا ... ماهي الطريقة التي سنستعملها للتحكم بالجهاز الاخر ؟؟
هناك طريقة ذكية وسنستعملها في هذه المقالة وهي "الرسائل" .
مبدأها يشبه مبدأ الChatting تماااااما ، حيث انك ترسل من برنامجك Client الى البرنامج الاخر (Server) الرقم 3
فيقوم الServer باستدعاء الدالة TurnOffComputer مثلا ... نفس الشيئ لو ارسلت الرقم 7 ، سيقوم السيرفر باستدعاء GetScreenShot.
اذن النظام الذي سنستخدمه هو الرسائل . فالان علينا التفكير في طريقة لنقل الرسائل من عندنا الى الطرف الاخر !
الجواب هو باستعمال خدمة الويب كأحد طرق نقل الرسائل ...
فالطريقة بالتفصيل ستكون بهذه الكيفية :-
1- اولا يقوم البرنامج الClient بارسال الرقم 1 مع اسم السيرفر الهدف الى خدمة الويب
2- تقوم خدمة الويب بحفظ الرقم + اسم الserver في مكان مخصص في الذاكرة
3- في برنامج الServer يوجد timer يعمل كل 3 ثواني مثلا .. وظيفته هو فحص هذا المكان في الذاكرة للبحث عن الجديد
4- فاذا عثر على الرقم 1 في المكان المخصص ... يقوم بترجمة الرقم ويفهمه على انه استدعاء لدالة معينة
طبعا نظام الارقام صار قديم في البرمجة .. الان يتم استخدام enum ، لذلك قمنا في بداية المقالة بانشاء enum لتسهيل الموضوع
--
نقوم اولا بإنشاء WebService ونسميها WebKernel مثلا او اي اسم يحلو لك
ثم نقوم بادراج الRemote object في هذا المشروع عبر :-
بعدها نقوم بكتابة دالة يطلبها الClient لكي تخزن رقم في الذاكرة
Application.Set هي دالة تقوم بحفظ بيانات لفترة قدرها نصف ساعة تقريبا ثم تحذف .
هي تستقبل بارمترين .. الاول هو key ، والاخر هو data
اي انها شبيهة جدا بHashtable او Dictionary
طبعا اعلم ان الكود يسوده الغموض وهذا طبيعي لأن البرنامج يعتمد على نمط لايعلم عنه سوا كاتب الكود .
الدالة المقابلة والتي تقوم باسترجاع البيانات من الذاكرة هي
Application.Get تستقبل بارمتار واحد وهو key .. وسترجع لك الdata المحفوظة على هذا الkey
كما قلت سابقا مبدأها يشبه مبدا HashTable فالي فاهمنه مابواجه مشكلة.
بقية الكود هي دوال مساعدة :-
ستفهم جميع هذه الدوال في النهاية اذا ركزت في الجزئين التاليين .
ولكن عليك ان تقوم برفع الخدمة الى استضافة كي نستطيع التحكم عن بعد
ثانيا :- كتابة Server
دائما اغلب مبرمجي تطبيقات (Client/server) يقومون بكتابة الserver اولا
فنحن بدورنا سنقوم بفعل ذلك ، لذلك قم بإنشاء مشروع جديد في نفس السلوشن
ومن نوع Windows App واسمه Server :
اولا علينا اضافة المراجع الضرورية ك Remote object الي سويناها في الجزء الاول
وخدمة الويب المرفوعة على استضافة .
بعدها نعرف متغير عام على المشروع يمثل الclient تبع الخدمة
ومتغير اخر عام نحفظ فيه اسمنا
قد تقول معليش انا فهمت ليش سويت متغير يعبر عن الخدمة وذلك لكي تستطيع استعمال دوال خدمة الويب
ولكن لم افهم ليش سويت متغير _MyName ؟؟؟
طبيعي في اي برنامج server/client ان يكون هناك عنوان فريد يدل على الاخر
فكيف يستطيع الclient ان يتصل بالسيرفر بدون عنوانه ؟
العنوان في العادة يكون عبارة عن IP Address ولكن باستعمال خدمة الويب يمكنك الاستغناء عن الايبي
واضافة اي اسم كي يستطيع الclient الوصول له ...
فنحن سنكتب اسمنا في الtextbox وثم ناخذ قيمته ونضعه في المتغير العام MyName .
والان كود الButtonOK هو :-
--
شرح الكود :-
في السطر الاول : نقوم بفحص الاسم المدخل ، هل يستخدمه شخص اخر ام لا ؟ فكما لايمكن ان يتشابه IP address على وجه الارض
كذلك لايمكننا ان ندع الاسماء تتشابه كي نستلم الرسائل من الجهة الصحيحة .
بقية الاسطر : مفهومة وواضحة .
السطر الاخير : تشغيل تايمر لفحص الاوامر الجديدة . فهذا تايمر يعمل كل 3 ثواني ويقوم بالاستعلام عن اخر الاوامر
وكوده هو :-
الكود مشروح بالتعليقات الخضراء
ولكن اهم شيئ فيه هو المتغير CMD وهو يعبر عن الامر المرسل من الClient
فإذا كان الامر الذي ارسله Client يساوي Commands.TurnOffPC قم باستدعاء دالة اغلاق الحاسوب
ونفس الشيئ للبقية .
--
الكود كامل :-
الان نذهب لتصميم الClient فيقوم بارسال امر الى الخدمة الويب ، فتقوم خدمة الويب بحفظها في ذاكرة الHost لمدة قدرها تقريبا نصف ساعة
طبعا كما ذكرنا في الفقرة السابقة ان الserver سيقوم كل 3 ثواني بفحص ذاكرة الHost للحصول على جديد الاوامر .
رابعا :- كتابة Client
كما قلنا سابقا سنقوم بعمل برنامج Client يقوم بارسال امر الى الخدمة ويقوم الServer بالاستعلام من الخدمة .
سنقوم بإنشاء برنامج Client في نفس السلوشن ايضا ، وهو من نوع Windows App وباسم Client :
كلعادة نقوم باضافة الموارد اللازمة (خدمة الويب + الRemote object)
والان اصبح السلوشن بهذا الشكل :
الان نبدأ البرمجة ونقول بتعريف المتغيرات العامة :-
ونبرمج زر OK بنفس طريقة الServer :-
والان نقوم بكتابة كود الازرار ال6 :-
استخدمت في الكود الدالة SendCommandTo هي لكي ترسل امر الى الخدمة
والدالة SendCommandAndWaitResult لارسال امر وانتظار النتيجة التي سيرجعها الServer
فمثلا هذا الزر يتطلب ارسال امر وجلب نتيجة تنفيذه :
فهذا الزر يتطلب استعمال دالة SendCommandAndWaitResult لانتظار النتيجة
بعد وصول النتيجة سيتم عرضها على فورم اخر .
ولكن هناك شيئ مهم جدا يخص Get screen shot ، فكما نعلم من اسم الزر انه امر لجلب صورة سطح المكتب للسيرفر
وهذا يتطلب نقل الصورة من الserver الى client ، ولكن انتبه فلا يجوز نقل صورة حجمها اكبر من 60 كيلو بايت !!!
60 كيلو بايت !!! . هي مساحة صغيرة جدا واغلب الصور تكون اكبر من هذا الحجم فما الحل ؟؟
الحل بسيط اكثر مماتتخيل . فقط قم بالدخول الى App.config والخاص بالمشروع Client الذي نعمل عليه حاليا
وقم بالتالي :-
كما ترا قمت باضافة maxReceivedMessageSize لزيادة حجم اقصى حد لاستقبال الرسائل
ووضعت قيمته 2000000 مما يساوي 2 ميغا بايت
ومستحيل صورة سطح المكتب التي سيتم التقاطها بتنسيق jpg ان يتجاوز حجمها ال700 كيلو ولكن للاحتطيات فقط وضعت 2 مب.
الكود كامل :-
خامسا :- فيديو نتيجة الدرس (من تصويري)
او من على اليوتيوب مباشرة
سابعا :- المراجع & الخاتمة
المراجع المعتمد عليها في هذه المقالة :-
[سلسلة التحكم] {3} - خدمة الويب (Web Service)
--
أتمنى أن تستفيدو من هذه المقالة
اعلم ان بعضكم قد يتسآل ويقول "مادام مبدأك نقل رسائل فقط ليش استعملت WebService ولم تقم بإرسال الرسائل مباشرة للServer" ؟
في الواقع الاتصال المباشر بين طرفين يسمى Peer2Peer وهو اتصال سهل يمكنك عمله بسهولة ولكن ...
لو كان الطرفان بعيدان وعلى شبكة مختلفة ، أعلم ان Peer2Peer ستواجه المصاعب والمآسي ولن يتم الاتصال
وكل هذا بسبب الجدران النارية والNAT ، ولكن في الاجزاء القادمة سنقوم بكشف الستار عن اسرار الاتصال المباشر
وسنقوم بعمل برنامج مثل TeamViewer يقوم بالتحكم الكامل عن بعد بواسطة Peer2Peer ولن نقوم بالاستعان بطرف ثالث (Third party).
نلقاكم بخير
إنتهى
[/size]
السلام عليكم ورحمة الله وبركاته
والصلاة والسلام على اشرف الخلق محمد وعلى اله الطيبين الطاهرين
--
اهلا وسهلا بكم اخواني في المقالة الرابعة من [سلسلة التحكم]
صفر :- اجزاء المقالة
اولا :- كتابة Remote object
ثانيا :- كتابة Web Service
ثالثا :- كتابة برنامج Server
رابعا :- كتابة برنامج Client
خامسا :- فيديو نتيجة الدرس (من تصويري)
سادسا :- المراجع & الخاتمة
اولا :- Remote object
الRemote object هو مصطلح يعلمه كل من تابع المقالة الثانية من هذه السلسلة
هذا الRemote object يحتوي على دوال تتحكم في الحاسوب فور استدعائها
ولكن من الذي يستدعي هذه الدالة ؟؟
البرنامج Client هو من يعطي اشارة للServer ليقوم باستدعاء الدالة من الRemote object
فيتم اسدعاء الدالة وتنفيذها على الServer ، كل هذا تم بأمر من برنامج الClient.
في البداية علينا إنشاء Remote object وهو عبارة عن Class library
--
خطوات انشاء Remote object :-
1-
سنسمي الRemote object باسم Control وسنقوم بانشاء مشروع جديد من نوع Class Library
2-
دائما اول شيئ تفكر فيه هو "الاوامر" ، فتسأل نفسك : ماهي الاوامر التي اريد من الServer ان ينفذها ؟؟
وتكتب كل الاوامر داخل enum
PHP كود :
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Control
{
public class Controls
{
public enum Commands
{
TurnOffPC,
ViewMessage,
PlayBeebs,
SetClipboard,
GetClipboard,
GetScreenShot,
GetPCName
}
}
}
الاوامر التي اريد من الجهاز الاخر (Server) ان ينفذها هي :
- اطفاء الحاسوب ، عرض رسالة ، تشغيل صوت ، اضافة قيمة للحافظة ، جلب قيمة الحافظة ، جلب صورة سطح المكتب الحالية - جلب اسم جهاز السيرفر
هذه الاوامر التي خطرت في بالي فقمت بكتابتها في enum.
3-
الان علينا كتابة دالة لكل امر كتبته فوق :-
PHP كود :
public void TurnOffPC()
{
System.Diagnostics.Process.Start("shutdown", "-s -t 1");
}
public void ViewMessage(string message)
{
MessageBox.Show(message, "Message From Client", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
public void PlayBeebs()
{
System.Media.SystemSounds.Beep.Play();
}
public void SetCliboard(string str)
{
Clipboard.SetText(str);
}
public string GetCliboard()
{
try
{
return Clipboard.GetText();
}
catch { return ""; }
}
public byte[] GetScreenShot()
{
try
{
var bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
Screen.PrimaryScreen.Bounds.Height,
PixelFormat.Format32bppArgb);
// Create a graphics object from the bitmap.
var gfxScreenshot = Graphics.FromImage(bmpScreenshot);
// Take the screenshot from the upper left corner to the right bottom corner.
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X,
Screen.PrimaryScreen.Bounds.Y,
0,
0,
Screen.PrimaryScreen.Bounds.Size,
CopyPixelOperation.SourceCopy);
// Save the screenshot to the specified path that the user has chosen.
using (var ms = new System.IO.MemoryStream())
{
bmpScreenshot.Save(ms, ImageFormat.Png);
return ms.ToArray();
}
}
catch { return null; }
}
public string GetPCName()
{
try
{
return System.Environment.MachineName;
}
catch { return ""; }
}
--
وهكذا قمنا بكتابة الدوال التي ستقوم بتنفيذ طموحاتنا على جهاز الServer
والكود النهائي :-
PHP كود :
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Control
{
public class Controls
{
public enum Commands
{
TurnOffPC,
ViewMessage,
PlayBeebs,
SetClipboard,
GetClipboard,
GetScreenShot,
GetPCName
}
public void TurnOffPC()
{
System.Diagnostics.Process.Start("shutdown", "-s -t 1");
}
public void ViewMessage(string message)
{
MessageBox.Show(message, "Message From Client", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
public void PlayBeebs()
{
System.Media.SystemSounds.Beep.Play();
}
public void SetCliboard(string str)
{
Clipboard.SetText(str);
}
public string GetCliboard()
{
try
{
return Clipboard.GetText();
}
catch { return ""; }
}
public byte[] GetScreenShot()
{
try
{
var bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
Screen.PrimaryScreen.Bounds.Height,
PixelFormat.Format32bppArgb);
// Create a graphics object from the bitmap.
var gfxScreenshot = Graphics.FromImage(bmpScreenshot);
// Take the screenshot from the upper left corner to the right bottom corner.
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X,
Screen.PrimaryScreen.Bounds.Y,
0,
0,
Screen.PrimaryScreen.Bounds.Size,
CopyPixelOperation.SourceCopy);
// Save the screenshot to the specified path that the user has chosen.
using (var ms = new System.IO.MemoryStream())
{
bmpScreenshot.Save(ms, ImageFormat.Png);
return ms.ToArray();
}
}
catch { return null; }
}
public string GetPCName()
{
try
{
return System.Environment.MachineName;
}
catch { return ""; }
}
}
}
ثانيا :- كتابة Web Service
الان يجب علينا التفكير مليا ... ماهي الطريقة التي سنستعملها للتحكم بالجهاز الاخر ؟؟
هناك طريقة ذكية وسنستعملها في هذه المقالة وهي "الرسائل" .
مبدأها يشبه مبدأ الChatting تماااااما ، حيث انك ترسل من برنامجك Client الى البرنامج الاخر (Server) الرقم 3
فيقوم الServer باستدعاء الدالة TurnOffComputer مثلا ... نفس الشيئ لو ارسلت الرقم 7 ، سيقوم السيرفر باستدعاء GetScreenShot.
اذن النظام الذي سنستخدمه هو الرسائل . فالان علينا التفكير في طريقة لنقل الرسائل من عندنا الى الطرف الاخر !
الجواب هو باستعمال خدمة الويب كأحد طرق نقل الرسائل ...
فالطريقة بالتفصيل ستكون بهذه الكيفية :-
1- اولا يقوم البرنامج الClient بارسال الرقم 1 مع اسم السيرفر الهدف الى خدمة الويب
2- تقوم خدمة الويب بحفظ الرقم + اسم الserver في مكان مخصص في الذاكرة
3- في برنامج الServer يوجد timer يعمل كل 3 ثواني مثلا .. وظيفته هو فحص هذا المكان في الذاكرة للبحث عن الجديد
4- فاذا عثر على الرقم 1 في المكان المخصص ... يقوم بترجمة الرقم ويفهمه على انه استدعاء لدالة معينة
طبعا نظام الارقام صار قديم في البرمجة .. الان يتم استخدام enum ، لذلك قمنا في بداية المقالة بانشاء enum لتسهيل الموضوع
--
نقوم اولا بإنشاء WebService ونسميها WebKernel مثلا او اي اسم يحلو لك
إقتباس :خطوات إنشاء خدمة الويب :-
1- إنشاء مشروع جديد من نوع ASP.NET Empty Web Application
2- قم بإضافة WebService من خلال Project > Add new item > Web Service
خطوات إنشاء خدمة الويب (بالصور) :-
1
2
ثم نقوم بادراج الRemote object في هذا المشروع عبر :-
بعدها نقوم بكتابة دالة يطلبها الClient لكي تخزن رقم في الذاكرة
PHP كود :
[WebMethod]
public void SendCommandTo(string SenderName, string ReceiverName, Controls.Commands cmd, string addtionalData)
{
Application.Set(ReceiverName + "|CMD", ((int)cmd).ToString());
Application.Set(ReceiverName + "|ADD", addtionalData);
Application.Set(ReceiverName + "|Sender", SenderName);
}
Application.Set هي دالة تقوم بحفظ بيانات لفترة قدرها نصف ساعة تقريبا ثم تحذف .
هي تستقبل بارمترين .. الاول هو key ، والاخر هو data
اي انها شبيهة جدا بHashtable او Dictionary
طبعا اعلم ان الكود يسوده الغموض وهذا طبيعي لأن البرنامج يعتمد على نمط لايعلم عنه سوا كاتب الكود .
الدالة المقابلة والتي تقوم باسترجاع البيانات من الذاكرة هي
PHP كود :
[WebMethod]
public string[] ReceiveCommand(string name)
{
try
{
string[] obj = new string[3];
obj[0] = (string)Application.Get(name + "|CMD");
obj[1] = (string)Application.Get(name + "|ADD");
obj[2] = (string)Application.Get(name + "|Sender");
return obj;
}
catch { return null; }
}
Application.Get تستقبل بارمتار واحد وهو key .. وسترجع لك الdata المحفوظة على هذا الkey
كما قلت سابقا مبدأها يشبه مبدا HashTable فالي فاهمنه مابواجه مشكلة.
بقية الكود هي دوال مساعدة :-
PHP كود :
using Control;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
namespace WebKernel
{
/// <summary>
/// Summary description for MyService
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class MyService : System.Web.Services.WebService
{
[WebMethod]
public void SendCommandTo(string SenderName, string ReceiverName, Controls.Commands cmd, string addtionalData)
{
Application.Set(ReceiverName + "|CMD", ((int)cmd).ToString());
Application.Set(ReceiverName + "|ADD", addtionalData);
Application.Set(ReceiverName + "|Sender", SenderName);
}
[WebMethod]
public object SendCommandAndWaitResult(string SenderName, string ReceiverName, Controls.Commands cmd)
{
Application.Set(ReceiverName + "|CMD", ((int)cmd).ToString());
Application.Set(ReceiverName + "|ADD", null);
Application.Set(ReceiverName + "|Sender", SenderName);
//wait for result
while (Application.Get(SenderName) == null) { }
object result = Application.Get(SenderName);
Application.Remove(SenderName);
return result;
}
[WebMethod]
public void ReturnResultToClient(string name, object result)
{
Application.Set(name, result);
}
[WebMethod]
public string[] ReceiveCommand(string name)
{
try
{
string[] obj = new string[3];
obj[0] = (string)Application.Get(name + "|CMD");
obj[1] = (string)Application.Get(name + "|ADD");
obj[2] = (string)Application.Get(name + "|Sender");
return obj;
}
catch { return null; }
}
[WebMethod]
public void RemoveRegisterName(string name)
{
try
{
Application.Remove(name + "|CMD");
Application.Remove(name + "|ADD");
Application.Remove(name + "|Sender");
}
catch { }
}
[WebMethod]
public bool CheckNameExist(string name)
{
if (Application[name + "|Alive"] != null)
{
return true;
}
else
{
Application.Set(name + "|Alive", -1);
return false;
}
}
}
}
ستفهم جميع هذه الدوال في النهاية اذا ركزت في الجزئين التاليين .
ولكن عليك ان تقوم برفع الخدمة الى استضافة كي نستطيع التحكم عن بعد
ثانيا :- كتابة Server
دائما اغلب مبرمجي تطبيقات (Client/server) يقومون بكتابة الserver اولا
فنحن بدورنا سنقوم بفعل ذلك ، لذلك قم بإنشاء مشروع جديد في نفس السلوشن
ومن نوع Windows App واسمه Server :
اولا علينا اضافة المراجع الضرورية ك Remote object الي سويناها في الجزء الاول
وخدمة الويب المرفوعة على استضافة .
بعدها نعرف متغير عام على المشروع يمثل الclient تبع الخدمة
ومتغير اخر عام نحفظ فيه اسمنا
PHP كود :
Service.MyServiceSoapClient _serviceClient = new Service.MyServiceSoapClient();
private string _MyName = string.Empty;
قد تقول معليش انا فهمت ليش سويت متغير يعبر عن الخدمة وذلك لكي تستطيع استعمال دوال خدمة الويب
ولكن لم افهم ليش سويت متغير _MyName ؟؟؟
طبيعي في اي برنامج server/client ان يكون هناك عنوان فريد يدل على الاخر
فكيف يستطيع الclient ان يتصل بالسيرفر بدون عنوانه ؟
العنوان في العادة يكون عبارة عن IP Address ولكن باستعمال خدمة الويب يمكنك الاستغناء عن الايبي
واضافة اي اسم كي يستطيع الclient الوصول له ...
فنحن سنكتب اسمنا في الtextbox وثم ناخذ قيمته ونضعه في المتغير العام MyName .
والان كود الButtonOK هو :-
PHP كود :
private void OKbutton_Click(object sender, EventArgs e)
{
if (_serviceClient.CheckNameExist(textBox1.Text.Trim()))
{
MessageBox.Show("Choose another name", "Error : name exist", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
_MyName = textBox1.Text.Trim();
textBox1.Enabled = false;
OKbutton.Enabled = false;
StatusLable.Text = "Waiting for commands ...";
ServerTimer.Start();
}
--
شرح الكود :-
في السطر الاول : نقوم بفحص الاسم المدخل ، هل يستخدمه شخص اخر ام لا ؟ فكما لايمكن ان يتشابه IP address على وجه الارض
كذلك لايمكننا ان ندع الاسماء تتشابه كي نستلم الرسائل من الجهة الصحيحة .
بقية الاسطر : مفهومة وواضحة .
السطر الاخير : تشغيل تايمر لفحص الاوامر الجديدة . فهذا تايمر يعمل كل 3 ثواني ويقوم بالاستعلام عن اخر الاوامر
وكوده هو :-
PHP كود :
private async void ServerTimer_Tick(object sender, EventArgs e)
{
//الاستعلام عن اخر امر
Task<ReceiveCommandResponse> cmdTask = _serviceClient.ReceiveCommandAsync(_MyName);
ReceiveCommandResponse cmdResb = await cmdTask;
Service.ArrayOfString arr = cmdResb.Body.ReceiveCommandResult;
if (arr[0] == null) { return; }
Control.Controls controlObj = new Control.Controls();
//حذف اخر من ذاكرة السيرفر
await _serviceClient.RemoveRegisterNameAsync(_MyName);
//استخلاص الامر والعنوان من الاستعلام
Commands CMD = (Commands)int.Parse(arr[0]);
string addtionalInfo = (string)arr[1];
string senderName = (string)arr[2];
//اذا كان الامر المرسل يساوي كذا قم باستدعاء الدالة المناسبة
switch (CMD)
{
case Commands.TurnOffPC:
controlObj.TurnOffPC();
break;
case Commands.ViewMessage:
controlObj.ViewMessage(addtionalInfo);
break;
case Commands.PlayBeebs:
controlObj.PlayBeebs();
break;
case Commands.SetClipboard:
controlObj.SetCliboard(addtionalInfo);
break;
case Commands.GetClipboard:
string data0 = controlObj.GetCliboard();
await _serviceClient.ReturnResultToClientAsync(senderName, data0);
break;
case Commands.GetScreenShot:
byte[] data1 = controlObj.GetScreenShot();
await _serviceClient.ReturnResultToClientAsync(senderName, data1);
break;
case Commands.GetPCName:
string data2 = controlObj.GetPCName();
await _serviceClient.ReturnResultToClientAsync(senderName, data2);
break;
}
}
الكود مشروح بالتعليقات الخضراء
ولكن اهم شيئ فيه هو المتغير CMD وهو يعبر عن الامر المرسل من الClient
فإذا كان الامر الذي ارسله Client يساوي Commands.TurnOffPC قم باستدعاء دالة اغلاق الحاسوب
ونفس الشيئ للبقية .
--
الكود كامل :-
PHP كود :
using Server.Service;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Server
{
public partial class ServerForm : Form
{
Service.MyServiceSoapClient _serviceClient = new Service.MyServiceSoapClient();
private string _MyName = string.Empty;
public ServerForm()
{
InitializeComponent();
}
private void OKbutton_Click(object sender, EventArgs e)
{
if (_serviceClient.CheckNameExist(textBox1.Text.Trim()))
{
MessageBox.Show("Choose another name", "Error : name exist", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
_MyName = textBox1.Text.Trim();
textBox1.Enabled = false;
OKbutton.Enabled = false;
StatusLable.Text = "Waiting for commands ...";
ServerTimer.Start();
}
private async void ServerTimer_Tick(object sender, EventArgs e)
{
//الاستعلام عن اخر امر
Task<ReceiveCommandResponse> cmdTask = _serviceClient.ReceiveCommandAsync(_MyName);
ReceiveCommandResponse cmdResb = await cmdTask;
Service.ArrayOfString arr = cmdResb.Body.ReceiveCommandResult;
if (arr[0] == null) { return; }
Control.Controls controlObj = new Control.Controls();
//حذف اخر من ذاكرة السيرفر
await _serviceClient.RemoveRegisterNameAsync(_MyName);
//استخلاص الامر والعنوان من الاستعلام
Commands CMD = (Commands)int.Parse(arr[0]);
string addtionalInfo = (string)arr[1];
string senderName = (string)arr[2];
//اذا كان الامر المرسل يساوي كذا قم باستدعاء الدالة المناسبة
switch (CMD)
{
case Commands.TurnOffPC:
controlObj.TurnOffPC();
break;
case Commands.ViewMessage:
controlObj.ViewMessage(addtionalInfo);
break;
case Commands.PlayBeebs:
controlObj.PlayBeebs();
break;
case Commands.SetClipboard:
controlObj.SetCliboard(addtionalInfo);
break;
case Commands.GetClipboard:
string data0 = controlObj.GetCliboard();
await _serviceClient.ReturnResultToClientAsync(senderName, data0);
break;
case Commands.GetScreenShot:
byte[] data1 = controlObj.GetScreenShot();
await _serviceClient.ReturnResultToClientAsync(senderName, data1);
break;
case Commands.GetPCName:
string data2 = controlObj.GetPCName();
await _serviceClient.ReturnResultToClientAsync(senderName, data2);
break;
}
}
}
}
الان نذهب لتصميم الClient فيقوم بارسال امر الى الخدمة الويب ، فتقوم خدمة الويب بحفظها في ذاكرة الHost لمدة قدرها تقريبا نصف ساعة
طبعا كما ذكرنا في الفقرة السابقة ان الserver سيقوم كل 3 ثواني بفحص ذاكرة الHost للحصول على جديد الاوامر .
رابعا :- كتابة Client
كما قلنا سابقا سنقوم بعمل برنامج Client يقوم بارسال امر الى الخدمة ويقوم الServer بالاستعلام من الخدمة .
سنقوم بإنشاء برنامج Client في نفس السلوشن ايضا ، وهو من نوع Windows App وباسم Client :
كلعادة نقوم باضافة الموارد اللازمة (خدمة الويب + الRemote object)
والان اصبح السلوشن بهذا الشكل :
الان نبدأ البرمجة ونقول بتعريف المتغيرات العامة :-
PHP كود :
Service.MyServiceSoapClient _serviceClient = new Service.MyServiceSoapClient();
private string _ServerName = string.Empty;
private string _MyName = string.Empty;
ونبرمج زر OK بنفس طريقة الServer :-
PHP كود :
private void OKbutton_Click(object sender, EventArgs e)
{
if (_serviceClient.CheckNameExist(textBox1.Text.Trim()))
{
MessageBox.Show("Choose another name (My Name)", "Error : name exist", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
groupBox1.Enabled = false;
groupBox2.Enabled = true;
_MyName = textBox1.Text.Trim();
_ServerName = textBox2.Text.Trim();
}
والان نقوم بكتابة كود الازرار ال6 :-
PHP كود :
private void SendMessageBtn_Click(object sender, EventArgs e)
{
string str = Interaction.InputBox("Enter your message to server");
if (str != string.Empty)
{
//ارسال امر
_serviceClient.SendCommandTo(_MyName, _ServerName, Service.Commands.ViewMessage, str);
}
}
private void SendBeebsBtn_Click(object sender, EventArgs e)
{
_serviceClient.SendCommandTo(_MyName, _ServerName, Service.Commands.PlayBeebs, null);
}
private void SendClipboardBtn_Click(object sender, EventArgs e)
{
string str = Interaction.InputBox("Enter your text to save it in server clipboard");
if (str != string.Empty)
{
_serviceClient.SendCommandTo(_MyName, _ServerName, Service.Commands.SetClipboard, str);
}
}
private void GetDataClipboardBtn_Click(object sender, EventArgs e)
{
//ارسال امر وانتظار النتيجةSendCommandAndWaitResult
string savedClipBoard = (string)_serviceClient.SendCommandAndWaitResult(_MyName,_ServerName,
Service.Commands.GetClipboard);
MessageBox.Show("The content of server clipboard is : " + savedClipBoard, "Server Clipboard",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void ScreenshotBtn_Click(object sender, EventArgs e)
{
//ارسال امر جلب الصورة وانتظار الصورة
byte[] serverShot = (byte[])_serviceClient.SendCommandAndWaitResult(_MyName, _ServerName,
Service.Commands.GetScreenShot);
using (System.IO.MemoryStream ms = new System.IO.MemoryStream(serverShot))
{
Image shot = Image.FromStream(ms);
ScreenShotForm shotForm = new ScreenShotForm();
shotForm.pictureBox1.BackgroundImage = shot;
shotForm.Show();
ms.Close();
}
}
private void MachinenameBtn_Click(object sender, EventArgs e)
{
//ارسال امر جلب اسم جهاز السيرفر وانظار النتيجة
string serverMachineName = (string)_serviceClient.SendCommandAndWaitResult(_MyName, _ServerName,
Service.Commands.GetPCName);
MessageBox.Show("The server machine name is : " + serverMachineName, "Server Machine Name",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void ShutdownBtn_Click(object sender, EventArgs e)
{
_serviceClient.SendCommandTo(_MyName, _ServerName, Service.Commands.TurnOffPC,null);
}
استخدمت في الكود الدالة SendCommandTo هي لكي ترسل امر الى الخدمة
والدالة SendCommandAndWaitResult لارسال امر وانتظار النتيجة التي سيرجعها الServer
فمثلا هذا الزر يتطلب ارسال امر وجلب نتيجة تنفيذه :
فهذا الزر يتطلب استعمال دالة SendCommandAndWaitResult لانتظار النتيجة
بعد وصول النتيجة سيتم عرضها على فورم اخر .
ولكن هناك شيئ مهم جدا يخص Get screen shot ، فكما نعلم من اسم الزر انه امر لجلب صورة سطح المكتب للسيرفر
وهذا يتطلب نقل الصورة من الserver الى client ، ولكن انتبه فلا يجوز نقل صورة حجمها اكبر من 60 كيلو بايت !!!
60 كيلو بايت !!! . هي مساحة صغيرة جدا واغلب الصور تكون اكبر من هذا الحجم فما الحل ؟؟
الحل بسيط اكثر مماتتخيل . فقط قم بالدخول الى App.config والخاص بالمشروع Client الذي نعمل عليه حاليا
وقم بالتالي :-
كما ترا قمت باضافة maxReceivedMessageSize لزيادة حجم اقصى حد لاستقبال الرسائل
ووضعت قيمته 2000000 مما يساوي 2 ميغا بايت
ومستحيل صورة سطح المكتب التي سيتم التقاطها بتنسيق jpg ان يتجاوز حجمها ال700 كيلو ولكن للاحتطيات فقط وضعت 2 مب.
الكود كامل :-
PHP كود :
using Microsoft.VisualBasic;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Web_Remote
{
public partial class ClientForm : Form
{
Service.MyServiceSoapClient _serviceClient = new Service.MyServiceSoapClient();
private string _ServerName = string.Empty;
private string _MyName = string.Empty;
public ClientForm()
{
InitializeComponent();
}
private void OKbutton_Click(object sender, EventArgs e)
{
if (_serviceClient.CheckNameExist(textBox1.Text.Trim()))
{
MessageBox.Show("Choose another name (My Name)", "Error : name exist", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
groupBox1.Enabled = false;
groupBox2.Enabled = true;
_MyName = textBox1.Text.Trim();
_ServerName = textBox2.Text.Trim();
}
private void SendMessageBtn_Click(object sender, EventArgs e)
{
string str = Interaction.InputBox("Enter your message to server");
if (str != string.Empty)
{
//ارسال امر
_serviceClient.SendCommandTo(_MyName, _ServerName, Service.Commands.ViewMessage, str);
}
}
private void SendBeebsBtn_Click(object sender, EventArgs e)
{
_serviceClient.SendCommandTo(_MyName, _ServerName, Service.Commands.PlayBeebs, null);
}
private void SendClipboardBtn_Click(object sender, EventArgs e)
{
string str = Interaction.InputBox("Enter your text to save it in server clipboard");
if (str != string.Empty)
{
_serviceClient.SendCommandTo(_MyName, _ServerName, Service.Commands.SetClipboard, str);
}
}
private void GetDataClipboardBtn_Click(object sender, EventArgs e)
{
//ارسال امر وانتظار النتيجةSendCommandAndWaitResult
string savedClipBoard = (string)_serviceClient.SendCommandAndWaitResult(_MyName,_ServerName,
Service.Commands.GetClipboard);
MessageBox.Show("The content of server clipboard is : " + savedClipBoard, "Server Clipboard",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void ScreenshotBtn_Click(object sender, EventArgs e)
{
//ارسال امر جلب الصورة وانتظار الصورة
byte[] serverShot = (byte[])_serviceClient.SendCommandAndWaitResult(_MyName, _ServerName,
Service.Commands.GetScreenShot);
using (System.IO.MemoryStream ms = new System.IO.MemoryStream(serverShot))
{
Image shot = Image.FromStream(ms);
ScreenShotForm shotForm = new ScreenShotForm();
shotForm.pictureBox1.BackgroundImage = shot;
shotForm.Show();
ms.Close();
}
}
private void MachinenameBtn_Click(object sender, EventArgs e)
{
//ارسال امر جلب اسم جهاز السيرفر وانظار النتيجة
string serverMachineName = (string)_serviceClient.SendCommandAndWaitResult(_MyName, _ServerName,
Service.Commands.GetPCName);
MessageBox.Show("The server machine name is : " + serverMachineName, "Server Machine Name",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void ShutdownBtn_Click(object sender, EventArgs e)
{
_serviceClient.SendCommandTo(_MyName, _ServerName, Service.Commands.TurnOffPC,null);
}
}
}
خامسا :- فيديو نتيجة الدرس (من تصويري)
او من على اليوتيوب مباشرة
كود :
https://www.youtube.com/watch?v=DClwZ_m9dAs
سابعا :- المراجع & الخاتمة
المراجع المعتمد عليها في هذه المقالة :-
[سلسلة التحكم] {3} - خدمة الويب (Web Service)
--
أتمنى أن تستفيدو من هذه المقالة
اعلم ان بعضكم قد يتسآل ويقول "مادام مبدأك نقل رسائل فقط ليش استعملت WebService ولم تقم بإرسال الرسائل مباشرة للServer" ؟
في الواقع الاتصال المباشر بين طرفين يسمى Peer2Peer وهو اتصال سهل يمكنك عمله بسهولة ولكن ...
لو كان الطرفان بعيدان وعلى شبكة مختلفة ، أعلم ان Peer2Peer ستواجه المصاعب والمآسي ولن يتم الاتصال
وكل هذا بسبب الجدران النارية والNAT ، ولكن في الاجزاء القادمة سنقوم بكشف الستار عن اسرار الاتصال المباشر
وسنقوم بعمل برنامج مثل TeamViewer يقوم بالتحكم الكامل عن بعد بواسطة Peer2Peer ولن نقوم بالاستعان بطرف ثالث (Third party).
نلقاكم بخير
إنتهى