2015년 12월 22일 화요일

[강좌#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 동작 메커니즘
-----------------
-------------
SMTP 명령어
-------------
• HELO. 이 명령어는 호스트간의 통신을 초기화 합니다 . 송신 호스트를 식별하기 위한 파라미터를 넘겨 주며 수신 호스트는 식별 했음을 알려 줍니다 . 이 명령이 수행되었다는 것은 통신을 시작 할 준비가 되었다는 것 입니다 . 정상적인 수행인 경우 250 번 코드를 넘겨 줍니다 .
송신 시스템 : HELO mail.oraclejavanew.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 는 다음과 같습니다 .
소스 코딩을 하기 전에 개략적으로 소스 내용을 정리 해 보도록 하겠습니다 ,
• 사용자가 톰에서 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.Regular__EXPRESSION__s;
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;
}
}

댓글 없음:

댓글 쓰기