Makale Özeti

Bu makalemde sizlerle Tcp protokolünün nasıl kullanıldığını bu protokolü kullanan bir mesajlaşma programı yazarak inceleyeceğiz. Programda clientlar sadece programın açılmasında sunucya giderek kullanıcı bilgilerinin saklanmakta olan XML dosyasını kendilerine kopyalayacak, dosyaya ulaşamadığı anda ise lokaldeki kopyasından disconnected olarak çalışabilecektir. Bunun yanı sıra clientlar kendi aralarında mesajlaşmada, online ve offline bilgilerini göndermede direkt haberleşecek ve sunucya yük bindirmeyeceklerdir.

Makale

          Makale serimizin bu son kısmına programımızın esas formu olan frmMain isimli formu kodlayarak başlayalım


 
 
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Net.Sockets;
using System.Xml;
 
 
namespace Messenger
{
    public partial class frmMain : Form
    {
        public frmMain()
        {
            InitializeComponent();
           
 
        }
        public static List<Form> frms = new List<Form>();
          Biraz önce bahsedilen ve açık mesajlaşma Formlarının tutulduğu değişken olan frms değişkeninin Generic List tipinden tanımlıyoruz.
 
 
        delegate void ShowFormDelegate(Form frm, string text, object tag);
        public void ShowForm(Form frm,string text,object tag)
        {
            if (this.InvokeRequired)
            {
 
                ShowFormDelegate d = new ShowFormDelegate(ShowForm);
                this.Invoke(d, new object[] { frm,text,tag });
            }
            else
            {
                frm.Text = text;
                frm.Tag = tag;
                frms.Add(frm);
                frm.Show();
            }
        }
          Yeni bir mesaj geldiği zaman o kullanıcıya ait form açık değilse yeni bir dorm açılması gerekmektedir. Bu formu açmak için gerekli kod ShowForm metodunun içindedir. Delegation işlemi ise diğer threadlerden bu metodun çağrılabilmesi için gereklidir.
 
 
        delegate void SetMessageDelegate(Message msg);
        public void SetMessage(Message msg)
        {
            if (this.InvokeRequired)
            {
 
                SetMessageDelegate d = new SetMessageDelegate(SetMessage);
                this.Invoke(d, new object[] { msg });
            }
            else
            {
                Form acikForm = null;
                foreach (Form f in frmMain.frms)
                {
                    if (f.Tag.ToString() == msg.Sender.Address)
                    {
                        acikForm = f;
                    }
                }
                if (acikForm != null)
                {
                    ((frmMessaging)acikForm).RecieveMessage(msg);
                }
                else
                {
                    frmMessaging frm = new frmMessaging();
                    this.ShowForm(frm, msg.Sender.Name, msg.Sender.Address);
                    frm.RecieveMessage(msg);
                }
            }
        }
          SetMessage formunda ise mesaj alındığı zaman çağrılacaktır. Bu metod açık dormların arasında dolaşacak ve açık formlardan mesaj gönderen kullanıcıya ait bir form varsa o formun RecieveMessage metodunu çağıracaktır, ancak açık form yoksa ilk önce ShowForm metodunu çağıracak sonrasında RecieveMessage metodunu çağıracaktır.
 
 
        delegate void SetUserOnlineDelegate(Client sender);
        public void SetUserOnline(Client sender)
        {
            if (this.InvokeRequired)
            {
 
                SetUserOnlineDelegate d = new SetUserOnlineDelegate(SetUserOnline);
                this.Invoke(d, new object[] { sender });
            }
            else
            {
               
                foreach (TreeNode tn in trvMain.Nodes)
                {
                    if (tn.Text == sender.Name)
                    {
                        tn.ImageIndex = 1;
                        tn.Tag = sender.Address;
                        if (trvMain.SelectedNode == tn)
                        {
                            trvMain.SelectedImageIndex = 1;
                        }
                    }
                }
                foreach (Form f in frmMain.frms)
                {
                    if (f.Text == sender.Name)
                    {
                        f.Tag = sender.Address;
                        ((frmMessaging)f).EnableControls();
                    }
                }
            }
        }
          SetUserOnlime metodu ise kullanıcı online olduğu zaman yani Type propertysi Login olan bir mesaj geldiğinde çağıralacaktır. Bu metod ise kullanıcıların listelendiği treeview kontrolünde ilgili kullanıcıyı bulur ve kullanıcının yanındaki icon'u yeşil yapar, daha sonrasında ise açık formların arasında kullanıcıya ait form olup olmadığını arar ve açık form varsa bu formun EnableControls metodunu çağırır.
 
 
        delegate void SetUserOfflineDelegate(Client sender);
        public void SetUserOffline(Client sender)
        {
            if (this.InvokeRequired)
            {
 
                SetUserOfflineDelegate d = new SetUserOfflineDelegate(SetUserOffline);
                this.Invoke(d, new object[] { sender });
            }
            else
            {
 
                foreach (TreeNode tn in trvMain.Nodes)
                {
                    if (tn.Text == sender.Name)
                    {
                        tn.ImageIndex = 0;
                        tn.Tag = null;
                        if (trvMain.SelectedNode == tn)
                        {
                            trvMain.SelectedImageIndex = 0;
                        }
                    }
                }
                foreach (Form f in frmMain.frms)
                {
                    if (f.Text ==sender.Name)
                    {
                        f.Tag = null;
                        ((frmMessaging)f).DisableControls();
                    }
                }
            }
        }
          SetUserOffline metounda ise SetUserOnline metodunda yapılan işlerin tam tersi yapılır, yani Kulanıcının yanındaki resim kırmızı olur ve kullanıcıya ait açık formun DisableControls() metodu çağırılır.
 
 
        private void Form1_Load(object sender, EventArgs e)
        {
            MessengerBase.mainForm = this;
           
            DataSet dsKullanicilar = new DataSet();
            try
            {
                System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
                doc.Load(System.Configuration.ConfigurationManager.AppSettings["kullaniciListesi"]);
                XmlNode nd;
                nd = doc.DocumentElement;
                nd.SelectSingleNode("//Kullanici[Adi='" + Environment.MachineName + "']")["Ip"].InnerText = System.Net.Dns.GetHostAddresses(Environment.MachineName)[0].ToString();
                doc.Save(System.Configuration.ConfigurationManager.AppSettings["kullaniciListesi"]);
                dsKullanicilar.ReadXml(System.Configuration.ConfigurationManager.AppSettings["kullaniciListesi"]);
                System.IO.File.Copy(System.Configuration.ConfigurationManager.AppSettings["kullaniciListesi"], "kullaniciListesi.xml", true);
                foreach (DataRow dr in dsKullanicilar.Tables[0].Rows)
                {
                    TreeNode tn = new TreeNode(dr["Adi"].ToString());
                    if (dr["Ip"].ToString() != System.Net.Dns.GetHostAddresses(Environment.MachineName)[0].ToString())
                    {
                        if (dr["Ip"].ToString() == "")
                        {
                            tn.ImageIndex = 0;
                        }
                        else
                        {
                           
                            Message msg = new Message();
                            msg.Reciever = new Client(dr["Adi"].ToString(), System.Net.Dns.GetHostAddresses(dr["Adi"].ToString())[0].ToString());
                            msg.Type = MessengerBase.MessageType.Login;
                            msg.Send();
                            tn.ImageIndex = 1;
                            tn.Tag = dr["Ip"].ToString();

 

                        }
                        trvMain.Nodes.Add(tn);
                    }
                }
            }
            catch (Exception )
            {
 
                dsKullanicilar.ReadXml("kullaniciListesi.xml");
                foreach (DataRow dr in dsKullanicilar.Tables[0].Rows)
                {
                    TreeNode tn = new TreeNode(dr["Adi"].ToString());
                    if (dr["Ip"].ToString() != System.Net.Dns.GetHostAddresses(Environment.MachineName)[0].ToString())
                    {
                        try
                        {
                            Message msg = new Message();
                            msg.Reciever = new Client(dr["Adi"].ToString(), System.Net.Dns.GetHostAddresses(dr["Adi"].ToString())[0].ToString());
                            msg.Type = MessengerBase.MessageType.Login;
                            msg.Send();
                            tn.ImageIndex = 1;
                            tn.Tag = dr["Ip"].ToString();
                        }
                        catch (Exception)
                        {
                            tn.ImageIndex = 0;
                        }
                        trvMain.Nodes.Add(tn);
                    }
                }
            }
           
          
            MessengerBase.StartListening();
        }
          Form açıldığı zaman ilk önce app.config dosyasında belirtilen yolsan xml dosyası okunur ve bu xml dosyasında kendisi ile ilgili kayda kullanıcı kendi ipsinin yazar. Sonrasında bu xml dosyası ilk önce lokal disk e kopyalanır ve bir dataset'e aktarılır ve kullanıcı listesi ise bu datasetin içinde sıra ile bir döngüde dolaşarak oluşturulur. Burada Xml'den gelen dosyada Ip'si boş olan kullanıcılar offline demektir. Eğer Ip varsa kullanıcıya online olunduğuna dair mesaj gönderilir.

         Ancak bu işlem sırasında herhangi bir hata oluştuğu zaman sistem offline çalışmaya müsade edecektir. Bundan dolayı hata oluşması durumunda lokal'de bulunan xml dosyası okunur ve dosyada bulunan her kullanıcıya mesaj atılır ve mesajın başarılı olup olmamasına göre kullanıcının durumu sistem tarafından belirlenir.
 
 
        private void CloseForm()
        {
            foreach (TreeNode tn in trvMain.Nodes)
            {
                if (tn.Tag != null)
                {
                    try
                    {
                        Message msg = new Message();
                        msg.Reciever = new Client(tn.Text, tn.Tag.ToString());
                        msg.Type = MessengerBase.MessageType.Logoff;
                        msg.Send();
                    }
                    catch (Exception)
                    {
                        tn.ImageIndex = 0;
                    }
                }
            }
                MessengerBase.tcpListener.Stop();
                if (MessengerBase.threadRecieve != null)
                {
                    if (MessengerBase.threadRecieve.IsAlive)
                    {
                        MessengerBase.threadRecieve.Abort();
 
                    }
                }
               
                Application.Exit();
        }
 
        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            CloseForm();
        }
       
        private void treeView1_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (e.Node.Tag != null)
            {
                foreach (Form f in frms)
                {
                    if (f.Text == e.Node.Text)
                    {
                        f.Focus();
                        return;
                    }
                }
                frmMessaging frm = new frmMessaging();
                ShowForm(frm, e.Node.Text, e.Node.Tag);
            }
        }
 
        private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
        {
            trvMain.SelectedImageIndex = e.Node.ImageIndex;
        }
    }
}
          CloseForm metodunda listede online olan tüm kullanıcılara tipi Logoff olan ve kullanıcının offline olduğunu bildiren mesaj gönderilir. ve thread durdurulur. Application.Exit() metodu ile de uygulamadan çıkılır. Bir diğer önemli nokta ise TreeView de bulunan bir Node'a çift tıklandığında o nodeda bilgisi bulunan kullanıcya ait bir mesajlaşma formunun açılmasıdır.

         Uygulamada kullanılan ve sunucuda bulunan Xml dosyasına tüm kullanıcıların yazma izninin olması önemlidir. Xml dosya yerine sql server kullanmak veya uygulamadaki tüm mesajların sunucu üzerinde çalışan bir windows servisinin bir clienttan alınan mesajın diğer client'a aktarılması görevini gerçekleştirmesi yaklaşımları ile de yazılabilir.

         Bu makalede hep birlikte bir Soket uygulamasının nasıl yazılabileceğini inceledik. TcpClient ve threading kullanarak yazdığımız bu uygulamada hata ayıklama ve exception yakalama uygulama makaleyi tamamlayıcı özellik taşıdığından uygulanmamıştır.

oztamer@hotmail.com
tamer.oz@yazgelistir.com
oztamer@hotmail.com

Ornek Uygulama