|
C# ile SMTP Sunucusunu Kullanarak Mail Göndermek |
|
Gönderiliyor lütfen bekleyin... |
|
|
Bu
makalemde size SMTP protokolünün işleyişinden ve c# ile SMTP mail
sunucularını kullanarak nasıl mail gönderebileceğimizden
bahsedeceğim.
İlk önce SMTP nedir? MTA MTU nedir? protokol nedir? biraz bu
kavramlardan bahsedelim.
Protokol
– Network üzerinden çalışan yazılımların birbirleri arasında
haberleşmeleri için kullandıkları kurallar topluluğuna denir.
SMTP -
Simple Mail Transfer Protocol yani Basit Mesaj Transfer Protokolüdür. İlgili
RFC kaydı Ağustos 1982de yazılmıştır. Outlook uygulamasında bir
mail yazdığımız zaman outlook bu maili SMTP protokolü
kurallarına uyarak mail sunucusuna iletir.
MTA
Mail Transfer Agent - Bugün hepimizin kullanmakta olduğu mail
sunucularıdır. Örneğin Microsoft Exchange Server.
MTU
Mail User Agent - Mail yazmak ve okumak için kullandığımız yazılımlardır.
En basit örneği outlook express veya Outlook uygulamaları olacaktır.
Aşağıda
MTA ile MUA iletişimi ve SMTP protokolü akış diyagramlarını
görmektesiniz.
Peki
SMTP protokolünün kuralları nelerdir?
İlk
önce yapmak istediğimizi c# kullanmadan yapmamız ve bu protokolün
nasıl işlediğini görmemiz gerekir. Çünkü yapmaya çalıştığımız
şey tamamen görsellikten uzak, network haberleşmesidir. MUA
kullanmadan ilk mailimizi msdos komut satırını kullanarak göndereceğiz.
Diyelimki [email protected]
adresine test konulu bir mesaj gondermek istiyoruz.
Yapmamız gereken command prompt’ u çalıştırıp, telnet ile
MTA’ya bağlanmaktır.
C:\>telnet mail.somemta.com
25 |
Bu
komutu yazdığımızda MTA bize aşağıdaki gibi bir mesaj gönderecektir.
220
mail.somemta.com ESMTP |
Karşı
komut olarak aşağıdaki komutu yazın ve enter tuşuna basın.
MTA
bizden komut beklediğini belirten
mesajını
gösterecektir. Bu
aşamadan sonra yapmamız gereken mailin kim tarafından, kime ve ne içerikle
gönderileceğinin bilgisini vermek olacaktır.
Mailin kim tarafından gönderildiğini belirtmek için aşağıdaki
komutu yazıp enter tuşuna basın.
sunucu
bize komutun alındığına dair aşağıdaki mesajı verecektir.
Şimdi
mesajı kime göndereceğimizin bilgisini içeren aşağıdaki komutu
yazıp enter tuşuna basın.
yine
sunucu bize komutun alındığına dair aşağıdaki mesajı
verecektir.
Buraya
kadar sanırım problem yok. Şimdi
mesajımızın içeriğini yazabiliriz. Bu bölümde yazacağımız
bilgiler MUA’ da görünecek bilgilerdir. Mesajın kimden geldiği
kime gittiği mesaj konusu ve içeriği.
Bu detayları girmemiz için aşağıdaki komutu yazıp enter tuşuna
basın.
sunucu bize komutun alındığına dair aşağıdaki mesajı verecektir.
Bu bölüme kadar yazdığımız
komutlar sonucunda enter tuşuna bastığımızda sunucu bizden komutu
aldığına dair 250 ok mesajları göndermekteydi. Biz data komutunu
yazdıktan sonra bu değişecektir. Artık enter tuşuna bastığımızda
sunucudan herhangi bir onay almayacağız. Bu moddan çıkmak için
“<Enter> . <Enter>” (enter tuşuna basılacak ,
nokta tuşuna basılacak, enter tuşuna basılacak) tuşlamamız
gerekecektir. Şimdi
mesajın içeriğini yazalım.
From:Levent
YILDIZ ACME Inc.
To:Levent YILDIZ MINDSTORM Inc.
Subject:ilk command prompt emailimiz
Bu
telnet kullanarak gönderdiğimiz ilk mail
.
|
data kısmını
yazdıktan sonra sunucu bize maili aldığına dair aşağıdaki gibi
bir mesaj gönderecektir.
250
ok 1118680536 qp 21248 |
Bu
mesaj sunucunun tipine göre değişebilir. Önemli olan 250 ok kısmıdır. Evet
artık mailimizi kontrol edebiliriz. Eğer
bu aşamada herhangi bir sorun ile karşılaşırsanız bu bağlandığınız
MTA’ nın güvenlik ayarları ile ilgili olabilir. Çoğu sistemler
bugün “POP-before-SMTP” yöntemini kullanarak SMTP güvenliğini
sağlamaktadır. Yani mail göndermeden önce sunucuya POP protokolü
ile bağlanıp username ve password göndermeniz ve sunucunun bunu doğrulaması
gerekir. Bu doğrulama sonucu sunucu size 1 saatliğine SMTP bağlantısı
izni verir. Gelelim
asıl konuya. Bunu c# programlama dilini kullanarak nasıl gerçekleştireceğiz? Gerekli
bütün kodlar aşağıdadır. Koda bir göz atın…
using System;
using System.Net.Sockets;
using System.IO;
namespace SendMailviaSMTP
{
public class SMTPMailSender
{
string mvarSMTPServerAddress;
string mvarSenderName;
string mvarSenderEmailAddress;
string mvarRecipientName;
string mvarRecipientEmailAddress;
string mvarEmailSubject;
string mvarEmailBody;
int mvarSMTPTimeOut;
int mvarSMTPRemotePort;
TcpClient tclSMTP;
NetworkStream nstSMTP;
StreamReader strSMTP;
StreamWriter stwSMTP;
DateTime dteTimeOutCheck;
public SMTPMailSender()
{
mvarSMTPTimeOut = 60;
mvarSMTPRemotePort = 25;
}
public bool SendEmail()
{
//SMTP
sunucusu ile bağlantı kuruluyor
//Connecting
to SMTP Server
tclSMTP=new
TcpClient();
try
{
tclSMTP.Connect(mvarSMTPServerAddress,mvarSMTPRemotePort);
}
catch
{
return false;
}
nstSMTP=tclSMTP.GetStream();
stwSMTP=new
StreamWriter(nstSMTP);
strSMTP=new
StreamReader(nstSMTP);
//mtadan
karşılama mesajı bekleniyor
//waiting for
greeting message from MTA
if
(WaitForResponse("220"))
{
//mtaya karşılama mesajı gönderiliyor
//sending greeting message to MTA
stwSMTP.WriteLine("HELO " + mvarSMTPServerAddress);
stwSMTP.Flush();
}
else
{
tclSMTP.Close();
return false;
}
if
(WaitForResponse("250"))
{
//gönderici email adresi gönderiliyor
//sending sender email address to MTA
stwSMTP.WriteLine("mail from:" + mvarSenderEmailAddress);
stwSMTP.Flush();
}
else
{
tclSMTP.Close();
return false;
}
if
(WaitForResponse("250"))
{
//alıcı email adresi gönderiliyor
//sending recipient email address to MTA
stwSMTP.WriteLine("rcpt to:" + mvarRecipientEmailAddress);
stwSMTP.Flush();
}
else
{
tclSMTP.Close();
return false;
}
if
(WaitForResponse("250"))
{
//maili yazmak için data moduna geçiliyor
//switching to data mode for entering body of the mail
stwSMTP.WriteLine("data");
stwSMTP.Flush();
}
else
{
tclSMTP.Close();
return false;
}
if
(WaitForResponse("354"))
{
//mesajin body kısmı hazırlanıyor
//preparing the body section of the email
string strSMTPData="";
strSMTPData="From:" + mvarSenderName + Environment.NewLine;
strSMTPData+="To:" + mvarRecipientName + Environment.NewLine;
strSMTPData+="Subject:" + mvarEmailSubject + Environment.NewLine +
Environment.NewLine;
strSMTPData+=mvarEmailBody + Environment.NewLine + "." +
Environment.NewLine;
//mesaj gönderiliyor
//sending message
stwSMTP.Write(strSMTPData);
stwSMTP.Flush();
}
else
{
tclSMTP.Close();
return false;
}
if
(WaitForResponse("250"))
{
//gönderim başarılı ise değer true döndürülüyor
//returns true if the send process succeeds
tclSMTP.Close();
return true;
}
else
{
tclSMTP.Close();
return false;
}
//gönderim
başarısız ise değer false döndürülüyor
//returns
false if the send process fails
tclSMTP.Close();
return false;
}
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;
}
public string SMTPServerAddress
{
get{return
mvarSMTPServerAddress;}
set{mvarSMTPServerAddress=value;}
}
public string SenderName
{
get{return
SenderName;}
set{mvarSenderName=value;}
}
public string SenderEmailAddress
{
get{return
SenderEmailAddress;}
set{mvarSenderEmailAddress=value;}
}
public string RecipientName
{
get{return
RecipientName;}
set{mvarRecipientName=value;}
}
public string RecipientEmailAddress
{
get{return
RecipientEmailAddress;}
set{mvarRecipientEmailAddress=value;}
}
public string EmailSubject
{
get{return
EmailSubject;}
set{mvarEmailSubject=value;}
}
public string EmailBody
{
get{return
EmailBody;}
set{mvarEmailBody=value;}
}
public int SMTPTimeOut
{
get{return
SMTPTimeOut;}
set{mvarSMTPTimeOut=value;}
}
public int SMTPRemotePort
{
get{return
SMTPRemotePort;}
set{mvarSMTPRemotePort=value;}
}
}
} |
Şimdi uygulamamızda neler yaptığımızı
inceleyelim.
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) |
SendEmail |
Atanan
özellikleri kullanarak maili gönderir (bool) |
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.
//mtadan karşılama mesajı bekleniyor
//waiting for greeting message from MTA
if (WaitForResponse("220"))
{
//mtaya 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.
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="[email protected]";
sm.RecipientEmailAddress="[email protected]";
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
makalede görüşmek üzere.Levent
YILDIZZ
Gulf
Agency Company – TURKEY
IT
Manager
MCP
[email protected]
[email protected]
http://www.gacworld.com
Makale:
C# ile SMTP Sunucusunu Kullanarak Mail Göndermek C#, Visual C# ve .NET Levent Yıldız
|
|
|
-
-
Eklenen Son 10
-
Bu Konuda Geçmiş 10
Bu Konuda Yazılmış Yazılmış 10 Makale Yükleniyor
Son Eklenen 10 Makale Yükleniyor
Bu Konuda Yazılmış Geçmiş Makaleler Yükleniyor
|
|