fmo_82

سلسلة دروس C# Network Programming

237 ردود في هذا الموضوع

انتظروا الفصل الخاص بالـVoice Over IP Programming والذي سيتم إضافته غدا بمشيئة الله ...

21_03_06_11_42_17_1142970137VOIP.JPG

:rolleyes: :rolleyes: :rolleyes:

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

بسم الله الرحمن الرحيم,

الفصل الحادي عشر : Voice Over IP Programming

المقدمة:

تتلخص الفكرة الأساسية من نقل الصوت عبر بروتوكول الإنترنت IP بتحويل الصوت إلى مجموعة من الـ Bits تجمع في Byte Array ثم كبسلته ليتم نقله ك Datagram Packets عبر الشبكة , وللاستقبال الصوت في الطرف الأخر يتم تجميع ال Packets مرة أخرى في مصفوفة Byte Array , وتتم عملية القراءة وفق مبدأ الـFIFO – First In First Out أي القادم أولا يعرض أولا ...

تكمن المشكلة الأساسية بنقل الصوت في مدى توفر الشروط اللازمة حتى يتم إيصال وعرض الصوت بالشكل السليم و وفق الترتب الذي أرسل عليه , وقد تعتبر محدوديات ومشاكل بروتوكولات ال Transport Layer من أهم ما دعا Microsoft من العزوف عن دعم الـ Dot Net لعملية نقل الصوت وخاصة في بيئة النظام الحالي , ومن المعروف أن نظام التشغيل Windows XP يدعم الاتصال باستخدام بروتوكول TCP أو UDP فقط وهذا يعني انك إذا كنت تعمل تحت منصة نظام التشغيل Windows XP فإن أي عملية اتصال لن تكون إلا باستخدام واحد من هذه البروتوكولات ...

أولا : The Requirements of Voice Communication Systems

سوف نناقش في هذا الجزء متطلبات نقل الصوت عبر الشبكة ومشاكل نقل الصوت باستخدام بروتوكول الـ TCP و الـ UDP ...

– متطلبات نقل الصوت المثلى :

1 - أسلوب النقل Stream

2 – البروتوكول المستخدم لنقل الصوت يجب أن يدعم Delivered on Sequence

3 – تعتمد سرعة النقل على مدى حجم الضغط المستخدم Voice Compression والجودة المطلوبة ويفضل في هذه الحالة أن لا تقل سرعة النقل عن 31 KB\S بمعدل لا يقل عن 8.000 KHz كحد أدنى لجودة الصوت.

- السؤال الذي يطرح نفسه الآن , هل وفر بروتوكول الـ TCP والـ UDP هذه الأمور ؟

أولا بروتوكول ال TCP : يدعم بروتوكول ال TCP كل هذه الأمور وبكفاءة عالية لكن المشكلة الوحيدة في هذا البروتوكول هو عدم إمكانية استخدامه لعمل

Conference System Multicast إذ انه من المعروف أن ال Multicasting وال Broadcasting من الأمور الخاصة ببروتوكول ال UDP ولا يدعم ال TCP أي من هذه الأمور وهو ما بينته في الفصل السابق, إذ يعتر الـ TCP بروتوكول موجه Oriented Protocol لذلك لا يمكن الاعتماد عليه في حالة حاجتنا لعمل Multicast Conference System أو في حالة البث الإذاعي Broadcasting .. إذا الحل الأخر والوحيد هو بروتوكول الـ UDP في حالة حاجتنا لهذه الأمور.

ثانيا بروتوكول ال UDP : لا يعتبر هذا البروتوكول حل جيد لعملية نقل الصوت بالكفاءة العالية إذ أنه لا يدعم عمليةDelivered on Sequence وهو ما سبب من استحالة عمل Fragmented للـ Packets المرسل ومن المعروف أن حجم Ethernet Encapsulation لا يزيد عن 1500 KB للـ Packet الواحد وهو الحجم الأقصى للـ Datagram Encapsulation الخاص بالـ Ethernet لذلك في حالة قمنا بعمل Fragmented لصوت فإننا لن نضمن وصول الصوت وفق الترتيب المرسل وهو ما يسبب مشكلة كبيرة في عملية إعادة ترتيب الـ Fragments المرسل ومن هذه النقطة قدمت الكثير من الشركات والمنظمات العالمية حلول خاصة لعملية نقل الصوت عبر ال UDP منها منظمة الـ ITU- International Telecommunications Union بتقديمها أسلوب النقل H.323 ومنظمة IETF Internet Engineering Task Force بتقديمها أسلوب النقل RTP – Real Time Transport Protocol , حيث أضاف هذا المعيار تحسينات على بروتوكول ال UDP لعملية نقل الصوت في الزمن الحقيقي إذ يستخدم أسلوب ال Stream المستخدم في TCP لكن تحت منصة ال UDP وقد حل هذا المعيار بعض هذه المشاكل لكن ليس جميعها , إذ أن الحاجة أصبحت ملحة لوجود بروتوكول يدعم عملية النقل وفق الترتيب الصحيح Delivered on Sequence بالإضافة إلى دعم الاتصال ك Stream ودعم لل IP Multicasting وال Broadcasting , وكان الحل بإنشاء بروتوكول أخر وهو الـSCTP – Stream Control Transmission Protocol لكن المشكلة أن منصة Windows لا تدعم هذا البروتوكول وقد تم دعمه بشكل كامل في نظام التشغيل Linux , كما وعدت Microsoft بدعم هذا البروتوكول في الإصدار التالي من نظام التشغيل Windows والذي سآتي على شرحه في الجزء التالي من هذا الفصل.

ثانيا : The Concept Of Voice Communication :

تمر عملية التقاط الصوت بمجموعة من المراحل تبدأ بالتقاط الصوت من المايكروفون وتمثيل الذبذبات الصوتية ثم تحويلها إلى مجموعة من الـ Bits وذلك بعمل Sampling لذبذبات الصوتية الملتقطة وبعد هذه العملية يمكننا نقل الصوت عبر الشبكة وتمر عملية نقل الصوت عبر الشبكة بمجموعة من المراحل وهي :

1- في الـ Application Layer , طريقة التقاط الصوت وتحويله إلى Bits وهو ما ذكرته سابقا , و استخدام تقنيات لضغط الصوت Audio Compression Teachings وحتى يمكن إرساله عبر الإمكانيات المحدودة لشبكة الاتصال.

2- في الـ Transport Layer , وهو من أهم الأمور التي يجب أخذها بعين الاعتبار إذ أن المفاضلة بين اختيار بروتوكول الـ UDP أو الـTCP تعتمد على مدى الحاجة التي نريدها ودقة الصوت من جهة أخرى إذ أن أفضل طريقة لنقل الصوت هي استخدام تقنيات الـStream لكن من المعروف أن بروتوكول الـUDP لا يدعم عملية النقل ك Stream كونه لا يدعم التوصيل وفق الترتيب Delivered on Sequence إذ أننا في هذه الحالة لن نتمكن من عمل الـ Fragmentation للـ Buffer حيث لن نضمن وصول الـ Fragments وفق الترتيب الذي أرسل عليه وسوف نضطر إلى التقيد بمحدوديات الـ Ethernet للـ Packet وهي 1500 KB للـ Packet الواحد ولحل هذه المشكلة سوف نلجأ إلى تبني بعض التقنيات الجديدة والتي تعتمد على بروتوكول الـ UDP وحتى يتم نقل الصوت ك Stream ومنها أسلوب النقل H.323 والذي ذكرته سابقا , لكن لم تدعم الدوت نيت أي من هذه التقنيات , لذلك عندما نريد نقل صوت من جهاز إلى آخر ك Stream لابد لنا من استخدام بروتوكول الـ TCP لكن كما ذكرنا سابقا فإنه لا يدعم الـ IP Multicasting و الـ Broadcasting

3- في الـ Network Layer يتم عنونة الـ Packets وإذا ما قررنا اعتماد الـ UDP فإننا سوف نتمكن من عمل البث الإذاعي Broadcasting ومجموعات البث Multicasting

4- في الـ Data Link Layer سيتم تحديد طبيعة ولإرسال سواء باستخدام الـ Ethernet أو غيره وفي هذه الحالة سيتم الاعتماد على الـ Ethernet لكن مشكلته كما ذكرتها سابقا بمحدودية حجم الـ Frame إذ لا تتجاوز الـ 1500 KB

5- في الـ Physical Layer طبعا المشاكل التي قد تحدث إثناء عملية النقل كثيرة جدا وقد يحدث تأخير Delay لسبب أو لآخر أو قد تضيع بعض الـ Bits إثناء الإرسال لذلك لابد من وجود بروتوكولات تدعم التصحيح لكل هذه المشاكل والتي قد تحدث إثناء عملية الإرسال.

يستقبل الطرف المقابل الـ Bits من طبقة الـ Physical Layer وتمر عبر الـ Data Link Layer ومن ثم الـ Network Layer وفي إثناء هذه المرحلة فإن مستقبل الـ Packets قد يكون هو الشخص المعني في حالة كان أسلوب البث Unicast أو قد يكون جزء من مجموعة الاستقبال Multicast أو قد يكون من ضمن الشبكة التي تم الإرسال لها ك Broadcast لذلك في حالة كونه جزء من مجموعة فإن جهة الإرسال غير معنية بالجهة التي سوف تستقبل الـ Packets وفي هذه الحالة فإنه غير معني سواء استقبلت جزء من الـ Packets أو كلها حيث لن يتم إرسال أي Acknowledgment إلى المرسل لذلك قد تحدث الكثير من المشاكل إثناء هذه المرحلة منها ضياع جزء من الـ Packets المرسل والذي سوف يسبب وصول الصوت بشكل متقطع , وطبعا سوف يكون الاعتماد في هذه الحالة على بروتوكولات الطبقة الأعلى وهي هنا Transport Layer فإذا كان المرسل والمستقبل يستخدم الـ TCP فإن كل هذه المشاكل سوف تحل لكن المشكلة تكمن في كونه يستخدم الـ UDP حيث لا يوجد حل إلا بإتباع معيار مساند يضمن وصول كافة الـ Packets بترتيب الذي أرسل عليه وبدون ضياع أجزاء من الـ Packets المرسل.

الشكل التالي يوضح عملية ضياع بعض الـ Packets إثناء الإرسال باستخدام شبكة Ethernet Wireless LAN و IP

Multicasting UDP مما سوف يسبب تقطيع في الصوت:

VOIP_img1.JPG

ثالثا: How to Create a Voice Chat Throw Dot Net Using Unmanaged API’s Functions :

كما قلنا سابقا فإن الدوت نيت لم تدعم أي من عمليات التقاط وعرض الصوت , لكن لإجراء هذه العمليات لابد من استخدام مجموعة ملفات الـ DLL والتي تأتي مع نظام التشغيل ومنها ملف winmm.dll الشهير , والخاص بالتعامل مع وسائل الـ Multimedia في نظام التشغيل , حيث يدعم هذا الملف مجموعة من الـ Methods لالتقاط الصوت عبر المايكروفون وتخزينه في Byte Array Buffer ومن ثم عرضه مرة اخرى وهذه الـ Method هي :

waveInGetNumDevs والتي تستخدم لتحديد عدد أجهزة الإدخال والمربوطة مع الـ Sound Card ولا تأخذ أي باروميترات.

waveInAddBuffer وتستخدم لتخزين الـBits الواردة من جهاز الإدخال في Byte Array Buffer وتأخذ هذه الـMethod ثلاثة باروميترات وهي:

waveInAddBuffer(IntPtr hwi, ref WaveHdr pwh, int cbwh)

حيث يمرر للأول جهاز الإدخال والذي تم اختياره و يحدد في الثاني Reference لموقع تخزين الـ Buffer وفي الثالث يحدد حجم الـ Buffer المستلم

الـميثود waveInClose و waveInOpen لفتح وإغلاق الاتصال مع جهاز الإدخال.

الميثود waveInPrepareHeader لتجهيز وحجز الـ Buffer وتأخذ نفس الباروميترات الموجودة في waveInAddBuffer.

الميثود waveInUnprepareHeader ويتم استدعائها بعد تعبئة الـ Buffer حتى يتم إرسال الـ Buffer ومن ثم تفريغه للاستعداد لتعبئته مرة أخرى.

الميثود waveInReset لإرجاع مؤشر الـPointer الخاص بالـ Buffer إلى صفر

الميثود waveInStart و الميثود waveInStop بدأ وإغلاق عملية الإدخال من المايكروفون.

ولاستخدام هذه الميثود في الدوت نيت نقوم بتعريفها أولا باستخدام DllImport وكما يلي:

[DllImport(winmm.dll)]
public static extern int waveInGetNumDevs();
[DllImport(winmm.dll)]
public static extern int waveInAddBuffer(IntPtr hwi, ref WaveHdr pwh, int cbwh);
[DllImport(winmm.dll)]
public static extern int waveInClose(IntPtr hwi);
[DllImport(winmm.dll)]
public static extern int waveInOpen(out IntPtr phwi, int uDeviceID, WaveFormat lpFormat, WaveDelegate dwCallback, int dwInstance, int dwFlags);
[DllImport(winmm.dll)]
public static extern int waveInPrepareHeader(IntPtr hWaveIn, ref WaveHdr lpWaveInHdr, int uSize);
[DllImport(winmm.dll)]
public static extern int waveInUnprepareHeader(IntPtr hWaveIn, ref WaveHdr lpWaveInHdr, int uSize);
[DllImport(winmm.dll)]
public static extern int waveInReset(IntPtr hwi);
[DllImport(winmm.dll)]
public static extern int waveInStart(IntPtr hwi);
[DllImport(winmm.dll)]
public static extern int waveInStop(IntPtr hwi);  

وكما سوف نستخدم مجموعة الـMethods التالية لتحويل الـ Byte Array Buffer إلى صوت مرة أخرى وعرضه على جهاز الإخراج :

[DllImport(winmm.dll)]
public static extern int waveOutGetNumDevs();
[DllImport(winmm.dll)]
public static extern int waveOutPrepareHeader(IntPtr hWaveOut, ref WaveHdr lpWaveOutHdr, int uSize);
[DllImport(winmm.dll)]
public static extern int waveOutUnprepareHeader(IntPtr hWaveOut, ref WaveHdr lpWaveOutHdr, int uSize);
[DllImport(winmm.dll)]
public static extern int waveOutWrite(IntPtr hWaveOut, ref WaveHdr lpWaveOutHdr, int uSize);
[DllImport(winmm.dll)]
public static extern int waveOutOpen(out IntPtr hWaveOut, int uDeviceID, WaveFormat lpFormat, WaveDelegate dwCallback, int dwInstance, int dwFlags);
[DllImport(winmm.dll)]
public static extern int waveOutReset(IntPtr hWaveOut);
[DllImport(mmdll)]
public static extern int waveOutClose(IntPtr hWaveOut);
[DllImport(mmdll)]
public static extern int waveOutPause(IntPtr hWaveOut);
[DllImport(mmdll)]
public static extern int waveOutRestart(IntPtr hWaveOut);
[DllImport(mmdll)]
public static extern int waveOutGetPosition(IntPtr hWaveOut, out int lpInfo, int uSize);
[DllImport(mmdll)]
public static extern int waveOutSetVolume(IntPtr hWaveOut, int dwVolume);
[DllImport(mmdll)]
public static extern int waveOutGetVolume(IntPtr hWaveOut, out int dwVolume);

وحتى نتمكن من عرض محتويات الـ Buffer نستخدم التعريف التالي:

using System;
using System.Runtime.InteropServices;
using System.Resources;
using System.IO;

 public class Winmm
{
 public const UInt32 SND_ASYNC = 1;
 public const UInt32 SND_MEMORY = 4;

 [DllImport("Winmm.dll")]
 public static extern bool PlaySound(byte[] data, IntPtr hMod,   UInt32 dwFlags);
 public Winmm()
 {}
public static void PlayWavResource(byte[] buffer)
{
PlaySound(buffer, IntPtr.Zero,  SND_ASYNC |  SND_MEMORY);
}
}

حيث نمرر للـ PlaySound Method الـ Byte Buffer والمستلم من Method الاستقبال الخاصة بالـ Socket وكما يلي:

void Voice_Receiver()
{
UdpClient sock = new UdpClient(5020);
sock.JoinMulticastGroup(IPAddress.Parse(multicast_IP.Text));
IPEndPoint iep = new IPEndPoint(IPAddress.Any, 0);
byte[] voice_Come = sock.Receive(ref iep);
Winmm.PlayWavResource(voice_Come);
sock.Close();
}

البدء بإنشاء برنامج المحادثة الصوتية Voice Chat System :

سوف نجزئ عملية التقاط الصوت وتخزينه في الـ Buffer ثم عرضه مرة أخرى في مجموعة من الـClasses وهو تقسيم تم استخدامه في الكثير من البرمجيات الخاصة ب Microsoft ومنها برنامج Windows Sound Recorder وسوف نجمع هذه الـ Classes في ملف واحد نسميه Voice Library وسوف أرفق محتويات هذه الـ Classes في ملحقات هذا الفصل , وهذه الـ Classes هي:

WaveIn Class وسوف نستخدمه لوضع كافة الـ Methods الخاصة بالتقاط الصوت وتخزينه في Byte Array

WaveOut Clasas وسوف نستخدمه لعرض الصوت الأتي من الـ Buffer ثم عرضه

WaveStream Class والذي سوف نستخدمه لتحويل الصوت إلى Stream حيث يسهل إرساله عبر الشبكة ويشبه عمله عمل MemoryStream المستخدمة في الدوت نيت

الميثود FifoStream لتنظيم الـ Stream بحيث يتم عرض الداخل أولا خارج أولا

الميثود WaveNative ويتم فيها وضع كافة التعريفات للـ Methods الخاصة بالملف winmm.dl والتي شرحناها سابقاّ.

سوف نستخدم في هذا المثال بروتوكول الـ UDP لعملية النقل ومعتمدا على أسلوب البث Full Diplex Unicast Voice Chat System وللبدء سوف يكون الشكل العام لبرنامج الاتصال كما يلي :

VOIP_img2.JPG

وسوف نقوم بكبسلة الـ Classes السابقة في ملف Voice.dll وسوف نضعه في الـ References الخاصة بالبرنامج وحتى نستطيع استخدام هذا الملف في جميع البرامج التي سوف تستخدم عملية الاتصال الصوتي بعد هذه العملية سنقوم الملف باستخدام الـ Using وكما يلي:

using System.Net;
using System.Net.Sockets;
using System.Threading;
using Voice;

ثم نقوم بتعريف الـSocket والـThread والذي سوف نستخدمه في البرنامج ويفضل وضع هذه التعريفات في بداية البرنامج أي بعد تعريف الـ Class الرئيسي والهدف من هذه العملية هي القدرة على إغلاق الـ Socket والـ Thread عند إطفاء البرنامج وحتى لا تبقى في الذاكرة عند إغلاق برنامج الاتصال , ويتم ذلك كما يلي:

public class Form1 : System.Windows.Forms.Form
{
private Socket socket;
 private Thread thread;

وسوف نعرف Object من الـClasses السابقة ونعرف الـBuffer الذي سيتم تسجيل الصوت المراد إرساله والـBuffer الذي سيتم عرض الصوت المستلم من الـSocket

private WaveOutPlayer m_Player;
private WaveInRecorder m_Recorder;
private FifoStream m_Fifo = new FifoStream();
private byte[] m_PlayBuffer;
private byte[] m_RecBuffer;

في الـ Constructure الخاص بالبرنامج أو في الـ Form Load Event قم بكتابة التعريف الخاص بالـ Socket والـ Thread

public Form1()
{
InitializeComponent();
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
thread = new Thread(new ThreadStart(Voice_In));
}

سوف نضع في الـ Voice_In Methodالكود الخاص بعملية استقبال الصوت من الـSocket وكما يلي:

private void Voice_In()
{
byte[] br;
socket.Bind(new IPEndPoint(IPAddress.Any, 5020));

while (true)
{
br = new byte[16384];
socket.Receive(br);
m_Fifo.Write(br, 0, br.Length);
}
}

حيث يتم استقبال الصوت من الشبكة باستخدام الـ Receive Method ثم نمرر الصوت المستقبل إلى الـ m_Fifo.Write Method وحتى يتم تنفيذه وتحويله إلى صوت مرة أخرى.

أما الـMethod التي تقوم بتسجيل الصوت وإرساله إلى الجهاز الأخر فهي:

private void Voice_Out(IntPtr data, int size)
{
//for Recorder
if (m_RecBuffer == null || m_RecBuffer.Length < size)
m_RecBuffer = new byte[size];
System.Runtime.InteropServices.Marshal.Copy(data, m_RecBuffer, 0, size);
//Microphone ==> data ==> m_RecBuffer ==> m_Fifo
socket.SendTo(m_RecBuffer, new IPEndPoint(IPAddress.Parse(Peer_IP.Text),5030));
}  

لاحظ أنه في حالة إذا ما أردنا عمل برنامج Full Duplex بحيث يرسل ويستقبل في نفس الوقت فإننا بحاجة إلى تعريف Tow Ports واحد للإرسال وأخرى للاستقبال وفي الطرف الأخر تكون Port الإرسال لديك هي Port الاستقبال لديه والعكس صحيح ...

في زر البدء يتم تنفيذ الميثود التالية:

private void Start()
{
Stop();
try
{
WaveFormat fmt = new WaveFormat(44100, 16, 2);
m_Player = new WaveOutPlayer(-1, fmt, 16384, 3, new BufferFillEventHandler(Filler));
m_Recorder = new WaveInRecorder(-1, fmt, 16384, 3, new BufferDoneEventHandler(Voice_Out));
}
catch
{
Stop();
throw;
}
}

أما في زر الإيقاف فيتم تنفيذ الميثود التالية:

private void Stop()
{
if (m_Player != null)
try
 {
 m_Player.Dispose();
 }
finally
{
m_Player = null;
}
if (m_Recorder != null)
try
{
m_Recorder.Dispose();
}
finally
{
m_Recorder = null;
}
m_Fifo.Flush(); // clear all pending data
}

الميثود التي تقوم بعرض الـ Voice Buffer والمستلم من Socket على السماعة:

private void Filler(IntPtr data, int size)
{
if (m_PlayBuffer == null || m_PlayBuffer.Length < size)
m_PlayBuffer = new byte[size];
if (m_Fifo.Length >= size)
m_Fifo.Read(m_PlayBuffer, 0, size);
else
for (int i = 0; i < m_PlayBuffer.Length; i++)
m_PlayBuffer[i] = 0;
System.Runtime.InteropServices.Marshal.Copy(m_PlayBuffer, 0, data, size);
// m_Fifo ==> m_PlayBuffer==> data ==> Speakers
}

رابعا: Testing TCP,UDP and Thinking in SCTP to Transfer Voice Throw Networks :

لإنشاء برنامج Multicast Half Duplex Voice Chat System نقوم بإضافة التعريفات التالية والخاصة بالـ Multicasting والتي شرحناها في الفصل السابق , وسوف نعتمد على وجود برنامجين واحد للمحاضر يتم من خلاله تسجيل الصوت وآخر لطالب حيث يستمع فيه لمحاضرة المعلم وكما في الشكل التالي:

VOIP_img3.JPG

في برنامج الإرسال (برنامج المعلم) لا يختلف الكود بشيء فقط عند الإرسال يتم ذلك باستخدام الـ IP Multicasting وكما يلي:

 private void Voice_Out(IntPtr data, int size)
{
//for Recorder
if (m_RecBuffer == null || m_RecBuffer.Length < size)
m_RecBuffer = new byte[size];
System.Runtime.InteropServices.Marshal.Copy(data, m_RecBuffer, 0, size);
//Microphone ==> data ==> m_RecBuffer ==> m_Fifo
socket.SendTo(m_RecBuffer, new IPEndPoint(IPAddress.Parse("224.0.1.7"), 5020));
}  

في الطرف المستقبل نقوم بالانضمام إلى الـIP Multicast Group ومن ثم الاستقبال من خلاله وكما يلي:

private void Voice_In()
{
UdpClient sock = new UdpClient(5000);
sock.JoinMulticastGroup(IPAddress.Parse("224.0.1.7"));
IPEndPoint iep = new IPEndPoint(IPAddress.Any,0);


while (true)
{
m_Fifo.Write(sock.Receive(ref iep), 0,sock.Receive(ref iep).Length);
}
}

الآن نفذ البرنامج ...

لاحظ أن الصوت قد يتقطع أحيانا وبتأكيد السبب واضح وهو أنه في حالة استخدام بروتوكول الـ UDP والـ Multicasting فإن عملية الإرسال ستكون عشوائية وهذا قد يسبب ضياع واحد أو أكثر من الـPackets المرسلة عبر الشبكة كل فترة وبما أن بروتوكول الـ UDP لا يدعم أي من عمليات التحقق من الوصول وعمليات التوصيل على الترتيب فإن حدوث واحد أو أكثر من هذه المشاكل أمر محتمل ...

دعنا الآن نجرب عملية الإرسال باستخدام بروتوكول TCP , لاحظ أن هذا البروتوكول هو بروتوكول موجه Oriented Protocol كما يدعم جميع عمليات التحقق من الوصول بالإضافة إلى كونه يدعم الاتصال بين الطرفين باستخدام أسلوب الـ Stream وهو ما يميز هذا البروتوكول عن غيره إذ أننا في حالة استخدامه لن نضطر إلى الأخذ بحجم البيانات المرسلة حيث يتم عمل Fragmentation لها بكل سهولة بالإضافة إلى سهولة تجميعها مرة أخرى وبدون الخوف من مشاكل الـ Delivered Out on Sequence ...

كل ما علينا تغييره هو تعريف الـ Socket السابق وجعله كما يلي :

  
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream , ProtocolType.Tcp);

أما في عملية الإرسال فيمكننا استخدام الـNetwork Stream Class وكما يلي:

 private void Voice_Out(IntPtr data, int size)
{
//for Recorder
if (m_RecBuffer == null || m_RecBuffer.Length < size)
m_RecBuffer = new byte[size];
System.Runtime.InteropServices.Marshal.Copy(data, m_RecBuffer, 0, size);
//Microphone ==> data ==> m_RecBuffer ==> m_Fifo
sock.Connect(new IPEndPoint(IPAddress.Parse("10.0.0.10"),5020));
NetworkStream ns = new NetworkStream (socket);
ns.Write (m_RecBuffer,0,m_RecBuffer.Length);
}  

في الطرف المستقبل نقوم باستخدام الـ Network Stream مرة أخرى لكن للاستقبال:

private void Voice_In()
{
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream , ProtocolType.Tcp);
sock.Bind(new IPEndPoint(IPAddress.Parse("10.0.0.10"),5020));
NetworkStream ns = new NetworkStream (sock);
while (true)
{
byte[] buffer = new byte [16384];
ns.Read (buffer,0,16384);
m_Fifo.Write(buffer, 0, buffer.Length); }
}

لاحظ أن دقة الصوت أصبحت ممتازة كما أنه لا يوجد أي تقطيع في الصوت لكن المشكلة تكمن في أننا لن نستطيع الاستفادة من هذه الإمكانيات الرائعة في الـMulticasting أو الـBroadcasting ...

والحل الوحيد هو إما استخدام المعايير السابقة مع بروتوكول الـUDP أو استخدام بروتوكول الـTCP أو الانتظار لحين الانتهاء من مشروع Long Horn حتى تدعم Microsoft بروتوكول الـSCTP ...

السؤال الذي يطرح نفسه الآن ما هو الجديد بهذا البروتوكول هل حل المشكلة؟؟

الجواب بكل بساطة نعم قد حل المشكلة حيث أن معمارية هذا البروتوكول الذي استفاد من ميزات الـTCP والدعم المقدم من قبل الـUDP لعمليات الـMulticasting إذ أصبح لدينا الآن منصة قوية يعتمد عليها في عمل الـFragmentation وإعادة ترتيبها بكل سهولة بالإضافة إلى دعمه عملية Delivered on Sequence وهذا واضح من بنية الـHeader الخاصة بهذا البروتوكول انظر إلى الشكل التالي:

VOIP_img4.JPG

خامسا: How to Create a Voice Conference System Using Microsoft Direct Play 9 :

دعمت Microsoft تقنيات رائعة جدا للنقل الصوت في الإصدار الخاص بDirect Play9 ويسمى أيضا DirectPlay Transport Protocol وهو جزء من مجموعة الـDirectX وكان الهدف من إطلاقها وجود معيار موحد لمبرمجين الألعاب فيما يخص الـNetwork Games, وتعتمد هذه المكتبة على مجموعة من المعايير الخاصة بتشبيك حيث كان الهدف منها هو جعل عملية الاتصال ممكنة تحت جميع البيئات المختلفة و سواء كان البروتوكول المستخدم هو الـTCP/IP أو IPX الخاص بNovel فإن عملية الاتصال ممكنة وبدون أي اختلافات من النواحي البرمجية, ومن أهم ميزات الـ DirectPlay Transport Protocol:

- Reliable delivery of messages حيث يدعم عملية التحقق التوصيل للجهة المعنية

- Sequential a delivery of messages حيث يدعم التوصيل وفق الترتيب الصحيح

- Send prioritization حيث يدعم عملية وضع أولويات للإرسال بناء على الأهمية

- Streaming Session حيث تدعم عملية النقل كStream Data ,

قدمت Microsoft هذه الحلول كبدائل لدعم بروتوكول الـSCTP المنتظر , وقد حلت جميع المشكلات التي كانت تواجه المبرمجين لنقل الصوت عبر بروتوكول الـTCP أو الـUDP وحل محله أسلوب آخر لربط ضمن مستوى طبقة الـ Transport Layer , وتحتوي الـ Direct Play على مجموعة ضخمة من الـClasses ومن أهمها :

أولا : الـ Connect Classes والخاصة بعملية الربط:

الـ Address , Guid , Peer Classes حيث تستخدم عند إنشاء الاتصال مع الطرف الأخر , وتستخدم DirectPlay طريقة لتمييز البرنامج عن الأخر بتوليد Hash Code خاص بكل برنامج ويتم ذلك باستخدام الـ Guid Class ويتم تمرير الكود المولد وعنوان الجهاز المقابل إلى الـ Peer Class وهذه العملية شبيهة بشكل كبير لعملية الربط باستخدام الـ Socket في بروتوكول TCP/IP , ويتم استخدامها كما يلي كمثال:

using Microsoft.DirectX.DirectPlay;
.
.
.
Address hostAddress = new Address();
hostAddress.ServiceProvider = Address.ServiceProviderTcpIp;  
                                  // Select TCP/IP service provider

ApplicationDescription dpApp = new ApplicationDescription();
appGuid = Guid.NewGuid();          // Create a GUID for the application
dpApp.GuidApplication = appGuid;   // Set the application GUID
dpApp.SessionName = "My Session";  // Optional Session Name

myPeer.Host(dpApp, hostAddress);   // Begin hosting

طبعا يجب الاختيار طبيعة البروتوكول المستخدم سواء كان TCP/IP أو IPX وحتى نستطيع وضع العنوان المقابل أو الـ IP Address ويتم ذلك كما يلي:

using Microsoft.DirectX.DirectPlay;
ServiceProviderInfo[] mySPInfo;
Peer myPeer;
System.Windows.Forms.ListBox listBox1;
ApplicationDescription myAppDesc;
.
.
myPeer = new Peer();
hostAddress = new Address();
peerAddress = new Address();

// Set the service provider to TCP/IP
peerAddress.ServiceProvider = Address.ServiceProviderTcpIp;
hostAddress.ServiceProvider = Address.ServiceProviderTcpIp;

// Attach FindHostResponseEventHandler to receive FindHostResponseMessages
myPeer.FindHostResponse += new FindHostResponseEventHandler(myEnumeratedHosts);

// Call FindHosts to start the enumeration
myPeer.FindHosts(myAppDesc, hostAddress, peerAddress, null, 10, 0, 0,
         FindHostsFlags.OkToQueryForAddressing);

حيث تم تعريف نوع البروتوكول المستخدم وهو TCP/IP ويتم البحث عن الطرف الأخر في الشبكة باستخدام الـ FindHosts Method والموجودة ضمن الـPeer Class , وتتم عملية الربط مباشرة باستخدام الـ Connect Method والموجودة ضمن الـ Peer Class وكما يلي:

using Microsoft.DirectX.DirectPlay;
// Structure for FindHostResponseMessages
public struct HostInfo
{
public ApplicationDescription appdesc;
public Address deviceAddress;
public Address senderAddress;

}
.
.
.
Peer myPeer = new Peer();
HostInfo hostinfo = new HostInfo();

// The FindHostResponseEventHandler
public void myEnumeratedHosts(object o, FindHostResponseEventArgs args)
{
hostinfo.appdesc = args.Message.ApplicationDescription;
hostinfo.deviceAddress = args.Message.AddressDevice;
hostinfo.senderAddress = args.Message.AddressSender;
}

// Attach the ConnectCompleteEventHandler to receive ConnectCompleteMessages
myPeer.ConnectComplete += new ConnectCompleteEventHandler(OnConnectComplete);

// Call connect passing the Host information returned in the FindHostResponse event
myPeer.Connect(hostinfo.appdesc, hostinfo.deviceAddress, hostinfo.senderAddress, null, ConnectFlags.OkToQueryForAddressing);

ثانيا: Microsoft.DirectX.DirectPlay.Voice والخاصة بكل عمليات تسجيل ونقل وعرض الصوت:

تمر عملية تسجيل ونقل وعرض الصوت بمجموعة من المراحل ونلخصها بالشكل التالي:

VOIP_img5.JPG

وسوف نقسمها إلى مجموعة من الـ Classes حسب الوظيفة لكل منها ,

1- الـ Classes الخاصة بطرف الـ Server لإنشاء وإدارة الـ Sessions :

Server Class ويستخدم كما يلي كمثال:

    Server server = new Voice.Server(peerObject);

حيث نسند له ال Peer Object والذي تم اشتقاقه من الـ Peer Class , ويحتوي الـ Server Object على الـ Methods الخاصة بعملية بدأ وإنهاء الجلسة بلإضافة إلى مجموعة من العمليات الأخرى:

VOIP_img6.JPG

ولبدأ الجلسة يجب أولا إسناد خصائص الجلسة إلى الـStartSession Method حيث يتم تعريفها من خلال الـ SessionDescription و كما يلي:

//set up session description for the voice server
Voice.SessionDescription sessionDesc = new Voice.SessionDescription();
sessionDesc.BufferAggressiveness = Voice.BufferAggressiveness.Default;
sessionDesc.BufferQuality = Voice.BufferQuality.Default;
sessionDesc.Flags = 0;
sessionDesc.SessionType = type;
sessionDesc.GuidCompressionType = compressionType;

ولإنشاء Voice Session نقوم بإنشاء Method نمرر لها ال Peer Object والـ Voice Session Type ونوع الضغط المستخدم Compression Type ويتم ذلك كما يلي:

protected void CreateVoiceSession(Peer dpp, Voice.SessionType type, Guid compressionType)
   {
try
    {
//set up session description for the voice server
Voice.SessionDescription sessionDesc = new Voice.SessionDescription();
sessionDesc.BufferAggressiveness = Voice.BufferAggressiveness.Default;
sessionDesc.BufferQuality = Voice.BufferQuality.Default;
sessionDesc.Flags = 0;
sessionDesc.SessionType = type;
sessionDesc.GuidCompressionType = compressionType;

//start the session
try
           {
               server.StartSession(sessionDesc);
               mIsHost = true;
               mInSession = true;
           }
           catch(DirectXException dxe)
           {
               throw dxe;
           }
       }
       catch(Exception e)
       {
           throw e;
       }
   }

ويتم استدعائها كما يلي:

CreateVoiceSession(host, Voice.SessionType.Peer, mConfigForm.CompressionGuid);

2- الـ Classes الخاصة بطرف الـ Client للاتصال مع الـ Sessions التي أنشئها الـServer:

الـ Client Class ومن أهم الـMethods الموجودة في الـ Client Class :

VOIP_img7.JPG

الميثود Connect وتستخدم لربط مع الVoice Session حيث يرسل الـ Server رقم الSession للـ Client وعندها يتمكن الـ Client من الدخول إلى الجلسة , ويتم ذلك كما يلي:

    protected void ConnectToVoiceSession(Peer dpp, Form wnd)
   {
       try
       {
Voice.SoundDeviceConfig soundConfig = new Voice.SoundDeviceConfig();


//Set sound config to defaults
soundConfig.GuidPlaybackDevice  = DSoundHelper.DefaultVoicePlaybackDevice;
soundConfig.GuidCaptureDevice = DSoundHelper.DefaultVoiceCaptureDevice;
soundConfig.Window = wnd;
     
//TODO: add error message for specific failures?
     
//Connect to voice session
client.Connect(soundConfig, mClientConfig, Voice.VoiceFlags.Sync);

//set state
mInSession = true;

//set transmit targets to all players
int[] xmitTargets = new int[1];
xmitTargets[0] = (int) PlayerID.AllPlayers;
client.TransmitTargets = xmitTargets;

//get sound device config to check for half-duplex
soundConfig = client.SoundDeviceConfig;
mHalfDuplex =  ((soundConfig.Flags &  Voice.SoundConfigFlags.HalfDuplex) != 0);

}
catch(Exception e)
{throw e;}
   }
}

لاحظ أن المشاكل في البروتوكولين الـ TCP والـUDP قد تم حلها في الـ DirectPlay لكن وكما هو معروف فإن الهدف من إنشاء الـ Direct Play لم يكن سوى لدعم برمجة الألعاب ومع المرونة الكبيرة التي تقدمها الـ Direct Play في عملية الاتصال الصوتي إلا أنها تفتقر لميزات الـ Voice Over IP والتي يقدمها بروتوكول الـ SCTP الخاص بالـ Linux...

وهكذا بينا ملخص عن أهم الطرق لاتصال الصوتي عبر الشبكة وطرق برمجة الـ Voice Chat تحت منصة الدوت نيت واهم ميزات وعيوب بروتوكولات الـTransport Layer ومدى إمكانياتها لنقل الصوت عبر الشبكة وأخيرا شرح لأهم الـ Classes والمستخدمة في الاتصال الصوتي باستخدام الـ DirectPlay Transport Protocol.

------------------------------------------------------------------------------

مثال الـ Voice Chat System والـ DirectPlay Voice Chat System بلإضافة إلى الفصل على شكل ملف PDF File ---- كل ذلك بالمرفقات

------------------------------------------------------------------------------

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

لا ادري سبب عدم تمكن المنتدى من رفع أي مرفقات ( ارجو الإسراع في حل المشكلة ) , تستطيع تحميلها الآن من الرابط التالي :

http://arabmoheet.net/forum/default.asp?co...&reply_no=23405

و

http://arabmoheet.net/forum/default.asp?co...e=6entry23405

بتوفيق

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

شكرا جزيلا لك اخي فادي ... وبارك الله فيك ;)

انتا عملاق -_-

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

اشكرك اخي بهاء واخي islam_khalil , وجزاكم الله خيرا

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

ما شاء الله تبارك الله ،

عندما بدأت كنت أظنها كم درس في الشات ثم تتوقف ، لم أتصور أنك ستواصل إلى هذا المستوى المتقدم .

هنيئاً لنا بك .

انتظر مني رسالة على الإيميل .

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

شكرا جزيلا لجهودك وان من المتابعين لك في هذا الموضوع القيم جدا......

لكن لم استطع تحميل الأمثلة لخطأ في العضوية

و استفسار آخر بخصوص (voice tutorial example 8)

وجربته على جهازين لا يوجد اتصال صوتي ولكن هناك the connection is good and text chat is ok

:angry:

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

المثال الموضوع مجرب 100 % لكن تحدث المشكلة عادة في الـFirewall الخاص بالـ Windows XP إذ أنه يمنع الإتصال كـ Multicast ولا يعمل الـ Multicast إلا في حالة إطفائه , وتأكد ايضا من اعدادات المايكروفون جرب تسجيل صوت باستخدام برنامج الـ Sound Recorder والموجود في الـWindows لإختبار الإعدادات

وسأحاول ارفاق الدرس والأمثلة في المرفقات , إذ انها لم تنجح معي في المرة السابقة

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

لم ينجح رفع اي شيء وقد قمت بضغط على (اضف هذا المرفق) لكن لم ينجح معي اي شيء :( :(

ارجو الإسراع في تصحيح هذ الخطأ :(

على اي حال ربما يلزم تحميل الدرس والأمثله التسجيل بالمنتدى الموضوع فيه

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

هذا رابط اخر تستطيع تحميل الدرس والأمثلة منه

http://rapidshare.de/files/16908177/Chapte...amming.zip.html

تم تعديل بواسطه fmo_82
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

تم بحمد الله الإنتهاء من الإصدار الثاني من الكتاب الإلكتروني المجاني

راجع الرابط التالي:

http://www.arabteam2000-forum.com/index.php?showtopic=91820

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

مين قال إن المجهود جبار ؟؟

كلمة جبار ده قليلة جدا جدا علي المجهود ده

ما شاء الله يا أخي

بارك الله فيك

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

الله يعطيك العافيه على هذا المجهود الكبير

والكتاب اكثر من رائع

وبالتوفيق.....

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

بخصوص (voice tutorial example 8)من امثله ال dx9sdkdirectplay

وجربته على جهازين لا يوجد اتصال صوتي ولكن هناك the connection is good and text chat is ok

في الـFirewall الخاص بالـ Windows XP عملت على إطفائه , وتأكدت ايضا من اعدادات المايكروفون وقرأت ال DirectX Documintation for c for this example يجب ان تظهر sound wizard في أول مره تشغيل و icons must be active لكن لم يحصل شيء :^) ارجو التكرم بتجريب المثال المرفق لاني انوي باستخدامه كجزء من مشروعي التخرج

وشكرا جزيلا لجهودك.....

Tut08_Voice.rar

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

أخي الحبيب : فاااادي

لك جزيل الشكر .. والشكر في حقك قليل ... ولكن أرجو أن تقبل مننا هذا القليل

أخي الكريم ... أنا بإنتظار موضووعك القااادم على أحر من الجمر ( Sniffring )

أرق المُنى لك أخي المُبدع ... في أمان الله

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

اخي C#please و osamazy و bmsaa2002 جزاكم الله كل خير , سيكون بإذن الله درس الـ Sniffring هو الدرس التالي وسأحاول التفرغ قدر الإمكان لتجهيزه

سأقوم بتجربة المثال الذي ارفقته اخي osamazy واخبرك بالنتيجة إن شاء الله , لكن لماذا لم تستخدم المثال المرفق مع درس VOIP فهو مجرب بشكل كامل ويعمل بدون اي مشاكل

لكم مني اطيب التحية

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
شكرا أخي الكريم علي هذة الموسوعة القيمة وبارك الله فيك :wacko: تم تعديل بواسطه mad
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

أخ فادي

لك كل الا متنان والشكر لموضوع الصوت خوصا

لدي طلب اطلب (command) لشفره الصوت وارساله الموجوده في المتال المرفق في الفصل 11 لتبين عمل الدوال وغيره .....

ولك جزيل الشكر

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

اخي لم امفهم طلبك ارجو التوضيح , يحتوي الفصل على شرح معظم الدوال , إذا كانت هنالك دالة غير واضحة اكتبها وسأوضحها لك :)

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

اخ فادي

اشكرك على ردك

سوالي هو (command ) اريده في البرنامج المنفد للصوت الموجود في الفصل 11 ؟ متلا (wavein & wave out ) كل واحد تكتب له

command لسهوله فهم كود

ولك جزيل الشكر

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

أ خ فادي اطلب منك (command ) الموجوده لكل (class or method ) في برنامج الصوت المنفد المرفق في الفصل 11 ولك جزيل الامتتننان

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

هذا مثال فيه Tow Projects واحد يرسل الصوت والأخر يستقبله واستخدمت الـ Multicast IP فيه ..

طبعا يجب ارسال الصوت بعد تحويله إلى Byte Buffer وكما يلي:

private void DataArrived(IntPtr data, int size)
{
if (m_RecBuffer == null || m_RecBuffer.Length < size)
m_RecBuffer = new byte[size];
System.Runtime.InteropServices.Marshal.Copy(data, m_RecBuffer, 0, size);
socket.SendTo(m_RecBuffer, new IPEndPoint(IPAddress.Parse("224.0.1.7"), 5000));
}

ثم يتم استقباله في الطرف الأخر ويعبأ في الميثود Filler ليتم عرضه على اللسبيكر , وهذا هو كود الإستقبال :

افترضنا ان اي شخص ينضم إلى الـ IP Multicast Group سيستقبل الصوت...

private void Voice_In()
{
UdpClient sock = new UdpClient(5000);
sock.JoinMulticastGroup(IPAddress.Parse("224.0.1.7"));
IPEndPoint iep = new IPEndPoint(IPAddress.Any,0);

while (true)
{
m_Fifo.Write(sock.Receive(ref iep), 0,sock.Receive(ref iep).Length);
}
}

ارجو ان تكون الصورة قد تم ايضاحها الآن واذا واجهتك اي صعوبة بالكود اسأل عنها :)

Half_one_to_many_Multicast.zip

تم تعديل بواسطه fmo_82
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

انتظروا واحد من أهم الفصول في الكتاب سيتم قريبا نشر جزء كبير منه وهذه محتويات الدرس القادم

02_05_06_06_36_57_1146577017Chapter15.GIF

:rolleyes:

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

بسم الله الرحمن الرحيم , سأقوم بتجزئة الموضوع إلى جزئين نظرا لكبره وسأنشر في البداية الجزء الأول وإذا وجدت التفاعل الجاد سأنشر الجزء الثاني بإذن الله :rolleyes:

03_05_06_04_31_32_1146699092Topics.GIF

الجزء الأول:

أولا- The Serialization Programming :

The Serialization Classes & Members : يمكننا من خلال الـSerialization عمل Serializing لـ Object من جهاز إلى آخر سواء عبر الشبكة المحلية باستخدام الـ Stream Socket أو عبر الإنترنت باستخدام الـ Web Services والـRemotting , ويمكننا استخدام الـ Serialization في الدوت نيت من خلال الـ System.Runtime.Serialization Namespace حيث يحتوي هذا الـ Namespace على عدد ضخم من الـ Classes والتي تساعد في عمل Serialization لأي Object عبر الإنترنت أو الشبكة المحلية, ويبين الجدول التالي أهم هذه الـ Classes والتي يمكننا الاستفادة منها في بناء النظم الموزعة:

Formatters , Formatter & FormatterServices : ويحتوي هذا الـ Class على كل الوظائف الرئيسية لعمل Serialization لأي Object ومنها يستخدم الـ Serialize Method لإرسال Serializing Object عبر الـ Stream

والـ Deserialize والتي تستخدم للاستقبال الـ Serializing Object من الـStream و تحويله إلى Object مرة أخرى.

وتأخذ عملية الإرسال نوعين هما الإرسال عبر بروتوكول

الـ Soap ويأتي ضمن الـ Namespace

System.Runtime.Serialization.Formatters.Soap حيث يجب إضافته إلى الـ Reference للمشروع وقد بينا في الفصل السابق كيفية عمل بروتوكول الـ SOAP حيث يحول الـ Object إلى XML Serial Stream , والطريقة الثانية هي بتحويل الـObject إلى Binary Serial Stream ويأتي ضمن الـNamespace :

System.Runtime.Serialization.Formatters.Binary والذي سيتم شرحه بالتفصيل في الأمثلة القادمة في هذا الفصل.

FormatterConverter : حيث يحتوي على الـ IFormatter Interface والـ IConvertible Interface لأستخدمهما في عمليات تمثيل الـ Object حتى يمكن إرساله عبر الـ Serializing Stream ...

ObjectIDGenerator : ويستخدم لتوليد رقم ID عشوائي لأي Object حيث يحتوي هذا الـ Class على Tow Methods الأول يقوم بتوليد رقم تسلسلي للـ Object يسمى GetID والثاني يرجع قيمة الـ ID الخاص بالـ Object ويسمى HasId ويأخذ كل منهما اسم

الـ Object الذي نريد توليد رقم تسلسلي له وقيمة True أو False لتحديد فيما إذا كانت هذه هي المرة الأولى التي تم فيها توليد رقم تسلسلي للـ Object أم لا...

GetId ( object_name,out bool) -->to Set an ID 
HasId( object_name,out bool) --> to Return the ID

SerializationInfo : ويحتوي على كل المعلومات التي يمكن أن نحتاج لها لعملية

الـ Serialize و الـ Deserialize لأي Object مثلا AssemblyName و الـ FullTypeName والـ MemberCount وتحويلات الـ Data Types للـ Objects وغيرها...

لإنشاء أي Serializable Class يجب أولا أن يعرف بالـ [serializable] Attribute حيث يعرف أن هذا الـ Class من الـ Classes التي يمكن أن يتم عمل Serializing عليها وكما يلي كمثال لعمل Serializable Class للـ Bank_Tier الخاص بالمثال السابق:

C#:
[Serializable]
public class Bank_Tier
{
public string Customer_Name;
public string Address;
public int zip_code;
public string payment_method;
public string ecard_number;
public float depositor;
public string acount_number;

public Bank_Tier()
{
// Get_Session_ID();
}
}

VB.NET:
<Serializable()> _
Public Class Bank_Tier
   Public Customer_Name As String
   Public Address As String
   Public zip_code As Integer
   Public payment_method As String
   Public ecard_number As String
   Public depositor As Single
   Public acount_number As String

   Public Sub New()
       ' Get_Session_ID()
   End Sub
End Class

ولإنشاء البرنامج الخاص بعملية الإرسال لابد من تحديد طريقة الإرسال سواء كـBinary Stream Data أو XML Data ففي الأول سنستخدم الـ BinaryFormatter والموجود ضمن الـ System.Runtime.Serialization.Formatters.Binary Namespace أما الثاني فسيستخدم مع الـ SOAP Protocol والمعرف ضمن الـ SOAPFormatter Class والموجود ضمن الـ System.Runtime.Serialization.Formatters.Soap Namespace.

ولعمل Serialization للـ Class السابق باستخدام الـ SOAP Formatter لابد أولا من تعريف الـ Namespaces التالية:

System.Runtime.Serialization
System.Runtime.Serialization.Formatters.Soap

ثم نقوم بعمل Instance من الـ Bank Web Services حيث نستطيع فيما بعد إسناد المعلومات الآتية من الـ Presentation Tier إليه ثم إرسالها إلى الـ Bank Server باستخدام الـ Remotting أو الـNetworkStream أو حفظها على الجهاز كـ Binary Data أو XML File وكما يلي كمثال:

لتخزين المخرجات بـ XML File باستخدام بروتوكول الـ SOAP :

C#:
Bank_Tier bt = new Bank_Tier();
bt.acount_number = acount_number;
bt.Address = Address;
bt.depositor=depositor;
bt.ecard_number = ecard_number;
bt.payment_method = payment_method;
bt.zip_code = zip_code;
 
Stream str = new FileStream("bank_result.xml", FileMode.Create,
FileAccess.ReadWrite);

IFormatter formatter = new SoapFormatter();
formatter.Serialize(str, bt);
str.Close();    

VB.NET:
Dim bt As Bank_Tier = New Bank_Tier
bt.acount_number = acount_number
bt.Address = Address
bt.depositor=depositor
bt.ecard_number = ecard_number
bt.payment_method = payment_method
bt.zip_code = zip_code

Dim str As Stream = New FileStream("bank_result.xml", FileMode.Create, FileAccess.ReadWrite)

Dim formatter As IFormatter = New SoapFormatter
formatter.Serialize(str, bt)
str.Close()

وسكون الـ Output ملف XML يحتوي على البيانات التي تم إدخالها وكما يلي:

- <SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
- <SOAP-ENV:Body>
- <a1:Form1_x002B_Bank_Tier id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/Serialization/Serialization%2C%20Version%3D1.0.2309.39316%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
 <Customer_Name xsi:null="1" />
 <Address id="ref-3">Amman Jordan</Address>
 <zip_code>962</zip_code>
 <payment_method id="ref-4">Master Card</payment_method>
 <ecard_number id="ref-5">66-6655-444433-55655</ecard_number>
 <depositor>600</depositor>
 <acount_number id="ref-6">3399948845</acount_number>
 </a1:Form1_x002B_Bank_Tier>
 </SOAP-ENV:Body>
 </SOAP-ENV:Envelope>

وبتأكيد نستطيع استخدام هذا المثال لإرسال البيانات عبر الـ Network Stream أو باستخدام الـ Remotting , كما يمكننا الاستفادة من الـ Serialization لتمثيل أي Object على شكل XML أو Binary Data لاستخدامها في أمور أخرى...

سنبين في المثال التالي كيفية الاستفادة من الـ BinaryFormatter Class لإرسال Object أو Binary Data من جهاز إلى آخر عبر الشبكة كما سنبين كيفية الاستفادة من الـ SOAP Protocol لإرسال Binary Data كـ XML Stream:

وكمثال نريد إرسال صورة من جهاز إلى آخر بعد تحويلها إلى Binary Stream باستخدام الـ MemoryStream وكما يلي:

أولا : باستخدام الـ BinaryFormatter في برنامج الإرسال:

C#:
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
.
.
private void Send_Image_Using_BinaryFormatter
{    
MemoryStream ms = new MemoryStream ();
pictureBox1.Image.Save (ms,System.Drawing.Imaging.ImageFormat.Jpeg);
object op = (object) ms;

BinaryFormatter br = new BinaryFormatter ();
 
TcpClient myclient = new TcpClient ("localhost",5000);
NetworkStream myns = myclient.GetStream ();
br.Serialize (myns,op);

myns.Close ();
myclient.Close ();  
}

VB.NET
Imports System.Runtime.Serialization.Formatters.Binary
Imports System.Runtime.Serialization
.
.
Private Property Send_Image_Using_BinaryFormatter() As void
Dim ms As MemoryStream = New MemoryStream ()
pictureBox1.Image.Save (ms,System.Drawing.Imaging.ImageFormat.Jpeg)
Dim op As Object = CObj(ms)

Dim br As BinaryFormatter = New BinaryFormatter

Dim myclient As TcpClient = New TcpClient("localhost", 5000)
Dim myns As NetworkStream = myclient.GetStream()
br.Serialize (myns,op)

myns.Close ()
myclient.Close ()
End Property

استخدام الـ BinaryFormatter في برنامج الاستقبال:

ولاستخدام بروتوكول الـ BinarFormatter في برنامج الاستقبال سنستخدم الميثود المعاكسة للـ Serialize وهي Deserialize ثم نقوم بعمل Casting للـ Object المستقبل ونحوله إلى MemoryStream Object مرة أخرى , عندها نستطيع عرضه على الـ Picture box باستخدام الـ FromStream Method والموجودة ضمن الـ Image Class :

C#:
void Image_Receiver()
{

NetworkStream myns;
TcpListener mytcpl;
Socket mysocket;
Thread myth;

mytcpl = new TcpListener (5000);
mytcpl.Start ();
mysocket = mytcpl.AcceptSocket ();
myns = new NetworkStream (mysocket);
BinaryFormatter br = new BinaryFormatter ();
object op;
 
op= br.Deserialize (myns); // Deserialize the Object from Stream
MemoryStream mm = (MemoryStream) op; // Casting The Object to MemoryStream Object
pictureBox1.Image = Image.FromStream(mm);
mytcpl.Stop();
if (mysocket.Connected ==true)
{while (true){Image_Receiver();}}
}
.
.
private void server_Load(object sender, System.EventArgs e)
{
Thread myth;
myth= new Thread  (new System.Threading .ThreadStart(Image_Receiver));
myth.Start ();
}

VB.NET
Imports System.Runtime.Serialization.Formatters.Binary
Imports System.Runtime.Serialization
.
.
Private Sub Image_Receiver()

   Dim myns As NetworkStream
   Dim mytcpl As TcpListener
   Dim mysocket As Socket
   Dim myth As Thread

   mytcpl = New TcpListener(5000)
   mytcpl.Start()
   mysocket = mytcpl.AcceptSocket()
   myns = New NetworkStream(mysocket)
   Dim br As BinaryFormatter = New BinaryFormatter
   Dim op As Object

   op = br.Deserialize(myns) ' Deserialize the Object from Stream
   Dim mm As MemoryStream = CType(op, MemoryStream) ' Casting The Object to MemoryStream Object

   pictureBox1.Image = Image.FromStream(mm)
   mytcpl.Stop()

   If mysocket.Connected = True Then
       Do While True
           Image_Receiver()
       Loop
   End If
End Sub
.
.
Private Sub server_Load(ByVal sender As Object, ByVal e As System.EventArgs)
   Dim myth As Thread
   myth = New Thread(New System.Threading.ThreadStart(AddressOf Image_Receiver))
   myth.Start()
End Sub

ثانيا : استخدام الـ SOAP في برنامج الإرسال:

حيث سترسل الصورة كـ XML Data ثم تحول في الطرف المقابل إلى صورة مرة أخرى وكل ذلك يتم باستخدام بروتوكول الـ SOAP وكما يلي في برنامج الإرسال:

C#:
using System.Runtime.Serialization.Formatters.Soap;
using System.Runtime.Serialization;
.
.
private void Send_Image_Using_ SoapFormatter
{
server fr = new server ();
fr.Show();
 
MemoryStream ms = new MemoryStream ();
pictureBox1.Image.Save (ms,System.Drawing.Imaging.ImageFormat.Jpeg);
object op = (object) ms;
 
TcpClient myclient = new TcpClient ("localhost",5000);
NetworkStream myns = myclient.GetStream ();

IFormatter formatter = new SoapFormatter();
formatter.Serialize(myns, op);
myns.Close();
}

VB.NET:
Imports System.Runtime.Serialization.Formatters.Soap
Imports System.Runtime.Serialization
.
.
Private void Property SoapFormatter() As Send_Image_Using_
Dim fr As server = New server
fr.Show()

Dim ms As MemoryStream = New MemoryStream
pictureBox1.Image.Save (ms,System.Drawing.Imaging.ImageFormat.Jpeg)
Dim op As Object = CObj(ms)

Dim myclient As TcpClient = New TcpClient("localhost", 5000)
Dim myns As NetworkStream = myclient.GetStream()

Dim formatter As IFormatter = New SoapFormatter
formatter.Serialize(myns, op)
myns.Close()
End Property

استخدام الـ SoapFormatter في برنامج الاستقبال:

ولاستخدام بروتوكول الـ SOAP في برنامج الاستقبال سنستخدم الميثود المعاكسة للـ Serialize وهي Deserialize ثم نقوم بعمل Casting للـ Object المستقبل ونحوله إلى MemoryStream Object مرة أخرى , عندها نستطيع عرضه على الـ Picture box باستخدام الـ FromStream Method والموجودة ضمن الـ Image Class :

C#:
using System.Runtime.Serialization.Formatters.Soap;
using System.Runtime.Serialization;
.
.
void Image_Receiver()
{
NetworkStream myns;
TcpListener mytcpl;
Socket mysocket;

mytcpl = new TcpListener (5000);
mytcpl.Start ();
mysocket = mytcpl.AcceptSocket ();
myns = new NetworkStream (mysocket);

object op;
IFormatter formatter = new SoapFormatter();
op= formatter.Deserialize(myns);
myns.Close();

MemoryStream mm = (MemoryStream) op;
pictureBox1.Image = Image.FromStream(mm);  
 
mytcpl.Stop();

if (mysocket.Connected ==true)
{
while (true)
 {Image_Receiver();}
}
}
.
.
private void server_Load(object sender, System.EventArgs e)
{
Thread myth;
myth= new Thread  (new System.Threading .ThreadStart(Image_Receiver)); // Start Thread Session
myth.Start ();
}

VB.NET:
Imports System.Runtime.Serialization.Formatters.Soap
Imports System.Runtime.Serialization
.
.

Private Sub Image_Receiver()

   Dim myns As NetworkStream
   Dim mytcpl As TcpListener
   Dim mysocket As Socket

   mytcpl = New TcpListener(5000)
   mytcpl.Start()
   mysocket = mytcpl.AcceptSocket()
   myns = New NetworkStream(mysocket)

   Dim op As Object
   Dim formatter As IFormatter = New SoapFormatter
   op = formatter.Deserialize(myns)
   myns.Close()

   Dim mm As MemoryStream = CType(op, MemoryStream)
   pictureBox1.Image = Image.FromStream(mm)

   mytcpl.Stop()

   If mysocket.Connected = True Then
       Do While True
           Image_Receiver()
       Loop
   End If
End Sub
.
.
Private Sub server_Load(ByVal sender As Object, ByVal e As System.EventArgs)
   Dim myth As Thread
   myth = New Thread(New System.Threading.ThreadStart(AddressOf Image_Receiver)) ' Start Thread Session
   myth.Start()
End Sub

وهكذا بينا كيفية استخدام الـ Serialization في بيئة الدوت نيت بنوعيها الـ Binary والـXML Data , سيتم الحديث في الجزء التالي من هذا الفصل عن الـ Remotting واستخدامها في برمجيات الشبكات والنظم الموزعة , وسنقوم بتطبيق إنشاء نظام eLearning Distributed System ... :)

Serialization_Example.zip

تم تعديل بواسطه fmo_82
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

من فضلك سجل دخول لتتمكن من التعليق

ستتمكن من اضافه تعليقات بعد التسجيل



سجل دخولك الان

  • يستعرض القسم حالياً   0 members

    لا يوجد أعضاء مسجلين يشاهدون هذه الصفحة .