티스토리 뷰
--------------------------------------------------------
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.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 는 다음과 같습니다 .
소스 코딩을 하기 전에 개략적으로 소스 내용을 정리 해 보도록 하겠습니다 , • 사용자가 톰에서 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 + “>rn”; 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 + “>rn”; dataToSend = Encoding.Default.GetBytes(sendString); writeStream.Write(dataToSend, 0, dataToSend.Length); // 응답메시지를 표시 합니다 . receiveData = readStream.ReadLine(); lstLog.Items.Add(receiveData); • 이제 두 전자우편 주소에 대한 인증 작업이 끝나면 DATA 라는 SMTP 명령어 뒤에 실제 데이터를 전송 합니다 . // 데이터를 보낸다 . sendString = “DATA “ + “rn”; dataToSend = Encoding.Default.GetBytes(sendString); writeStream.Write(dataToSend, 0, dataToSend.Length); // 응답메시지를 표시 합니다 . receiveData = readStream.ReadLine(); lstLog.Items.Add(receiveData); // 메시지의 제목과 본문 내용을 전송 합니다 . 그리고 마지막엔 .(Dot) 을 전송 sendString = “SUBJECT: “ + txtSubject.Text + “rn” + txtMessage.Text + “rn” + “.” + “rn”; dataToSend = Encoding.Default.GetBytes(sendString); writeStream.Write(dataToSend, 0, dataToSend.Length); // 응답메시지를 표시 합니다 . receiveData = readStream.ReadLine(); lstLog.Items.Add(receiveData); • 마지막으로 서버에 QUIT 명령을 전송하여 애플리케이션이 사용 하고 있는 리소스를 해제 합니다 . // 서버에 연결 종료 메시지를 보냅니다 . sendString = “QUIT “ + “rn”; 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 + "rn"; 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 + ">rn"; 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 + ">rn"; dataToSend = Encoding.ASCII.GetBytes(sendString); writeStream.Write(dataToSend,0,dataToSend.Length); receiveData = readStream.ReadLine(); lstLog.Items.Add(receiveData); // sending data sendString = "DATA " + "rn"; 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 + "rn" + txtMessage.Text + "rn" + "." + "rn"; dataToSend = Encoding.ASCII.GetBytes(sendString); writeStream.Write(dataToSend,0,dataToSend.Length); receiveData = readStream.ReadLine(); lstLog.Items.Add(receiveData); // sending Disconnect from Server sendString = "QUIT " + "rn"; 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; } } } | |