Makale Özeti

SendMailViaSMTP uygulamasının detaylı incelemesi. Kod açıklamaları

Makale

Kullandığımız namespace’ler

System

System.Net.Sockets

System.IO

 

Sockets ns ini MTA’ya tcpclient ile bağlanmak için, IO ns ini ise networkstream’e gelen bilgileri okumak için kullandık.

 

Sınıfımızın üyelerini incelememiz gerekirse;

 

Üye ismi

Açıklama

mvarSMTPServerAddress

MTA’nin ip adresi veya “fully qualified domain name”’I (string)

mvarSenderName

Mail client uygulamasında gözükecek Gönderici İsmi (string)

mvarSenderEmailAddress

MTA’ya bildirilecek gönderici email adresi.Bu değer eğer yanlış gönderilirse MTA’nın tipine göre değişen güvenlik ayarlarınca sunucu ile bağlantınız sonlandırılabilir. (string)

mvarRecipientName

Mail client uygulamasında gözükecek Alıcı ismi (string)

mvarRecipientEmailAddress

MTA’ya bildirilecek alıcı email adresi. (string)

mvarEmailSubject

Göndereceğimiz email’in konusu (string)

mvarEmailBody

Göndereceğimiz email’in mesaj kısmı (string)

mvarSMTPTimeOut

Saniye cinsinden zamanaşımı süresi (int varsayılan=60)

mvarSMTPRemotePort

MTA’ya bağlanılırken kullanılacak port numarası (int varsayılan=25)

 

 

 

Yapıcı metodumuzda iki üyenin varsayılan özellikleri atanmıştır.

public SMTPMailSender()

              {

                     mvarSMTPTimeOut = 60;

                     mvarSMTPRemotePort = 25;

              }

 

 

 

Sınıfımızın kalbi, veritipi bool olan SendEmail() metodunu inceleyelim.

 

1- MTA’ya bağlantı gerçekleştiriliyor
Metodun herhangi bir aşamasında herhangi bir hata oluştuğu taktirde metod false değerini döndürecek ve biz de bu sayede emailin ulaşıp ulaşmadığını anlayabileceğiz.

tclSMTP=new TcpClient();

try

{

       tclSMTP.Connect(mvarSMTPServerAddress,mvarSMTPRemotePort);

}

catch

{

       return false;

}

Eğer bağlantıda herhangi bir hata oluşursa metodumuz false döndürecektir.

 

2- Bağlantı oluştuktan sonra soketten veri alışverişi için gerekli olan networkstream sınıfımızı yaratıyoruz.Aynı şekilde bu sınıftan da string okuyup yazmak için streamwriter ve streamreader sınıflarımızı oluşturuyoruz.

nstSMTP=tclSMTP.GetStream();

stwSMTP=new StreamWriter(nstSMTP);

strSMTP=new StreamReader(nstSMTP);

 

3- Bütün araçlarımızı oluşturduğumuza göre artık veri alıp gönderebiliriz.Makalenin başında değinmiş olduğum SMTP protokol kurallarına uyarak MTA’ya gerekli mesajları gönderip MTA’nın cevaplarına göre hareket edeceğiz.

Bu satırlarda WaitForResponse metodunu gözardı edin.İlerleyen satırlarda bu metod’u açıklayacağım.

Bu medodun şimdilik sadece MTA’dan ilgili cevabı almak için olduğunu bilmeniz yeterli olacaktır.

//mta'dan karşılama mesajı bekleniyor

//waiting for greeting message from MTA

if (WaitForResponse("220"))

{

       //mta'ya karşılama mesajı gönderiliyor

       //sending greeting message to MTA

       stwSMTP.WriteLine("HELO " + mvarSMTPServerAddress);

       stwSMTP.Flush();

}

else

{

       tclSMTP.Close();

       return false;

}

Gördüğünüz gibi MTA’dan 220 cevabı başarılı bir şekilde alınırsa MTA’ya “HELO mail.somemta.com” mesajı gönderilmektedir.Eğer mesaj alışverişinde herhangi bir sorun yaşanırsa WaitForResponse metodu false değerini döndürecektir ve sonuç olarak tclSMTP tcpclient bağlantısı kapatılıp SendEmail metodu false olarak dönecektir.

 

Diğer bütün aşamalar mesaj alıp gönderme , yani yukarıdaki bloğun aynısıdır.

Sadece gönderilen mesajlar ve alınan cevaplar değişecektir.

Tabi SMTP protokolünün izin verdiği sırada J

 

 

 

 

 

Gelelim WaitForResponse metodumuza;

bool WaitForResponse(string strResponseCode)

{

       //zamanaşımı kontrolü için mevcut tarih saat bilgisi alınıyor

       //gathering current date time data for timeout check

       dteTimeOutCheck=DateTime.Now;

       //zamanaşımı değeri bulunuyor

       //calculating timeout value

       TimeSpan tsp=DateTime.Now-dteTimeOutCheck;

       //zamanaşımı değeri kullanıcının belirlediği değeri aşıncaya kadar döngü çalıştırılıyor

       //looping code until the timeout exceeds the user defined value

       while(tsp.Seconds<mvarSMTPTimeOut)

       {

              //MTA bize herhangi bir mesaj göndermiş mi kontrol ediliyor.

              //checking if MTA has sent a message to us

              if (nstSMTP.DataAvailable)

              {

                     //eğer göndermişşse mesaj okunuyor

                     //if so, get the message

                     string strIncomingData=strSMTP.ReadLine();

                     //gelen bilginin protokol numarası istenen koda uyuyormu kontrol ediliyor

                     //checking if the requested protocol number matches the server response

                     if (strIncomingData.Substring(0,strResponseCode.Length)==strResponseCode)

                           return true;

              }

              //zamanaşımı değeri yeniden hesaplanıyor

              //recalculating the timeout value

              tsp=DateTime.Now-dteTimeOutCheck;

       }

       return false;

}

Bu metodumuz gördüğünüz gibi string tipinde strResponseCode parametresini almaktadır.
Bu parametre MTA’dan beklediğimiz mesajın kodunu temsil etmektedir.

Hatırlarsanız bizim MTA’ya gönderdiğimiz veriler sonucunda MTA bize “250 ok” “354 go ahead” gibi mesajlar geri göndermekteydi.İşte biz bu mesajların ilk 3 harfini bu metoda göndererek MTA’nın bize istediğimiz gibi cevap verip vermediğini anlayacağız.Eğer MTA istediğimiz cevabı verirse metod true vermezse false değerini döndürecektir.

 

Burda kontrol etmemiz gereken bir durum mevcut.Zamanaşımı.

Protokolün herhangi bir aşamasında sunucu kasıtlı veya kasıtsız bir şekilde bağlantıya son verebilir.

Bu tür bir durum gerçekleştiğinde eğer zamanaşımı mekanizmamız olmazsa sürekli sunucunun cevabını bekleriz, programımız sağlıklı çalışmaz.

 

Yapmamız gereken mevcut tarih saat bilgisini bir değişkene atayıp döngü içersinde zamanaşımı değerini aşıp aşmadığımızı kontrol etmektir.

//zamanaşımı kontrolü için mevcut tarih saat bilgisi alınıyor

//gathering current date time data for timeout check

dteTimeOutCheck=DateTime.Now;

//zamanaşımı değeri bulunuyor

//calculating timeout value

TimeSpan tsp=DateTime.Now-dteTimeOutCheck;

//zamanaşımı değeri kullanıcının belirlediği değeri aşıncaya kadar döngü çalıştırılıyor

//looping code until the timeout exceeds the user defined value

while(tsp.Seconds<mvarSMTPTimeOut)

{

…..

…..
//zamanaşımı değeri yeniden hesaplanıyor

//recalculating the timeout value

tsp=DateTime.Now-dteTimeOutCheck;

}

 

dteTimeOutCheck değişkenine sistemin o anki tarih saat değerini atıyoruz.

TimeSpan nesnesi sayesinde dteTimeOutCheck değeri ile mevcut tarih saat değeri arasındaki farkı buluyoruz.While döngüsü koşul bölümünde ise farkın sınıf üyesi olan mvarSMTPTimeOut değerini aşıp aşmadığını kontrol ediyoruz.Böylece bir timeout mekanizması kurmuş oluyoruz.

 

Şimdi isterseniz while döngüsünün içindeki kodu inceleyelim.

//MTA bize herhangi bir mesaj göndermiş mi kontrol ediliyor.

//checking if MTA has sent a message to us

if (nstSMTP.DataAvailable)

{

       //eğer göndermişşse mesaj okunuyor

       //if so, get the message

       string strIncomingData=strSMTP.ReadLine();

       //gelen bilginin protokol numarası istenen koda uyuyormu kontrol ediliyor

       //checking if the requested protocol number matches the server response

       if (strIncomingData.Substring(0,strResponseCode.Length)==strResponseCode)

              return true;

}

//zamanaşımı değeri yeniden hesaplanıyor

//recalculating the timeout value

tsp=DateTime.Now-dteTimeOutCheck;

 

Burdaki ilk satırımız " if (nstSMTP.DataAvailable)”  sayesinde networkstream belleğinde herhangi bir verinin mevcut olup olmadığını kontrol ediyoruz.

 

İlk bakışta bu kontrol gereksiz olarak gözükebilir.Hatta işi “strSMTP.ReadLine()” komutu ile çözebileceğimizi düşünebilirsiniz.Hatta pratikte bunu uyguladığınızda herhangi bir hata vermeyecek gayet sağlıklı çalışacaktır.Fakat diğer taraftan protokol akışında bir problem olduğunu düşünürsek bu kontrolün ne kadar önemli olduğu görülecektir.

 

Varsayalımki biz MTA’dan 250 cevabını beklerken sunucu bize “502 unimplemented” yani geçersiz bir komut gönderdiniz mesajını gönderdi.Readline ile 502’yi okuyacağız fakat beklediğimiz mesaj 502 olmadığından dolayı döngü sonlanmayacak devam edecektir ve tekrar readline() komutu çalışacaktır.

Eğer networkstream belleğinde herhangi bir mesaj mevcut değil ise Readline metodu imleci birsonraki mesaj gelene kadar tutacaktır.Bu da bizim zamanaşımı mekanizmamızın devredışı kalmasına sebep olacaktır ki bu hiç istemediğimiz  bir durumdur.

 

Sıra geldi son aşamaya , kodun testi.

Aşağıdaki kodu örnek alıp kendi kullanacağınız MTA adresi, alıcı gönderici adresi değerlerini değiştirmeyi unutmayın.

using System;

namespace SendMailviaSMTP

{

       class Class1

       {

              [STAThread]

              static void Main(string[] args)

              {

                     SMTPMailSender sm=new SMTPMailSender();

                     sm.SMTPTimeOut=10;

                     sm.SenderEmailAddress="lyildiz@somemta.com";

                     sm.RecipientEmailAddress="leventyildiz@gmail.com";

                     sm.SMTPServerAddress="mail.somemta.com";

                     sm.SenderName="Levent YILDIZ ACME Inc";

                     sm.RecipientName="Levent YILDIZ MINDSTORM Inc";

                     sm.EmailSubject="Test subject";

                     sm.EmailBody="Test Body";

                     if (sm.SendEmail())

                     {

                           Console.WriteLine("Email başarıyla gönderildi.");

                     }

                     else

                     {

                           Console.WriteLine("Email gönderiminde hata oluştu.");

                     }

              }

       }

}

 

 

Bu makalemde c# ile SMTP sunucuları kullanarak nasıl mail göndereceğimizi, hangi sınıflara ihtiyacımız olduğunu anlattım.Umarım yeterince açıklayıcı olabilmişimdir.Bir sonraki makalemde görüşmek üzere.

 

Levent YILDIZ

Gulf Agency Company – TURKEY

IT Manager

MCP

msmoracle@hotmail.com

levent.yildiz@gacworld.com

http://www.gacworld.com