Moving Complex Objects Across The Network - نسخة قابلة للطباعة +- منتدى فيجوال بيسك لكل العرب | منتدى المبرمجين العرب (http://vb4arb.com/vb) +-- قسم : قسم لغة السي شارب C#.NET (http://vb4arb.com/vb/forumdisplay.php?fid=175) +--- قسم : قسم مقالات C#.NET (http://vb4arb.com/vb/forumdisplay.php?fid=177) +--- الموضوع : Moving Complex Objects Across The Network (/showthread.php?tid=4089) |
Moving Complex Objects Across The Network - Sajad - 14-09-12 [COLOR="#800080"]بسم الله الرحمن الرحيم
((رب اشرح لي صدري ويسر لي امري واحلل عقدة من لساني يفقهوا قولي)) صدق الله العلي العظيم [/COLOR] [COLOR="#000080"]السلام عليكم اعضاء و زوار المنتدى الكرام ورحمة الله وبركاته ارجو ان تكونوا بتمام الصحة والعافية ان شاءالله سنتعلم في هذا المقال ان شاءالله كيفية بناء مشروع لنقل البيانات المعقدة عبر الشبكة عن طريق استخدام بروتوكول TCP. [/COLOR] [COLOR="#000080"]ان البيانات التي تنتقل عبر الشبكة تنتقل وفق بروتوكولات محددة تنظم عملية مرورها من خلال الشبكة ,و من اهم هذه البروتوكولات بروتوكول UDP وبرتوكول TCP وهما من اهم انواع البروتوكولات المستخدمة لنقل البيانات داخل الشبكة ,حيث تعتبر بروتوكول الUDP من النوع الغير موجه (ConnectionLess) أي لا يحتاج الى انشاء جلسة (اتصال) مع الطرف المقابل لتبادل البيانات لانه لا يدعم وصول البيانات كلها الى المستقبل لذا قد تضيع بعض الحزم(Packets) اثناء مرورها داخل الشبكة ولا تصل بشكل كامل الى وجهتا لكن هذا البروتوكول تتسم بالسرعة العالية في نقل البيانات لذا يفضل استخدامها لنقل مقاطع الصوت او الفيديو ويستخدم لعمل الBroadcasting والMulticasting. أما برتوكول الTCP تعتبر من النوع الموجه (Connection Oriented) أي يحتاج الى انشاء جلسة مع الطرف المقابل لتبادل البيانات ويدعم وصول البيانات بشكل سليم الى المستلم لكن هذا يسبب بطئ في نقل البيانات الى الطرف المستقبل لان هذا البروتوكول يدعم عمليات التحقق (Authentication) من وصول البيانات بشكل كامل وسليم الى وجهتها ,يستخدم عادة لنقل الرسائل. [/COLOR] [COLOR="#0000CD"]وفرت لنا الNET.Framework مكتبات جاهزة لارسال واستقبال البيانات عبر الشبكة وذلك عن طريق مجال الاسماء Sockets حيث تضم مجموعة كبيرة من الاصناف التي تتعامل مع الشبكات ومن اهما TCPClient and UDPClient and Socket. لكننا سنتعامل في هذا الدرس كيفية ارسال واستقبال البيانات عبر بروتوكول TCP وذلك عن طريق الكائن TcpClient and TcpListener والكائن Socket.[/COLOR] اولا : ارسال بيانات من خلال الكائن Socket الطرف المرسل PHP كود : using System; [COLOR="#0000CD"]كما تلاحظون ان عملية الارسال سهلة للغاية كل ما هنالك اننا أنشأنا كائن من نوع Socket واخترنا الAdressFmily من نوع InterNetwork حيث يتعامل هذا النوع مع الIPv4 والIPv6 ثم اخترنا نوع الارسال وهو Stream ومن ثم نوع البروتوكول الا وهو الTCP وبعد ذلك فتحنا الاتصال مع الRemotehost عن طريق الIPEndPoint ثم ارسلنا الرسالة بعد تحوليها الى Bytes عن طري الكائن الدالة Send ثم اغلقنا الاتصال. [/COLOR] الطرف المستقبل PHP كود : using System; [COLOR="#0000CD"]الملاحظات المهمة في طرف المستقبل: 1- دالة ال()Bind وظيفتها ربط الIP والPort بالSocket 2- دالة ال()Listen وظيفتها بدء التنصت على الPort واسندنا ايها 1- الاستقبال عدد غير محدد من الاجهزة 3- دالة ال()Accept الموافقة على بدء الجلسة مع الطرف المقابل 4- استخدام InfiniteLoop لاستقبال الرسائل بشكل مستمر 5-استخدام الThreading في عملية الاستقبال وذلك لتشغيل دالة الاستقبال بشكل منفصل عن المسار الخاص بالبرنامج, واغلاق الsocket والThread عند اغلاق البرنامج. 6- اهم ملاحظة في طرف المستقبل هي استخدام دالة الInvoke الخاص باداة الTextbox لانشاء Delegate جديدة عن طريق الMethodInvoker ,حيث عند عدم انشاء delegate سيظهر Exception اثناء استقبال الرسالة لان الاداة التي نريد عرض الرسالة داخلها تعمل على مسلك آخر غير المسلك التي نعمل عليها لذا يجب فصله عن طريق دالة الInvoke وذلك بعمل delegate نضع داخلها الTextbox وذلك لعرض البيانات داخلها للحؤول دون ظهور استثناء يعرقل عمل البرنامج. إلا هنا وبفضل الله انتهينا من تبيان اهم الملاحظات المتعلقة بارسال واستقبال البيانات عبر الشبكة وذلك بانشاء برنامجين توضحان هذه الفكرة ,لكن المشكلة تكمن في كيفية ارسال مجموعة من القيم من خلال الشبكة كوحدة واحدة مثلا ارسال معلومات لموظف ما تضم اسم وعمر ورقم وراتب الموظف كعنصر واحد من خلال الشبكة وكيفية استرجاع هذه البيانات وعرضها مرة اخرى في طرف المستقبل كحالتها الاصلية!!؟؟ اذا ما العمل؟؟ الحل ان شاءالله سهل مع قليل من الصبر والتفكير ان اهم وأسهل وسيلة لعمل ذلك هي بانشاء صنف تضم هذه القيم ثم دمج هذه القيم في مصفوفة من نوع Byte وبعد ذلك ارسالها عبر الشبكة ,لكن المشكلة تكمن في كيفية استرجاع بعض هذه القيم مرة اخرى عند الطرف المستقبل؟؟ الحل هي بارسال طول كل قيمة نصية فقط مع حجم المصفوفة الى الطرف المستقبل لان حجم النص هنا غير محدد ثم القيام باسترجاع القيم اعتمادا على طول كل عنصر. [/COLOR] ثانيا : ارسال البيانات المعقدة من خلال الكائن TcpClient واستقبالها من خلال الكائن TcpLietener الطرف المرسل : اولا قم باضافة Class الى البرنامج عن طريق الProject-->AddClass وسمي الكلاس بStudentInfo ثم استبدلها بالكود التالي التي سيأتي شرحها لاحقا : PHP كود : using System; اهم شيء في هذا الClass هي دالة الInfoToBytes تقتصر وظيفة هذه الدالة بتحويل كل عنصر من عناصر الصنف الى مصفوفة من البايتات عن طريق دالة الGetBytes للكائن UnicodeEncoding للنصوص حيث تدعم العربية والGetBytes للكائن Bitconverter للانواع الاخرى ومن ثم دمج هذه العناصر في مصفوفة واحدة من نوع Byte وذلك عن طريق دالة الBlockCopy للكائن Buffer حيث تقوم هذه الدالة باخذ نسخة من مصفوفة ووضعها في موقع معين في مصفوفة اخرى وتأخذ الصيغة التالية: PHP كود : Buffer.BlockCopy(byte []array1,int start,byte []array2,int offset,int size); [COLOR="#0000CD"]المدخل الاول array1 هي المصفوفة المطلوب نسخها الى المصفوفة الثانية array2 المدخل الثاني هي موقع البداية للمصفوفة الاولى المدخل الثالث هي المصفوفة التي تستقبل عناصر المصفوفة الاولى المدخل الرابع تمثل الموقع الذي سوف نبدأ منه بكتابة المصفوفة الاولى داخل المصفوفة الثانية وهذه القيمة سوف تتغير مع كل اضافة الى المصفوفة والمدخل الاخير هي حجم المصفوفة المطلوب الحاقها بالمصفوفة الثانية وايضا قمنا بحساب حجم البيانات و طول كل عنصر من نوع string لاننا لا نعرف طول النص المراد ارسالها عبر الشبكة ,أما اطوال المتغيرات من نوع .....int or float or double فهي معروفة حيث تأخذ الint إما 2bytes or 4bytes or 8bytes والFloat تأخذ 4bytes والDouble تأخذ 8bytes لكن كحجم افتراضي تأخذ الint اربع بايتات والDouble 4bytes وهو ما تلاحظونه في زيادة الpos بمقدار4 بعد الint و 8 بعد الDouble حيث تحجز دالة الGetBytes للكائن BitConverter اربعة مواقع من المصفوفة الثانية في كل حالة نسخ من نوع int و8 مواقع في حالة الDouble ما عدا طول الstring فنقوم بحساب طول النص المراد ارسالها وخزنها ايضا في المصفوفة وبعد ذلك زيادة الpos بمقدار طول النص. [/COLOR] الكود الخاص بالForm1.cs للمرسل: PHP كود : using System; [COLOR="#0000CD"]اهم الملاحظات: 1- استدعاء الدالة StudentInfo.InfoToBytes لتحويل البيانات الى مصفوفة واحدة من نوع byte 2- انشاء كائن الاتصال TcpClient ثم عمل Connect للRemote host 3- جلب الStream من الTcp الى الNetworkStream 4- اسناد الNetworkStream الى الBinaryWriter 5- كتابة الحجم والبيانات الى الNetworkStream عن طريق دالة الWrite للBinaryWriter [/COLOR] الطرف المستقبل: [COLOR="#0000CD"]ايضا قم باضافة Class الى البرنامج عن طريق الProject-->AddClass وسمي الكلاس ب ReceiveStudentInfo ثم استبدلها بالكود التالي : [/COLOR] PHP كود : using System; [COLOR="#0000CD"]تضم هذا الكلاس دالة تستقبل مصفوفة من البايتات المرسلة من قبل المرسل وتقوم بتجزئتها وارجاعها الى حالتها الاصلية من خلال كائني الBitConverter للint and double وللانواع الاخرى ايضا والUnicodeEncoding للنصوص حيث يرجع سبب استخدام هذا الكائن في برنامجنا للنصوص بانها تدعم العربية. وتضم كائن الBitConverter دوال تحويل من ByteArray to BaseType مع اعطاءه موقع البداية فقط حيث لا يحتاج الى تحديد الطول لانه يعتمد على نوع المتغير ,وتضم الUnicodeEncoding ايضا دالة تحويل من ByteArray to string وهنا يجب ان نحدد طول النص المراد استرجاعها.[/COLOR] الكود الخاص بالForm1.cs للطرف المستقبل: PHP كود : using System; [COLOR="#0000CD"]اهم الملاحظات: 1- انشاء كائن TcpListener ثم بدء الجلسة مع الطرف المرسل 2- قبول الجلسة مع المرسل باستخدام دالة الAcceptSocket 3- انشاء NetworkStream و BinaryReader 4- قراءة الحجم المرسل 5- قراءة البيانات المرسلة الى مصفوفة بايتات 6- تمرير المصفوفة الى دالة الReceiveStudentInfo.GetStudentInfo لاسترجاع البيانات المرسلة [/COLOR] |