개발/C#, ASP.NET

C#으로 Send Mail 구현하기(SMTP)

판팡선 2009. 4. 23. 10:46
출처 julymorning4님의 블로그 | 오라클러
원문 http://blog.naver.com/julymorning4/100019742677
[강좌#9]C#으로 Send Mail 구현하기(SMTP)

이번 강좌에서는 C#으로 메일 보내는것을 만들어 보도록 하겠습니다.

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

Sendmail(SMTP, Simple Mail Transfer Protocol) 구현 하기

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

 

본 예제에서는 Socket 을 이용하여 메일을 보내는 프로그램을 작성 하였습니다 . 소켓 통신 프로그램을 개발 하신 분들은 소스 이해에 크게 어려움이 없으리라 생각 됩니다 .

관련 클래스

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

System.net.Sockets. TcpClient : System.Net.Sockets 에는 Socket 과 같은 저 수준 소켓 통신을 위한 클래스도 들어 있지만 TCP 통신에 대하여 간편한 인터페이스를 제공 하는 TcpClient 및 TcpListener 와 같은 고 수준 클래스도 포함되어 있는데 그 중 하나가 TcpClient 클래스 입니다 . TcpClient 와 TcpListener 는 스트림 모델에 따라 데이터를 송 . 수신 하지만 Socket 클래스는 바이트 수준의 접근법을 쓰고 있습니다 . 이 TcpClient 와 TcpListener 는 NetworkStream Class 를 사용한 스트림 기반 통신을 수행 하지만 필요에 따라서는 바이트를 다룰 수도 있습니다 .

---------

생성자

---------

public TcpClient(); // 기본 생성자

public TcpClient(IPEndPoint ipEnd); // 여기에서 IPEndPoint 는 원격 종점이 아니라 로컬의 종점임을 기억하라 ...

public TcpClient(string hostname, int port)

IPEndPoint 를 이용한 생성자를 이용하여 작성시 아래처럼 TcpClient 객체를 생성 후 원격 종점을 넘겨 주어야 합니다 .

// 로컬 종점을 생성한다 .

IPAddress ipAddr = IPAddress.Parse(”192.168.0.2”);

IPEndPoint ipEnd = new IPEndPoint(ipAddr, 11100);

TcpClient newClient = new TcpClient(endpoint); // 로컬의 IPEndPoint 임을 명심

// 서버에 연결을 하기 위해서는 반드시 connect 를 호출

newClient.Connect(“192.168.0.5”, 11100);

TcpClient 생성자에게 전달되는 매개변수는 로컬의 종점인 반면 Connect() 메소드는 실제로 클라이언트가 서버에 연결을 맺기 떄문에 원격의 종점을 매개변수로 넘겨 줍니다 .

마지막 세번째 생성자는 인자로 넘겨준 DNS 명과 포트번호를 사용하여 원격 연결을 맺는 것입니다 .

TcpClient newCLient = new TcClient(“localhost”, 8080);

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

호스트에 대한 연결 맺기

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

일단 TcpClient 의 인스턴스를 생성하고 나면 원격 호스트에 대한 연결을 맺어야 합니다 . 클라이언트는 Connect() 메소드를 사용하여 TCP 호스트에 연결을 설정 합니다 .

Connect() 메소드의 형식은 다음과 같이 세가지가 있습니다 .

public void Connect(IPEndPoint endpoint);

public void Connect(IPAdress ipAddr, int port);

public void Connect(string hostname, int port);

•  연결하고자 하는 원격 종점을 가리키는 IPEndPoint 객체를 넘긴다 .

TcpClient newClient = new TcpClient();

IPAddress ipAddr = IPAddress.Parse(“127.0.0.1”);

IPEndPoint ipEnd = new IPEnPoint(ipAddr, 80);

newClient.Connect(endPoint);

•  IPAddress 개체와 포트 번호를 넘긴다 .

TcpClient newClient = new TcpClient();

IPAddress ipAddr = IPAddress.Parse(“127.0.0.1”);

newClient.Connect(ipAddr, 80);

•  호스트명과 포트 번호를 넘긴다 .

TcpClient newClient = new TcpClient();

newClient.Connect(“127.0.0.1”, 80);

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

메시지 주고 받기

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

NetworkStream 클래스를 사용 하여 연결된 두 애플리케이션의 통신 채널은 스트림 수준의 처리 과정을 거칩니다 . 데이터를 주고 받기 전에 하위 스트림을 획득 해야 하는데 이를 위해 TcpClient 는 GetStream() 메소드를 제공 합니다 . GetStream() 은 하위 소켓을 사용하여 NetworkStream 클래스의 인스턴스를 생성 한 다음 이를 호출 자에게 돌려 줍니다 . 아래의 예를 보도록 참조 하세요 .

NetworkStream tcpStream = newClient.GetStream();

스트림을 획득하고 나면 Read() 및 Write() 메소드를 사용하여 호스트 애플리케이션으로부터 데이터를 읽고 쓸 수 있습니다 . Write() 메소드는 호스트로 송신 할 데이터를 가지고 있는 바이트 배열 , 쓰기를 시작 하는 스트림의 위치 , 그리고 데이터의 길이 등을 매개 변수로 가집니다 .

byte[] senBytes = Ecoding.Default.GetBytes(“ 연습입니다 .”);

tcpStream.Write(sendBytes, 0, sendBytes.Length);

Read() 메소드는 수신할 데이터를 저장 할 바이트 배열 , 읽기 시작 한 위치 , 그리고 읽어온 바이트 수 등을 인자로 넘깁니다 .

bytes[] readBytes = new byte[newClient.ReceiveBufferSize];

tcpStream.Read(readBytes, 0, newClient.ReceiveBufferSize):

// 바이트를 문자열로 변환 합니다 .

string returnData = Encoding.Default.GetString(readBytes);

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

TCP 소켓 닫기

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

TcpClient.Close();

 

 

 

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

SMTP 동작 메커니즘

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

 

http://oraclejava.co.kr/images/lecture/SMTP1.jpg

 

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

SMTP 명령어

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

•  HELO. 이 명령어는 호스트간의 통신을 초기화 합니다 . 송신 호스트를 식별하기 위한 파라미터를 넘겨 주며 수신 호스트는 식별 했음을 알려 줍니다 . 이 명령이 수행되었다는 것은 통신을 시작 할 준비가 되었다는 것 입니다 . 정상적인 수행인 경우 250 번 코드를 넘겨 줍니다 .

송신 시스템 : HELO mail.oraclejava.co.kr

수신 시스템 : 250 OK

•  MAIL. 이 명령어는 메일 보내는 사람이 누구인지 알려주며 메일 메시지를 전달하는 과정에서 오류가 발생 하면 원래 메일을 보낸 사람에게 메시지를 돌려 줍니다 .

송신 시스템 : MAIL FROM:

수신 시스템 : 250 OK

•  RCPT TO. 이 명령어는 메일을 받을 사람을 기술 합니다 .

송신 시스템 : MAIL RCPT TO:

수신 시스템 : 250 OK

•  DATA. 이 명령어는 메시지 파일의 본문에 포함될 내용을 알려 줍니다 . 송신 시스템이 .(Dot) 을 보내면 끝임을 알리는 것입니다 . 아래의 예는 SUBJECT: 를 통해 메일의 제목을 설정 합니다 . SUBJECT 와 캐리지 리턴 / 줄바꿈 문자 사이에 메일의 제목이 들어 갑니다 .

송신 시스템 : DATA

수신 시스템 : 354 Ready to Receive data …

송신 시스템 : SUBJECT: 메일의 제목 입니다 . 메일의 내용 입니다 ..

수신 시스템 : 250 OK

•  QUIT. 연결을 종료 할것을 알림

송신 시스템 : QUIT

수신 시스템 : 221 mail.Oraclejava.co.kr Closing Connection…

SMTP 클라이언트 프로그램의 UI 는 다음과 같습니다 .

 

http://oraclejava.co.kr/images/lecture/SMTP2.jpg

 

 

소스 코딩을 하기 전에 개략적으로 소스 내용을 정리 해 보도록 하겠습니다 ,

•  사용자가 톰에서 Send 를 Click 하면 SMTP 서버에 연결을 맺습니다 .

TcpClient smtpServer = new TcpClient(txtSmtpServer.Text, 25);

•  다음은 SMTP 서버와 통신을 하기 위하여 Stream 클래스를 생성 합니다 . 서버로 보내는 스트림을 작성 하기 위하여 NetworkStream 을 사용 하고 서버로부터 수신되는 응답을 읽기 위해 StreamReader 를 사용 합니다 . 이 스티림은 ReadLine() 이라는 메소드를 제공 하므로 NetworkStream 의 Read() 를 호출 하는 것 보다 훨씬 사용이 편리 합니다 . Read() 메소드는 스트림을 바이트 단위로 읽기 때문에 Encoding Class 를 이용하여 문자열로 변환해야 합니다 .

NetworkStream writeStream = smtpServer.GetStream();

StreamReader readStream = new StremaReader(smtpServer.GetStream());

•  서버에 연결이 맺어지면 이를 알려주는 메시지를 표시 합니다 . StreamReader.ReadLine() 을 이용하여 메시지를 읽고 , 이 메시지를 리스트 박스에 표시 합니다 .

receiveData = readStream.ReadLine();

lstLog.Items.Add(receiveData);

•  그 다음 NetworkStream 객체에 SMTP MAIL FROM 명령을 전송하여 서버에 사용자의 전자 우편 주소를 알려 줍니다 .

// 보내는 사람의 이메일 주소를 보낸다 .

sendString = “MAIL FROM: “ + “<” + txtFrom.Text + “>\r\n”;

dataToSend = Encoding.Default.GetBytes(sendString);

writeStream.Write(dataToSend, 0, dataToSend.Length);

// 응답메시지를 표시 합니다 .

receiveData = readStream.ReadLine();

lstLog.Items.Add(receiveData);

•  RCPT TO 명령어에서 받을 사람의 이메일 주소를 알려 줍니다 .

// 받는 사람의 이메일 주소를 보낸다 .

sendString = “RCPT TO: “ + “<” + txtTo.Text + “>\r\n”;

dataToSend = Encoding.Default.GetBytes(sendString);

writeStream.Write(dataToSend, 0, dataToSend.Length);

// 응답메시지를 표시 합니다 .

receiveData = readStream.ReadLine();

lstLog.Items.Add(receiveData);

•  이제 두 전자우편 주소에 대한 인증 작업이 끝나면 DATA 라는 SMTP 명령어 뒤에 실제 데이터를 전송 합니다 .

// 데이터를 보낸다 .

sendString = “DATA “ + “\r\n”;

dataToSend = Encoding.Default.GetBytes(sendString);

writeStream.Write(dataToSend, 0, dataToSend.Length);

// 응답메시지를 표시 합니다 .

receiveData = readStream.ReadLine();

lstLog.Items.Add(receiveData);

// 메시지의 제목과 본문 내용을 전송 합니다 . 그리고 마지막엔 .(Dot) 을 전송

sendString = “SUBJECT: “ + txtSubject.Text + “\r\n” + txtMessage.Text + “\r\n” + “.” + “\r\n”;

dataToSend = Encoding.Default.GetBytes(sendString);

writeStream.Write(dataToSend, 0, dataToSend.Length);

// 응답메시지를 표시 합니다 .

receiveData = readStream.ReadLine();

lstLog.Items.Add(receiveData);

•  마지막으로 서버에 QUIT 명령을 전송하여 애플리케이션이 사용 하고 있는 리소스를 해제 합니다 .

// 서버에 연결 종료 메시지를 보냅니다 .

sendString = “QUIT “ + “\r\n”;

dataToSend = Encoding.Default.GetBytes(sendString);

writeStream.Write(dataToSend, 0, dataToSend.Length);

// 응답메시지를 표시 합니다 .

receiveData = readStream.ReadLine();

lstLog.Items.Add(receiveData);

// 열려진 모든 리소스를 닫습니다 .

writeStream.Close();

readStream.Close();

smtpServer.Close();

•  ClientForm.cs

using System;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

using System.Net.Sockets;

using System.Net;

using System.Text;

using System.IO;

using System.Text.RegularExpressions;

namespace MailClient

{

///

/// Summary description for Form1.

///

public class MailForm : System.Windows.Forms.Form

{

private System.Windows.Forms.Label label5;

private System.Windows.Forms.ListBox lstLog;

private System.Windows.Forms.Button btnSend;

private System.Windows.Forms.TextBox txtMessage;

private System.Windows.Forms.TextBox txtSubject;

private System.Windows.Forms.TextBox txtTo;

private System.Windows.Forms.TextBox txtFrom;

private System.Windows.Forms.TextBox txtSmtpServer;

private System.Windows.Forms.Label label6;

private System.Windows.Forms.Label labelSubject;

private System.Windows.Forms.Label labelTo;

private System.Windows.Forms.Label labelFrom;

private System.Windows.Forms.Label labelServer;

///

/// Required designer variable.

///

private System.ComponentModel.Container components = null;

public MailForm()

{

//

// Required for Windows Form Designer support

//

InitializeComponent();

//

// TODO: Add any constructor code after InitializeComponent call

//

}

///

/// Clean up any resources being used.

///

protected override void Dispose( bool disposing )

{

if( disposing )

{

if (components != null)

{

components.Dispose();

}

}

base.Dispose( disposing );

}

#region Windows Form Designer generated code

///

/// Required method for Designer support - do not modify

/// the contents of this method with the code editor.

///

private void InitializeComponent()

{

this.label5 = new System.Windows.Forms.Label();

this.lstLog = new System.Windows.Forms.ListBox();

this.btnSend = new System.Windows.Forms.Button();

this.txtMessage = new System.Windows.Forms.TextBox();

this.txtSubject = new System.Windows.Forms.TextBox();

this.txtTo = new System.Windows.Forms.TextBox();

this.txtFrom = new System.Windows.Forms.TextBox();

this.txtSmtpServer = new System.Windows.Forms.TextBox();

this.label6 = new System.Windows.Forms.Label();

this.labelSubject = new System.Windows.Forms.Label();

this.labelTo = new System.Windows.Forms.Label();

this.labelFrom = new System.Windows.Forms.Label();

this.labelServer = new System.Windows.Forms.Label();

this.SuspendLayout();

//

// label5

//

this.label5.AutoSize = true;

this.label5.Location = new System.Drawing.Point(22, 128);

this.label5.Name = "label5";

this.label5.Size = new System.Drawing.Size(107, 17);

this.label5.TabIndex = 25;

this.label5.Text = "Status Messages:";

//

// lstLog

//

this.lstLog.ItemHeight = 12;

this.lstLog.Location = new System.Drawing.Point(22, 144);

this.lstLog.Name = "lstLog";

this.lstLog.Size = new System.Drawing.Size(450, 112);

this.lstLog.TabIndex = 24;

//

// btnSend

//

this.btnSend.Location = new System.Drawing.Point(360, 24);

this.btnSend.Name = "btnSend";

this.btnSend.Size = new System.Drawing.Size(112, 104);

this.btnSend.TabIndex = 23;

this.btnSend.Text = "&Send";

this.btnSend.Click += new System.EventHandler(this.btnSend_Click);

//

// txtMessage

//

this.txtMessage.Location = new System.Drawing.Point(20, 288);

this.txtMessage.Multiline = true;

this.txtMessage.Name = "txtMessage";

this.txtMessage.Size = new System.Drawing.Size(452, 216);

this.txtMessage.TabIndex = 22;

this.txtMessage.Text = "";

//

// txtSubject

//

this.txtSubject.Location = new System.Drawing.Point(118, 104);

this.txtSubject.Name = "txtSubject";

this.txtSubject.Size = new System.Drawing.Size(234, 21);

this.txtSubject.TabIndex = 20;

this.txtSubject.Text = "";

//

// txtTo

//

this.txtTo.Location = new System.Drawing.Point(118, 72);

this.txtTo.Name = "txtTo";

this.txtTo.Size = new System.Drawing.Size(234, 21);

this.txtTo.TabIndex = 18;

this.txtTo.Text = "";

//

// txtFrom

//

this.txtFrom.Location = new System.Drawing.Point(118, 48);

this.txtFrom.Name = "txtFrom";

this.txtFrom.Size = new System.Drawing.Size(234, 21);

this.txtFrom.TabIndex = 15;

this.txtFrom.Text = "";

//

// txtSmtpServer

//

this.txtSmtpServer.Location = new System.Drawing.Point(118, 24);

this.txtSmtpServer.Name = "txtSmtpServer";

this.txtSmtpServer.Size = new System.Drawing.Size(234, 21);

this.txtSmtpServer.TabIndex = 13;

this.txtSmtpServer.Text = "";

//

// label6

//

this.label6.AutoSize = true;

this.label6.Location = new System.Drawing.Point(22, 272);

this.label6.Name = "label6";

this.label6.Size = new System.Drawing.Size(109, 17);

this.label6.TabIndex = 21;

this.label6.Text = "Message to Send:";

//

// labelSubject

//

this.labelSubject.AutoSize = true;

this.labelSubject.Location = new System.Drawing.Point(22, 104);

this.labelSubject.Name = "labelSubject";

this.labelSubject.Size = new System.Drawing.Size(52, 17);

this.labelSubject.TabIndex = 19;

this.labelSubject.Text = "Subject:";

//

// labelTo

//

this.labelTo.AutoSize = true;

this.labelTo.Location = new System.Drawing.Point(22, 72);

this.labelTo.Name = "labelTo";

this.labelTo.Size = new System.Drawing.Size(23, 17);

this.labelTo.TabIndex = 17;

this.labelTo.Text = "To:";

//

// labelFrom

//

this.labelFrom.AutoSize = true;

this.labelFrom.Location = new System.Drawing.Point(22, 48);

this.labelFrom.Name = "labelFrom";

this.labelFrom.Size = new System.Drawing.Size(38, 17);

this.labelFrom.TabIndex = 16;

this.labelFrom.Text = "From:";

//

// labelServer

//

this.labelServer.AutoSize = true;

this.labelServer.Location = new System.Drawing.Point(22, 24);

this.labelServer.Name = "labelServer";

this.labelServer.Size = new System.Drawing.Size(79, 17);

this.labelServer.TabIndex = 14;

this.labelServer.Text = "Smtp Server:";

//

// MailForm

//

this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);

this.ClientSize = new System.Drawing.Size(496, 518);

this.Controls.Add(this.label5);

this.Controls.Add(this.lstLog);

this.Controls.Add(this.btnSend);

this.Controls.Add(this.txtMessage);

this.Controls.Add(this.txtSubject);

this.Controls.Add(this.txtTo);

this.Controls.Add(this.txtFrom);

this.Controls.Add(this.txtSmtpServer);

this.Controls.Add(this.label6);

this.Controls.Add(this.labelSubject);

this.Controls.Add(this.labelTo);

this.Controls.Add(this.labelFrom);

this.Controls.Add(this.labelServer);

this.Name = "MailForm";

this.Text = "SMTP Client";

this.ResumeLayout(false);

}

#endregion

///

/// The main entry point for the application.

///

[STAThread]

static void Main ()

{

Application.Run(new MailForm());

}

private void btnSend_Click(object sender, System.EventArgs e)

{

// change the cursor to hourglass

Cursor appCursor = Cursor.Current;

Cursor.Current = Cursors.WaitCursor;

string sendString;

byte [] dataToSend;

string receiveData;

try

{

// creating an instance of the TcpClient class

TcpClient smtpServer = new TcpClient(txtSmtpServer.Text, 25);

lstLog.Items.Add("Connection Established with " + txtSmtpServer.Text);

// creating stream classes for communication

NetworkStream writeStream = smtpServer.GetStream();

StreamReader readStream = new StreamReader(smtpServer.GetStream());

sendString = "HELO " + txtSmtpServer.Text + "\r\n";

dataToSend = Encoding.ASCII.GetBytes(sendString);

writeStream.Write(dataToSend,0,dataToSend.Length);

receiveData = readStream.ReadLine();

lstLog.Items.Add(receiveData);

// sending From Email Address

sendString = "MAIL FROM: " + "<" + txtFrom.Text + ">\r\n";

dataToSend = Encoding.ASCII.GetBytes(sendString);

writeStream.Write(dataToSend,0,dataToSend.Length);

receiveData = readStream.ReadLine();

lstLog.Items.Add(receiveData);

// sending To Email Address

sendString = "RCPT TO: " + "<" + txtTo.Text + ">\r\n";

dataToSend = Encoding.ASCII.GetBytes(sendString);

writeStream.Write(dataToSend,0,dataToSend.Length);

receiveData = readStream.ReadLine();

lstLog.Items.Add(receiveData);

// sending data

sendString = "DATA " + "\r\n";

dataToSend = Encoding.ASCII.GetBytes(sendString);

writeStream.Write(dataToSend,0,dataToSend.Length);

receiveData = readStream.ReadLine();

lstLog.Items.Add(receiveData);

// sending Message Subject and Text

sendString = "SUBJECT: " + txtSubject.Text + "\r\n" + txtMessage.Text + "\r\n" + "." + "\r\n";

dataToSend = Encoding.ASCII.GetBytes(sendString);

writeStream.Write(dataToSend,0,dataToSend.Length);

receiveData = readStream.ReadLine();

lstLog.Items.Add(receiveData);

// sending Disconnect from Server

sendString = "QUIT " + "\r\n";

dataToSend = Encoding.ASCII.GetBytes(sendString);

writeStream.Write(dataToSend,0,dataToSend.Length);

receiveData = readStream.ReadLine();

lstLog.Items.Add(receiveData);

// closing all open resources

writeStream.Close();

readStream.Close();

smtpServer.Close();

}

catch(SocketException se)

{

MessageBox.Show("SocketException:" + se.ToString());

}

catch(Exception excep)

{

MessageBox.Show("Exception:" + excep.ToString());

}

// restoring the cursor state

Cursor.Current = appCursor;

}

}