الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

جمع الخبرات لانتاج برنامج شات يعمل محليا

بدأه شوق المسافر في 12 أغسطس 2010 · 5 رد · 2,028 مشاهدة · في برمجة الشبكات والتطبيقات الموزعة
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم اخواني

اولا اشكر استاذ فادي على الدروس المهمة اللي من خلالها انجزت جزء من مشروع تخرجي ولحد الان ماقدرت اكمل الباقي لانو بصراحة حسيت انو يصعب شوي شوي

طبعا مشروع التخرج

1-الكتابة النصية بين اجهزة الكلاينت

2-ارسال الملفات بين اجهزة الكلاينت

3-ارسال صور بين اجهزة الكلاينت

4-ارسال كاميرة ويب بين جهازين كلاينت

5-المحادثة الصوتية لشخص واحد وكل اجهزة المتبقية تسمعه الكلاينت تسمعه

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

طبعا اعتقد انو المشروع ضخم جدا ويحتاج وقت ولكن بصراحة حبيت انزل المشروع هنا في المنتدى وقلت اخلي الكل يستفاد وانا ايضا استفاد من الخبرات وبنفس الوقت انجز مشروع التخرج

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

أنظرو للصورة

doD10562.jpg

الصورة تبين مبدا عمل البرنامج يعني بصراحة وبالمختصر المفيد مافي شي ينتقل بين الاجهزة الا بعلم السيرفر وهذا الشي راح يساعدنا كثير بالتحكم بالاجهزة مثلا نريد منع خاصية الكتابة او الكاميرا او نقل الملفات الخ....

انا بصراحة انجزت عمليات الكتابة يعني اول خطوة من المشروع وتمت بنجاح ولكن الان اريد انجاز عملية امنية وهي

=====وضع اسم وباسورد للدخول=====

يعني اي مستخدم يريد يدخل بيحط اسم وباسورد بالاضافة الى ايبي السيرفر والبورت (طبعا هنا البورت 5000) ولما يتصل بالسيرفر السيرفر بيفحص البيانات للشخص من خلال مصفوفة مخزون فيها اسماء وايبيات وباسوردات المستخدمين

طبعا هذي الاسماء بيتحكم فيها المسؤول عن نسخة السيرفر بصراحة انا فكرت بقاعدة بيانات ولكن اعتقد فيها شوي صعوبة ولهذا فكرت بعمل مصفوفة بنسخة السيرفر

هل احد لدية الفكرة عن طريقة البرمجة؟؟

وكيف سنرسل البيانات الى السيرفر والسيرفر كيف سيعرف انو هذه بيانات تسجيل العضو وليست بيانات نقل ملف او غيرها؟

اتوقع انا فكرت بتغير البورت (يارب تكون فكرتي صحيحة) يعني لتسجيل الدخول هنالك بورت وللكتابة بورت وللمايك بورت وللكاميرات بورت ....وهكذا

هذا اولا .

ثانيا هنالك شي مهم وهو ارسال الى جميع نسخ الكلاينت المتواجدين حالياَ وهذا فكرته انو اذا اتصل احد بالسيرفر اكيد رح يفحص الاسم والباسورد

وبعدها يقوم السيرفر ارسال رسالة broadcast الى الجميع بالمتواجدين حاليا (اعتقد شوي صعب) .

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

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

ملاحظة/

1- البرمجة بلغة سي شارب 2005 او الفجيوال ستوديو 2008 (اشتغلت على الاثنين)

2-السيرفر اخذته من احد برامج الاستاذ فادي بس لحتى يتم تجربة البرنامج

3-المشروع يعمل على شبكة داخلية وليس على الانترنت

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

ارفق لكم المشروع طبعا في بعض الازرار لا تعمل يعني يحتاجلها برمجة (لا تتفاجأوو)

تحياتي للجميع

رابط التحميل بالمرفقات

رابط التحميل.txt

تم تعديل هذه المشاركة بواسطة فادي عبدالقادر في 17 أغسطس 2010 في 23:21 — السبب: تغيير العنوان

#2

وعليك السلام اخي الكريم,

اولا بخصوص عملية التحقق فالأفضل عمل قاعدة بيانات وربطها بـ Web Service وكمثال تعمل دالة ترسل لها اسم المستخدم وكلمة المرور ثم يتم التحقق على السيرفر وارجاع النتيجة بقيمة true or false طبعا يوجد الكثير من الطرق لتحقق اكثر احترافية لكن كمشروع تخرج هذا سينجز العمل.

ثانيا بخصوص استخدام اكثر من بورت فالأفضل استخدمها كالتالي: بورت لصوت ولصورة واخر لتراسل النصي وحيث يمكنك مثلا عمل Text Commands لتمييز الرسائل عن الأوامر الأخرى, كمثال على ذلك بإمكانك تحميل هذا المشروع

http://www.socketcoder.com/Products.aspx?index=1

حيث يوجد به نسخة تجارية احترافية واخرى مجانية مفتوحة المصدر للأهداف التعليمية فقط علما ان المشروع يعمل على VS2010 و Silverlight 4 لكن ما ستستفيد منه فعليا هو الأفكار المطبقة في المشروع من ناحية الغرف المتعددة والتراسل النصي والصوتي والفديو عبر الإنترنت حيث يمكنك تطبيقها بأي اصدار كالإصدار الذي تستخدمه في مشروعك.

ثالثا ارجو في المرات القادمة مراعاة قوانين طرح الأسئلة في القسم حيث العنوان المناسب لسؤال وكذلك محاولات الحل.

واهلا وسهلا بك

#3

شكرا على ردك اخ فادي وبارك الله فيك

1- حبيت اعدل اسم الموضوع بس بصراحة ما عرفت كيف اغير ياريت من المشرفين يغيرو اسم الموضوع الى (جمع الخبرات لانتاج برنامج شات يعمل محليا )

2-انا درست عمليات Asynchronous Sockets واكثر من مرة وفهمتها بشكل ممتاز ولكن الذي لم افهمه عند التطبيق على السيرفر(المرفق للاستاذ فادي)في بعض الامور لم افهمها من دوال وطريقة تنفيذ السيرفر برمجيا فياريت تشرح لنا استاذ فادي فائدة كل دالة وما عملها ولماذا وضعت وكيف رح ياتي التنفيذ عليها (بعرف تعب عليك) بس شو نعمل اتحملنا شوي تحياتي لك..

//
//                      (C) Fadi Abdelqader - SocketCoder.Com 2009
// SocketCoderTextServer Class is a part of SocketCoder Classes Project (C) SocketCoder.Com
//                   - This Project Is Created to Work Behind The NAT - 
//                - Just The Server Should be on a PC that has a public IP - 
//
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.Collections;
using System.Linq;

namespace SocketCoder_WCService
{
    class SocketCoderTextServer
    {
        static ArrayList ClientsList= new ArrayList();//تكوين مصفوفة لخزن الكلاينت

        static Socket Listener_Socket;//تكوين سوكت
        static byte[] RBuffer = new byte[280];//تكوين مصفوفة بايت
        static Socket NSock;//تكوين سوكت اخر
        static string RoomIDChar = "(R#!)";//متغير 

        static string SpecialChar = "(!#!)";
        static string FFlag = "(!F!)"; // Final Flag The End of the meesage 
        static string TJoin = "0";// To Send Join Message
        static string TDrop = "1";// To Send Drop List Message
        static string TCmd = "2"; // To Send Command Message 
        static string TMsg = "3"; // To Send Normal Message
        static string TidBack = "4"; // To Send User ID Message if somebody joined
        static string TFileS = "5"; // To Send File Size
        static string TFileE = "6"; // To Send File Extension
        static string TFileN = "7";// To Send File Name
        static string OutMSG = "Unknown";

        struct TalkerInfo//تكوين سجل يحتوي على الايدي والاسم اوبجكت
        {
            public string NickName;
            public string RoomID;
            public SocketCoderClient TalkerSocket;//اوبجكت من الكلاس الداخلي
        }

        // (1) Establish The Server
        public string Start_Server_On(int Port)
        {
            try
            {
                IPAddress[] AddressAr = null;
                String ServerHostName = "";

                try
                {
                    ServerHostName = Dns.GetHostName();
                    IPHostEntry ipEntry = Dns.GetHostByName(ServerHostName);
                    AddressAr = ipEntry.AddressList;
                }
                catch (Exception) { }

                if (AddressAr == null || AddressAr.Length < 1)
                {
                    return "Unable to get local address ... Error";
                }

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

                Listener_Socket.Bind(new IPEndPoint(AddressAr[0], Port));
                Listener_Socket.Listen(-1);

                Listener_Socket.BeginAccept(new AsyncCallback(EndAccept), Listener_Socket);

                return ("Listening On " + AddressAr[0].ToString() + ":" + Port + "... OK");

            }
            catch (Exception ex) { return ex.Message; }
        }
        // (2) Accept Clients Conncetion
        private static void EndAccept(IAsyncResult ar)
        {
            try
            {
                Listener_Socket = (Socket)ar.AsyncState;
                WaitingRoomID(Listener_Socket.EndAccept(ar));
                Listener_Socket.BeginAccept(new AsyncCallback(EndAccept), Listener_Socket);
            }
            catch (Exception) { }
        }

        // (3) Create a Socket for Each Client and add his Socket to The ArrayList
        private static void WaitingRoomID(Socket sock)
        {
            NSock = sock;
            NSock.BeginReceive(RBuffer, 0, RBuffer.Length, SocketFlags.None, new AsyncCallback(OnRoomDataRecieved), NSock);
        }
        private static void OnRoomDataRecieved(IAsyncResult ar)
        {
            NSock.EndReceive(ar);

            string RMSG = UnicodeEncoding.Unicode.GetString(RBuffer);

            TalkerInfo Talker = new TalkerInfo();

            string RoomID = GetOnlyRoomID(RMSG);
            string NickName = GetOnlyNickName(RMSG);

            if (RoomID != null)
            {

                    Talker.RoomID = RoomID;
                    Talker.NickName = GetOnlyNickName(RMSG);
                    Talker.TalkerSocket = new SocketCoderClient(NSock);

                    AddClient(Talker);

                    string WelcomeMSG = TMsg + SpecialChar + "Welcome to Our Room Joined at " + DateTime.Now.ToShortTimeString() + " In Room " + RoomID + FFlag;
                    while (WelcomeMSG.Length < 140)
                    {
                        WelcomeMSG += "-";
                    }

                    Byte[] byteDateLine = System.Text.Encoding.Unicode.GetBytes(WelcomeMSG);
                    Talker.TalkerSocket.ReadOnlySocket.Send(byteDateLine, byteDateLine.Length, 0);
            }
        }
        private static string GetOnlyRoomID(string FullMSG)
        {
            int index = FullMSG.IndexOf(RoomIDChar);
            int MSGLen = (FullMSG.IndexOf(FFlag) - (FFlag.Length)) - index;

            if (index >= 0)
            {
                if (MSGLen >= 0)
                {
                    return FullMSG.Substring(index + RoomIDChar.Length, MSGLen);

                }
                else return null;
            }
            else return null;
        }
        private static string GetOnlyNickName(string RoomIDFullMSG)
        {
            int index = RoomIDFullMSG.IndexOf(RoomIDChar);
            if (index > 0)
                return RoomIDFullMSG.Substring(0, index);
            else return null;
        }
        private static void AddClient(TalkerInfo Talker)
        {

            ClientsList.Add(Talker);

            // client.Sock.RemoteEndPoint - " has joined the room"
            Talker.TalkerSocket.SetupRecieveCallback(Talker);
        }

        // (4) Send The Recieved Data to All Clients in The Room
        private static void OnRecievedData(IAsyncResult ar)
        {
            TalkerInfo client = (TalkerInfo)ar.AsyncState;
            string ClientRoom = client.RoomID;
            try
            {
            byte[] aryRet = client.TalkerSocket.GetRecievedData(ar);

            if (aryRet.Length < 1)
            {
                // client.Sock.RemoteEndPoint - "has left the room"
                client.TalkerSocket.ReadOnlySocket.Close();
                ClientsList.Remove(client);
                SendCMDMessage(client.NickName, TDrop, client.RoomID);
                return;
            }

            string FullMsg = UnicodeEncoding.Unicode.GetString(aryRet);
            string MSGType = GetMSGType(FullMsg);

            if (MSGType == TJoin || MSGType == TDrop || MSGType == TidBack || MSGType == TMsg || MSGType == TCmd)
            {
                SendTheMessageToTheAllUsers(FullMsg, ClientRoom); 
            }
            else if (MSGType == TFileS || MSGType == TFileE || MSGType == TFileN)
            {
                SendTheMessageToTheAllUsers(FullMsg, ClientRoom); 
            }
            }
            catch (Exception ex) { LogWriter.WriteLog("Text OnRecievedData", ex.Message); }
            finally { client.TalkerSocket.SetupRecieveCallback(client); }
        }

        public string ShutDown()
        {
            try
            {
                if (Listener_Socket != null) Listener_Socket.Close();
                GC.Collect();
                GC.WaitForPendingFinalizers();
                return ("Shuted down ...");
            }
            catch (Exception ex) { return (ex.Message); }
        }

        #region Messaging ... Join/Drop/SendToAll ...
        private static string GetMSGType(string FullMSG)
        {
            int index = FullMSG.IndexOf(SpecialChar);
            if (index >= 0)
            {
                return FullMSG.Substring(0, index);

            }
            else return OutMSG;
        }
        private static string GetNickName(string FullMSG)
        {
            int index = FullMSG.IndexOf(SpecialChar);
            if (index >= 0)
            {
                return FullMSG.Substring(index + SpecialChar.Length);

            }
            else return "";
        }

        private static void SendTheMessageToTheAllUsers(string FullMessage, string RoomID)
        {
            try
            {
                byte[] aryRet = UnicodeEncoding.Unicode.GetBytes(FullMessage);


                var query = from TalkerInfo Talker in ClientsList
                            where Talker.RoomID == RoomID
                            select Talker;

                foreach (TalkerInfo Talker in query)
                {
                    try
                    {
                        Talker.TalkerSocket.ReadOnlySocket.BeginSend(aryRet, 0, aryRet.Length, SocketFlags.None, new AsyncCallback(Eend_Send), Talker);
                    }
                    catch
                    {
                        Talker.TalkerSocket.ReadOnlySocket.Close();
                        ClientsList.Remove(Talker);
                        SendCMDMessage(Talker.NickName, TDrop, Talker.RoomID);
                        return;
                    }
                }
            }
            catch (Exception) { }
        }
        private static void Eend_Send(IAsyncResult ar)
        {
            TalkerInfo client = (TalkerInfo)ar.AsyncState;
            try
            {
                client.TalkerSocket.ReadOnlySocket.EndSend(ar);
            }
            catch (Exception)
            {
                client.TalkerSocket.ReadOnlySocket.Close();
                ClientsList.Remove(client);
                return;
            }
        }
        private static void SendCMDMessage(string TXTMSG, string MessageType,string ToRoom)
        {
            string FullMessage = MessageType + SpecialChar + TXTMSG + FFlag;

            while (FullMessage.Length < 140)
            {
                FullMessage += "-";
            }

            SendTheMessageToTheAllUsers(FullMessage, ToRoom);
        }
        #endregion Messaging ... Join/Drop/SendToAll ...

        internal class SocketCoderClient
        {
            // To create a new socket for each client 

            private Socket New_Socket;
            private byte[] buffer = new byte[280];

            public SocketCoderClient(Socket PassedSock)
            {
                New_Socket = PassedSock;
            }

            public Socket ReadOnlySocket
            {
                get { return New_Socket; }
            }

            public void SetupRecieveCallback(object info)
            {
                try
                {
                    AsyncCallback recieveData = new AsyncCallback(SocketCoderTextServer.OnRecievedData);
                    New_Socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, recieveData, info);
                }
                catch (Exception)
                {
                }
            }
            public byte[] GetRecievedData(IAsyncResult ar)
            {
                int nBytesRec = 0;
                try
                {
                    nBytesRec = New_Socket.EndReceive(ar);
                }
                catch { }
                byte[] byReturn = new byte[nBytesRec];
                Array.Copy(buffer, byReturn, nBytesRec);
                return byReturn;
            }
        }

    }
}

تم تعديل هذه المشاركة بواسطة شوق المسافر في 14 أغسطس 2010 في 20:50

#4

السلام عليكم..

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

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

1-

Listener_Socket.Bind(new IPEndPoint(AddressAr[0], Port));

لماذا استعملناها مع انها تستخدم للاستقبال فقط

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

2-

Listener_Socket.BeginAccept(new AsyncCallback(EndAccept), Listener_Socket);

كيف سيكون تنفيذ هذه الدالة؟؟

فكرتي لها هي:يبدا باستقبال الطلب الاول وبعدها عبر callback ينتقل الى تنفيذ انهاء طلب الاتصال وخلال هذه الفترة تستقبل طلب اخر من الكلاينت وتستمر بهذا الشكل لكي لاينتظر الكلاينت الثاني حتى ينهي تنفيذ طلب الكلاينت الاول؟

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

3-

Listener_Socket = (Socket)ar.AsyncState;

مامعنى هذه الجملة هل ترجع حالة callback وتضعها في Listener_Socket ؟

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

4-

Listener_Socket.BeginAccept(new AsyncCallback(EndAccept), Listener_Socket);

لماذا وضعنا هذه الدالة في private static void EndAccept(IAsyncResult ar)

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

5-

GC.Collect();

GC.WaitForPendingFinalizers();

لماذا نستعمل هذه الدوال؟

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

6-

AsyncCallback recieveData = new AsyncCallback(SocketCoderTextServer.OnRecievedData);

New_Socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, recieveData, this);

مافائدة هذه الدوال وهل سوف تستدعي OnRecievedData ولماذا استعملنا BeginReceive

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

7-

public byte[] GetRecievedData(IAsyncResult ar)

ماذا تعني IAsyncResult ar وهل هي سترنك او اوبجكت او ماذا؟

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

هذه اسئلتي اعرف طويلة بس محتاج افهمها وياريت تشرحلنا تسلسل تنفيذ الدوال لانو انا اذا ما عرفت تسلسل تنفيذ الدوال برمجيا ماافهم البرنامج ابد

#5

-

Listener_Socket.Bind(new IPEndPoint(AddressAr[0], Port));

لماذا استعملناها مع انها تستخدم للاستقبال فقط

الدالة الأولى تستخدم لبدأ التصنت على بورت و أي بي محدد على السيرفر لاستقبال طلب الاتصال وليس لاستقبال البيانات

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

2-

Listener_Socket.BeginAccept(new AsyncCallback(EndAccept), Listener_Socket);

كيف سيكون تنفيذ هذه الدالة؟؟

فكرتي لها هي:يبدا باستقبال الطلب الاول وبعدها عبر callback ينتقل الى تنفيذ انهاء طلب الاتصال وخلال هذه الفترة تستقبل طلب اخر من الكلاينت وتستمر بهذا الشكل لكي لاينتظر الكلاينت الثاني حتى ينهي تنفيذ طلب الكلاينت الاول؟

هنا تبدأ عملية الموافقة على طلب الاتصال بالخادم وبالتالي أي طلب سيأتي له سيتم تحويله إلى دالة الـEnd Accept لإنهاء طلبه حيث نقوم بتخزين المعلومات الخاصة به في Arraylist خاص

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

3-

Listener_Socket = (Socket)ar.AsyncState;

مامعنى هذه الجملة هل ترجع حالة callback وتضعها في Listener_Socket ؟

4-

Listener_Socket.BeginAccept(new AsyncCallback(EndAccept), Listener_Socket);

لماذا وضعنا هذه الدالة في private static void EndAccept(IAsyncResult ar)

تمرر في دالة BeginAccept السوكيت الخاصة بالمتصل ويتم في دالة End Accept انهاء هذا الطلب وبالتالي تمرر كـState Object حيث نحتاج إلى تحويلها إلى Socket مرة أخرى من خلال عمل Casting لها كما في السطر السابق ثم في داخل هذه الدالة نستدعي دالة الـBegin مرة أخرى لاستمرار عملية الموافقة على الطلبات الأخرى القادمة إلى الخادم.

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

5-

GC.Collect();

GC.WaitForPendingFinalizers();

لماذا نستعمل هذه الدوال؟

تستخدم هذه لتأكد من افراغ الذاكرة من جميع من محتويات Data Collection معين وهنا يتم افراغ المحتويات الخاصة بالاتصال السابق حيث يتم إنهاء السوكيت بشكل الي في حالة كان هنالك اتصال باقي بحيث يتم انهاء كافة الاتصالات ثم إغلاق السوكيت.

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

6-

AsyncCallback recieveData = new AsyncCallback(SocketCoderTextServer.OnRecievedData);

New_Socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, recieveData, this);

مافائدة هذه الدوال وهل سوف تستدعي OnRecievedData ولماذا استعملنا BeginReceive

نستخدم دالة SetupRecieveCallback هنا لتأسيس عملية استقبال البيانات من كل متصل يتم إضافته والموافقة عليه. وأما BeginReceive فتستخدم لبدأ عملية الاستقبال وإنهائها في دالة OnRecievedData حيث يتم استدعاها مرة أخرى هناك لاستمرار عملية الاستقبال من ذلك المتصل.

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

7-

public byte[] GetRecievedData(IAsyncResult ar)

ماذا تعني IAsyncResult ar وهل هي سترنك او اوبجكت او ماذا؟

نستخدم هذه الدالة فقط لتحصيل البيانات التي يتم ارسالها من قبل المتصل وارجاعها كـBytes, تعني البيانات التي يتم ارسالها حيث تكون في جميع حالات الاتصال غير المتزامن من نوع IasyncResult بحيث نحتاج لحقا إلى تحويلها إلى صورتها التي كانت عليها.

لمزيد من المعلومات حول طريقة تحويل البيانات وإنهائها في الاتصال غير المتزامن أنصحك بالاطلاع على الفصل الرابع في الرابط التالي

http://www.socketcoder.com/ArticleFile.aspx?index=2&ArticleID=67

وأما نوع البيانات التي يتم تمريرها هنا فهي Custom Socket Class قمنا بإنشائه باسم SocketCoderClient ويحوي دوال نحتاجها لكل متصل مثل دالة SetupRecieveCallback لتأسيس عملية الاستقبال ودالة GetRecievedData لجلب البيانات التي يتم استقبالها...

نرجو أن تكون الصورة أكثر وضوحا الآن وأرجو ملاحظة أن هذه الطريقة التي نستخدمها في ذلك الكلاس ليست طريقة رسمية وانما هي فقط طريقة من الطرق المتبعة والمفضلة لعدم تكرار الدوال ولتنظيم الكود بقدر الإمكان ليس إلا وأما الطريقة الرسمية فهي التي تم شرحها في الكتاب ...

مع تمنياتي لك بالتوفيق

#6

تحياتي لك اخ فادي وشكرا لك ويارب تقوم بالسلامة

مواضيع مشابهة

عدد الزوار حالياً

المتواجدون خلال آخر دقيقتين · يتحدّث كل ٣٠ ثانية

—الإجمالي—أعضاء مسجّلون—زوار بدون تسجيل

جارٍ التحقق من المتواجدين…