C# 메일 보내기 질문 (2)

41429 단어 개발우편물서버
3. C#에서 TcpClient 기반 메일 보내기 구성 요소 만들기
이전 절에서 Dos 명령줄 아래에서 SMTP 서버 연결을 테스트할 때 SMTP의 일부 명령을 사용했지만 정보를 인코딩하고 디코딩할 수 없고 인증과 정보 전송을 계속할 수 없습니다.있다Net 라이브러리에서 System을 사용할 수 있습니다.Net.Sockets.TcpClient 클래스는 이전 메일링 구성 요소의 같은 기능을 실현합니다. (사실 OpenSmtp도 이 구성 요소를 바탕으로 개발되었습니다.) 여기는 SMTP 프로토콜의 규범을 충분히 이해하기 위한 테스트입니다.
1, SMTP 명령 및 응답
메일 발송의 기본 과정은 일문일답 방식으로 서버와 교류하기 때문에 SMTP 명령과 응답에 대한 자세한 내용은 RFC821을 참조하십시오.
일반적인 SMTP/ESMTP 명령은 다음과 같습니다.
명령
작용
HELO
표준 SMTP를 사용하여 서버에 사용자 ID 식별
EHLO
ESMTP를 사용하여 서버에 사용자 ID를 식별하고 ESMTP를 지원하는 서버에 대해
STARTTLS
STARTLS를 지원하는 서버에 대한 일반 연결을 보안 연결로 향상시키는 TLS 활성화
AUTH LOGIN
인증 절차 시작
MAIL FROM
보낸 사람 주소 지정
RCPT TO
단일 메일 수신자 지정하기;RCPT TO 여러 개 가능
DATA
전송 데이터, 서버에서 . 데이터 수신 중지
VRFY
지정한 사용자/메일박스가 있는지 확인하십시오. 항상 비활성화됩니다.
EXPN
지정한 메일박스 목록이 있는지 확인하십시오. 항상 비활성화됩니다.
HELP
서버 지원 명령 질의
NOOP
운영 안 함, 서버 응답 250 OK
RSET
세션 재설정, 현재 전송 취소, 서버 응답 250 OK
QUIT
세션 종료
일반적인 SMTP 서버 응답:
500 문법 오류, 알 수 없는 명령 501 매개 변수 문법 오류 502 명령이 실행되지 않았습니다. 503 명령 순서 오류 504 매개 변수가 부여되지 않은 211 시스템 상태 또는 시스템 도움말 응답 214 도움말 정보 220 서비스 준비 221 서비스가 전송 채널을 닫고 있습니다. 421 서비스가 사용할 수 없습니다. 전송 채널 250 작업을 닫고 있습니다. 251 비로컬 사용자를 완성합니다. 450 작업이 완료되지 않았습니다. 메일박스를 사용할 수 없습니다. [예: 메일박스 바쁨] 550 작업이 완료되지 않았습니다. 메일박스를 사용할 수 없습니다. [예: 메일박스가 존재하지 않습니다. 접근할 수 없습니다] 451 작업이 취소되었습니다. 처리 과정에서 오류가 발생했습니다. 551 비로컬 사용자입니다. 452 작업 미완성: 시스템 저장 공간 부족 552 작업 취소: 할당된 저장 공간을 초과한 553 작업 미완성: 메일박스 이름을 사용할 수 없음 [예: 메일박스 이름 문법 오류] 354 메일 데이터 입력을 시작하여 . 종료 554 작업 실패
따라서 만약에 우리가 컨트롤러에서 메일을 출력하고 발송하는 전 과정은 대체적으로 다음과 같다. (서버마다 피드백하는 정보가 다르고 멀티미디어를 가진 메일을 보내는 구조가 더욱 복잡하다면) 그 중에서 Receive는 서버가 데이터를 수신하고 Send는 서버에 데이터를 발송한다.
Send:    EHLO g1
Receive: 250-mail
         250-PIPELINING
         250-AUTH LOGIN PLAIN
         250-AUTH=LOGIN PLAIN
         250-STARTTLS
         250 8BITMIME
Send:    AUTH LOGIN
Receive: 334 dXNlcm5hbWU6
Send:    cWluZ3NwYWNl
Receive: 334 UGFzc3dvcmQ6
Send:    NINULFzLnhtdQ==
Receive: 235 Authentication successful
Send:    MAIL FROM: ******@***.com
Receive: 250 Mail OK
Send:    RCPT TO: <******@***.com>
Receive: 250 Mail OK
Send:    DATA
Receive: 354 End data with <CR><LF>.<CR><LF>
Send:    From: <<******@***.com>
Send:    To: <<******@***.com>
Send:    Subject: =?utf-8?B?5Y+R6YCBIG0yIHZpYSBoMiBVc2VUY3BDbGllbnQg?=
Send:    Date: Fri, 16 May 2014 01:17:40 GMT
Send:    MIME-Version: 1.0
Send:    Content-Type: text/html;
Send:      charset="utf-8"
Send:    Content-Transfer-Encoding: base64
Send:
Send:    5rWL6K+V44CCSnVzdCBhIHRlc3QuPGJyLz48aW1nIHNyYz0nY2lkOlVtVnpiM1Z5WTJVdWFu
Send:    Qm4nIGFsdD0nJy8+
Send:    .
Receive: 250 Mail OK queued as AgAi0gCXn8M0Z3VTmF4QAA--.4500S2
Send:    QUIT
Receive: 221 Bye

2. C# 인코딩으로 메일 보내기
다음으로 우리는Net 클래스 라이브러리의 TcpClient 클래스는 서버와의 상호 작용을 수행합니다.
먼저 ISendMail 인터페이스와 같은 클래스를 설정합니다. UseTcpClient
또한 내부 클래스 메시지를 데이터 캐리어로 설정하고utf-8을 전역 문자 인코딩으로 정의하며base64를 전역 전송 인코딩으로 정의합니다.
    using System.Net.Sockets;
    public class UseTcpClient : ISendMail
    {
        private TcpClient Tcp { get; set; }
        private Stream Stream { get; set; }
        private Message Mail { get; set; }

        private string ContentTransferEncoding = "base64";
        private Encoding Charset = Encoding.UTF8;

        private class Message
        {
            public Message()
            { }

            public Message(string from, string[] to)
            {
                From = from;
                To = to;
                Data = new List<string>();
            }
            public string From { get; set; }
            public string[] To { get; set; }
            public List<string> Data { get; set; }
        }

        public void CreateHost(ConfigHost host)
        {
            throw new NotImplementedException();
        }

        public void CreateMail(ConfigMail mail)
        {
            throw new NotImplementedException();
        }

        public void CreateMultiMail(ConfigMail mail)
        {
            throw new NotImplementedException();
        }

        public void SendMail()
        {
            throw new NotImplementedException();
        }
       
    }

이제 CreateHost 구현 방법
SSL을 사용하여 서버에 연결할 때 TcpClient가 필요합니다.GetStream()이 반환하는 NetworkStream은 SslStream으로 포장됩니다.서버가 사전 소통을 하는 과정에서 일문일답식은 명백히 알 수 있다
        public void CreateHost(ConfigHost host)
        {
            if (host.Server != null && host.Port != 0)
            {
                Tcp = new TcpClient(host.Server, host.Port);
                Tcp.SendTimeout = 50000;
                Tcp.SendBufferSize = 1024;
                Tcp.ReceiveTimeout = 50000;
                Tcp.ReceiveBufferSize = 1024;

                if (host.EnableSsl)
                {
                    var ssl = new SslStream(Tcp.GetStream());
                    ssl.AuthenticateAsClient(host.Server, null, System.Security.Authentication.SslProtocols.Tls, false);
                    Stream = ssl;
                }
                else
                    Stream = Tcp.GetStream();

                LingerOption lingerOption = new LingerOption(true, 10);
                Tcp.LingerState = lingerOption;
                CheckErrorCode(ReadStream(), "220");
                if (!string.IsNullOrEmpty(host.Username) && !string.IsNullOrEmpty(host.Password))
                {
                    WriteStream("EHLO " + Dns.GetHostName() + "\r
"); CheckErrorCode(ReadStream(), "250"); WriteStream("AUTH LOGIN\r
"); if (CheckReplyCode(ReadStream(), "334")) { WriteStream(ConvertToBase64(host.Username) + "\r
"); CheckErrorCode(ReadStream(), "334"); WriteStream(ConvertToBase64(host.Password) + "\r
"); CheckErrorCode(ReadStream(), "235"); } } else { WriteStream("HELO " + Dns.GetHostName() + "\r
"); CheckErrorCode(ReadStream(), "250"); } } }

우리는 WriteStream() 방법으로 명령과 데이터를 보내고 ReadStream() 방법으로 서버 피드백을 얻으며 CheckErrorCode() 및 CheckReplyCode() 방법으로 피드백의 정보가 이상이 아니라고 판단하여 다음 단계를 진행합니다.
TcpClient가 보내는 데이터는 제한이 있기 때문에 비교적 긴 데이터를 보낼 때 데이터를 몇 번으로 나누어 보내는 것이 가장 좋다.사실 이렇게 하면 여전히 문제를 가져올 수 있다. 우리가 데이터 흐름을 동기화하는 방식을 사용하기 때문에 빅데이터, 예를 들어 첨부 파일의 발송은 네트워크 전송이나 서버의 상호작용 문제로 인해 이상을 초래할 수 있기 때문에 Lumi Soft 프로젝트에서 사용하는 것은 비동기적인 방식이다. 여기서 우리는 모두 테스트를 하고 테스트를 할 때 비교적 작은 첨부 파일을 사용하여 이런 문제를 피한다.
        private void WriteStream(string request)
        {
            byte[] buffer = Charset.GetBytes(request);
            var pageSize = 72;
            var totalPages = (int)Math.Ceiling(((double)buffer.Length) / pageSize);
            for (var i = 0; i < totalPages; i++)
            {
                Stream.Write(buffer, i * pageSize, i == totalPages - 1 ? buffer.Length - i * pageSize : pageSize);
                Console.WriteLine("Send(" + i + "):" + Charset.GetString(buffer, i * pageSize, i == totalPages - 1 ? buffer.Length - i * pageSize : pageSize));
            }
        }

        private string ReadStream()
        {
            var buffer = new byte[1024];
            var size = Stream.Read(buffer, 0, buffer.Length);
            var response = Charset.GetString(buffer, 0, size);
            Console.WriteLine("Receive: " + response);
            return response;
        }

        private void CheckErrorCode(string response, string code)
        {
            if (response.IndexOf(code) == -1)
            {
                throw new Exception("Exception: " + response);
            }
        }

        private bool CheckReplyCode(string response, string code)
        {
            if (response.IndexOf(code) == -1)
            {
                return false;
            }
            else
            {
                return true;
            }
        }

다음 몇 가지 방법은 네트워크에서 전송하기 위해base64 인코딩을 하는 데 사용됩니다.
ConvertToBase64() 메서드:
ConvertToBase64 () 방법은 간단하게utf-8 인코딩된 문자열을 base64 인코딩하는 것입니다.전송 인코딩은 메일 제목, 본문(다국어 포함), 첨부 파일, 자원 삽입(이진 데이터) 등을 특정 문자 집합으로 변환하여 순수한 텍스트의 메일 전송 환경에 적응하도록 정의한다.주요 인코딩 방식은quoted-printable과base64입니다.
"Base64 인코딩은 입력한 데이터를 모두 지정된 ASCII 문자 64개로 변환하는 문자 시퀀스입니다. 이 64자 문자는 {'A'-'Z','a'-'z','0'-'+','/'}로 구성됩니다. 인코딩할 때 변환해야 할 데이터를 6bit씩 꺼내서 10진수로 변환합니다. 이 숫자의 범위는 최소 0, 최대 63입니다. 그리고 {'A'-'Z','a'-'z','0'-'9','+','/'}를 조회합니다.구성된 사전표는 해당 위치의 ASCII 부호 문자를 출력합니다. 이렇게 하면 3바이트당 데이터 내용이 4개의 사전의 ASCII 부호 문자로 변환되고 데이터 끝에 3바이트가 부족할 때 "="로 채워집니다.
"Quoted-printable 인코딩 역시 입력한 정보를 인쇄 가능한 ASCII 문자로 변환하지만 인코딩 여부는 정보의 내용에 따라 결정됩니다. 읽은 바이트가 33-60, 62-126 범위에 있으면 직접 인쇄 가능한 ASCII 문자로 변환됩니다. 그렇지 않으면 이 바이트를 두 개의 4bit으로 나누어 각각 16진수로 표시하고 앞에'='를 추가합니다.이렇게 하면 인코딩이 필요한 바이트마다 세 문자로 변환되어 표시됩니다."
중국어에 대한 더 좋은 지원을 얻기 위해base64로 설정하는 것이 좋습니다.
ConvertHeaderToBase64() 메서드:
메일 내용의 각 유형에는 메일 본문, 첨부 파일, 삽입 자원을 포함하고 Content-Transfer-Encoding 필드 값을 설정하여 이 유형의 전송 인코딩을 정의할 수 있습니다.제목과 파일 이름 자체가 필드이기 때문에 그 값의 전송 인코딩을 설정하는 데는 특수한 방법이 필요하다. 즉, Convert Header ToBase64()가 해야 할 일이다.
이렇게 하면 메일 제목 필드의 값은 =?{문자 인코딩}?{전송 인코딩}?{인코딩된 문자열}?=.그 중에서 전송 인코딩은 약칭을 사용하는데 B는base64를 대표하고 Q는quoted-printable를 대표하기 때문에 중국어 제목은 다음과 같이 정의할 수 있다. =?utf-8?B?5Y+R6YCBIG0yIHZpYSBoMiBVc2VUY3BDbGllbnQg?= .
ConvertFileToBase64() 메서드:
ConvertFile ToBase64 () 방법은 첨부 파일과 다른 내장 자원을 바이너리 형식으로base64비트 인코딩으로 변환하여 다양한 종류의 파일을 메일로 전송할 수 있습니다.
        private string ConvertToBase64(string str)
        {
            byte[] buffer = Charset.GetBytes(str.ToCharArray());
            return Convert.ToBase64String(buffer);
        }

        private string ConvertHeaderToBase64(string str)
        {
            if (MustEncode(str))
            {
                return "=?" + Charset.WebName + "?B?" + ConvertToBase64(str) + "?=";
            }
            return str;
        }

        private string ConvertFileToBase64(string file)
        {
            var fs = new FileStream(file, FileMode.Open, FileAccess.Read);
            var buffer = new byte[(int)fs.Length];
            fs.Read(buffer, 0, buffer.Length);
            var fileStr = Convert.ToBase64String(buffer);
            fs.Close();
            return fileStr;
        }

        private bool MustEncode(string str)
        {
            if (!string.IsNullOrEmpty(str))
            {
                foreach (char c in str)
                {
                    if (c > 127)
                    {
                        return true;
                    }
                }
            }
            return false;
        }

다음 CreateMail 구현 방법
이 방법은 메일의 내용을 만들지만 첨부 파일과 내장 자원은 포함하지 않고 본문일 뿐입니다.데이터 명령 후에 서버에 한 줄씩 보낼 수 있도록 메일 내용 문자열 그룹을 간단하게 만드는 것을 볼 수 있습니다.
        public void CreateMail(ConfigMail mail)
        {
            Mail = new Message(mail.From, mail.To);
            Mail.Data.Add("From: <" + mail.From + ">\r
"); foreach (var to in mail.To) { Mail.Data.Add("To: <" + mail.From + ">\r
"); } Mail.Data.Add("Subject: " + ConvertHeaderToBase64(mail.Subject) + "\r
"); Mail.Data.Add("Date: " + DateTime.Now.ToUniversalTime().ToString("R") + "\r
"); Mail.Data.Add("MIME-Version: 1.0\r
"); Mail.Data.Add("Content-Type: text/html;\r
"); Mail.Data.Add(" charset=\"" + Charset.WebName + "\"\r
"); Mail.Data.Add("Content-Transfer-Encoding: " + ContentTransferEncoding + "\r
"); Mail.Data.Add("\r
"); // It is important, otherwise the body may be missing. Mail.Data.Add(ConvertToBase64(mail.Body) + "\r
"); }

CreateMultiMail 구현 방법
이 방법은 메일의 내용을 만들고 첨부 파일과 내장 자원을 포함한다.메일의 내용은 각 부분으로 나뉘어 각 부분에는 Content-Type과 Charset이 표시되고 Content-Transfer-Encoding도 설정된다.위에서 Content-Transfer-Encoding에 대해 논의한 적이 있습니다. 이제 Content-Type에 대해 자세히 알아야 합니다.
Content-Type 필드는 메일 내용의 각 부분의 유형과 관련 속성을 정의합니다.메일 내용 중 외곽에 있는 것은 모두multipart 형식이고,multipart는 세 개의 하위 형식을 포함한다:multipart/mixed,multipart/related,multipart/alternative.이 세 가지 멀티파트의 하위 유형은 메일 내용에 끼워 넣는 관계이다.
multipart/mixed
multipart/related
multipart/alternative text/plain 순수 텍스트 본문 text/html 하이퍼텍스트 본문
내장 리소스
액세서리
위의 그림과 같이 첨부 파일을 포함하면 첨부 파일 외곽에서multipart/mixed를 성명하고, 내장 자원을 포함하면 내장 자원 외곽에서multipart/related를 성명하고, 텍스트/plain과text/html가 동시에 존재하면 텍스트 외곽에서multipart/alternative를 성명합니다.이러한 유형의 내용 범위는 boundary 속성이 정의한 유일한 표지에 의해 결정됩니다. "―{boundary}"로 시작하여 "--{boundary}--"로 끝납니다. 서로 다른 유형의 내용 사이에는 빈 줄로 구분해야 하기 때문에 메일 내용은 대략 다음과 같습니다.
From: <******@***.com>
To: <******@***.com>
Subject: =?utf-8?B?5Y+R6YCBIG0yIHZpYSBoMiBVc2VUY3BDbGllbnQg?=
Date: Thu, 15 May 2014 11:06:51 GMT
MIME-Version: 1.0
Content-Type: multipart/mixed;
    boundary="b4ed1357_39ae_4098_a043_df80407fb136"
Message-Id: <53749FCC.00C525.01636@***.com>

This is a multi-part message in MIME format.

--b4ed1357_39ae_4098_a043_df80407fb136
Content-Type: multipart/related;
    boundary="4954e4a1_b756_497d_8daa_458ecf101a1c"

--4954e4a1_b756_497d_8daa_458ecf101a1c
Content-Type: multipart/alternative;
    boundary="91de458a_e772_46ac_ab87_0c6fa2009e35"

--91de458a_e772_46ac_ab87_0c6fa2009e35
Content-Type: text/plain;
    charset="utf-8"
Content-Transfer-Encoding: base64

SWYgeW91IHNlZSB0aGlzIG1lc3NhZ2UsIGl0IG1lYW5zIHRoYXQgeW91ciBtYWlsIGNsaWVudCBkb2VzIG5vdCBzdXBwb3J0IGh0bWwu

--91de458a_e772_46ac_ab87_0c6fa2009e35
Content-Type: text/html;
    charset="utf-8"
Content-Transfer-Encoding: base64

5rWL6K+V44CCSnVzdCBhIHRlc3QuPGJyLz48aW1nIHNyYz0nY2lkOlVtVnpiM1Z5WTJVdWFuQm4nIGFsdD0nJy8+

--91de458a_e772_46ac_ab87_0c6fa2009e35--


--4954e4a1_b756_497d_8daa_458ecf101a1c
Content-ID: <UmVzb3VyY2UuanBn>
Content-Type: application/octet-stream;
    name="Resource.jpg"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
    filename="Resource.jpg"

/9j/4QCpRXhpZgAASUkqAAgAAAAFABIBAwABAAAAAQAAADEBAgAVAAAASgAAADIBAgAUAAAAXwAAABMCAwABAAAAAQAAAGmHBAABAAAAcwAAAAAAAABBQ0QgU3lzdGVtcyDK/cLrs8nP8QAyMDEwOjExOjE2IDE1OjExOjQ5AAMAkJICAAQAAAA4MTIAAq

--4954e4a1_b756_497d_8daa_458ecf101a1c--


--b4ed1357_39ae_4098_a043_df80407fb136
Content-Type: application/octet-stream;
    name="Attachment.docx"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
    filename="Attachment.docx"

FvoPRtdhKeiil2M6hP8c20PQBFGmdiNqSkDZ/b9+145hSEocmGIwZrUTrZVGa3BB21NxsbJiEVgpFXaLDL2NXuLH1kUUBglSmsgYzsIbJLf3qSznYMQ0bQJGVsiuifOg1xCJUJiHRj6UlhfCaRXvBOyGxAH4/Gj1waQ2CwRhrBsvTDzLgtYJoKjy

--b4ed1357_39ae_4098_a043_df80407fb136--

다음은 인코딩을 통해 이루어집니다.
        public void CreateMultiMail(ConfigMail mail)
        {
            Mail = new Message(mail.From, mail.To);
            Mail.Data.Add("From: <" + mail.From + ">\r
"); foreach (var to in mail.To) { Mail.Data.Add("To: <" + mail.From + ">\r
"); } Mail.Data.Add("Subject: " + ConvertHeaderToBase64(mail.Subject) + "\r
"); Mail.Data.Add("Date: " + DateTime.Now.ToUniversalTime().ToString("R") + "\r
"); Mail.Data.Add("MIME-Version: 1.0\r
"); var mixedBoundary = Guid.NewGuid().ToString().Replace("-", "_"); if (mail.Attachments != null && mail.Attachments.Length > 0) { Mail.Data.Add("Content-Type: multipart/mixed;\r
"); Mail.Data.Add(" boundary=\"" + mixedBoundary + "\"\r
"); Mail.Data.Add("\r
"); Mail.Data.Add("This is a multi-part message in MIME format.\r
"); Mail.Data.Add("\r
"); Mail.Data.Add("--" + mixedBoundary + "\r
"); } var relatedBoundary = Guid.NewGuid().ToString().Replace("-", "_"); if (mail.Resources != null && mail.Resources.Length > 0) { Mail.Data.Add("Content-Type: multipart/related;\r
"); Mail.Data.Add(" boundary=\"" + relatedBoundary + "\"\r
"); Mail.Data.Add("\r
"); Mail.Data.Add("--" + relatedBoundary + "\r
"); } var altBoundary = Guid.NewGuid().ToString().Replace("-", "_"); Mail.Data.Add("Content-Type: multipart/alternative;\r
"); Mail.Data.Add(" boundary=\"" + altBoundary + "\"\r
"); Mail.Data.Add("\r
"); Mail.Data.Add("--" + altBoundary + "\r
"); Mail.Data.Add("Content-Type: text/plain;\r
"); Mail.Data.Add(" charset=\"" + Charset.WebName + "\"\r
"); Mail.Data.Add("Content-Transfer-Encoding: " + ContentTransferEncoding + "\r
"); Mail.Data.Add("\r
"); Mail.Data.Add(ConvertToBase64("If you see this message, it means that your mail client does not support html.") + "\r
"); Mail.Data.Add("\r
"); Mail.Data.Add("--" + altBoundary + "\r
"); Mail.Data.Add("Content-Type: text/html;\r
"); Mail.Data.Add(" charset=\"" + Charset.WebName + "\"\r
"); Mail.Data.Add("Content-Transfer-Encoding: " + ContentTransferEncoding + "\r
"); Mail.Data.Add("\r
"); Mail.Data.Add(ConvertToBase64(mail.Body) + "\r
"); Mail.Data.Add("\r
"); Mail.Data.Add("--" + altBoundary + "--\r
"); if (mail.Resources != null && mail.Resources.Length > 0) { foreach (var resource in mail.Resources) { var fileInfo = new FileInfo(resource); if (fileInfo.Exists) { Mail.Data.Add("\r
"); Mail.Data.Add("\r
"); Mail.Data.Add("--" + relatedBoundary + "\r
"); Mail.Data.Add("Content-ID: <" + ConvertToBase64(fileInfo.Name) + ">\r
"); Mail.Data.Add("Content-Type: " + GetMimeType(fileInfo.Extension) + ";\r
"); Mail.Data.Add(" name=\"" + ConvertHeaderToBase64(fileInfo.Name) + "\"\r
"); Mail.Data.Add("Content-Transfer-Encoding: " + ContentTransferEncoding + "\r
"); Mail.Data.Add("Content-Disposition: attachment;\r
"); Mail.Data.Add(" filename=\"" + ConvertHeaderToBase64(fileInfo.Name) + "\"\r
"); Mail.Data.Add("\r
"); var fileStr = ConvertFileToBase64(resource); Mail.Data.Add(fileStr + "\r
"); } } Mail.Data.Add("\r
\r
--
" + relatedBoundary + "--\r
"); } if (mail.Attachments != null && mail.Attachments.Length > 0) { foreach (var attachment in mail.Attachments) { var fileInfo = new FileInfo(attachment); if (fileInfo.Exists) { Mail.Data.Add("\r
"); Mail.Data.Add("\r
"); Mail.Data.Add("--" + mixedBoundary + "\r
"); Mail.Data.Add("Content-Type: " + GetMimeType(fileInfo.Extension) + ";\r
"); Mail.Data.Add(" name=\"" + ConvertHeaderToBase64(fileInfo.Name) + "\"\r
"); Mail.Data.Add("Content-Transfer-Encoding: " + ContentTransferEncoding + "\r
"); Mail.Data.Add("Content-Disposition: attachment;\r
"); Mail.Data.Add(" filename=\"" + ConvertHeaderToBase64(fileInfo.Name) + "\"\r
"); Mail.Data.Add("\r
"); var fileStr = ConvertFileToBase64(attachment); Mail.Data.Add(fileStr + "\r
"); } } Mail.Data.Add("\r
"); Mail.Data.Add("\r
"); Mail.Data.Add("--" + mixedBoundary + "--\r
"); } }

SendMail 구현 방법
        public void SendMail()
        {
            if (Tcp != null && Stream != null)
            {
                WriteStream("MAIL FROM: <" + Mail.From + ">\r
"); CheckErrorCode(ReadStream(), "250"); foreach (var to in Mail.To) { WriteStream("RCPT TO: <" + to + ">\r
"); CheckErrorCode(ReadStream(), "250"); } WriteStream("DATA\r
"); CheckErrorCode(ReadStream(), "354"); foreach (var item in Mail.Data) { WriteStream(item); } WriteStream("\r
.\r
"); CheckErrorCode(ReadStream(), "250"); WriteStream("QUIT\r
"); CheckErrorCode(ReadStream(), "221"); Stream.Close(); Tcp.Close(); } }

3. 테스트
본문만 포함된 간단한 메일 보내기 테스트:
    class Program
    {
        static void Main(string[] args)
        {
            var h1 = new ConfigHost()
            {
                Server = "smtp.gmail.com",
                Port = 465,
                Username = "******@gmail.com",
                Password = "******",
                EnableSsl = true
            };
            var m1 = new ConfigMail()
            {
                Subject = "Test",
                Body = "Just a test.",
                From = "******@gmail.com",
                To = new string[] { "******@gmail.com" },
            };

            var agent = new UseTcpClient();
            var output = "Send m1 via h1 " + agent.GetType().Name + " ";
            Console.WriteLine(output + "start");
            try
            {
                agent.CreateHost(h1);
                m1.Subject = output;
                agent.CreateMail(m1);
                agent.SendMail();
                Console.WriteLine(output + "success");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            Console.WriteLine(output + "end");
            Console.WriteLine("-----------------------------------");

            Console.Read();
        }
    }

멀티미디어 메일 보내기 테스트:
    class Program
    {
        static void Main(string[] args)
        {
            var h2 = new ConfigHost()
            {
                Server = "smtp.163.com",
                Port = 25,
                Username = "******@163.com",
                Password = "******",
                EnableSsl = false
            };
            var m2 = new ConfigMail()
            {
                Subject = "Test",
                Body = "Just a test. <br/><img src='cid:" + Convert.ToBase64String(Encoding.Default.GetBytes("Resource.jpg")) + "' alt=''/> ",
                From = "******@163.com",
                To = new string[] { "******@163.com" },
                Attachments = new string[] { @"E:\Test\SendMail\Attachment.pdf" },
                Resources = new string[] { @"E:\Test\SendMail\Resource.jpg" }
            };

            var agent = new UseTcpClient();
            var output = "Send m2 via h2 " + agent.GetType().Name + " ";
            Console.WriteLine(output + "start");
            try
            {
                agent.CreateHost(h2);
                m2.Subject = output;
                agent.CreateMultiMail(m2);
                agent.SendMail();
                Console.WriteLine(output + "success");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            Console.WriteLine(output + "end");
            Console.WriteLine("-----------------------------------");

            Console.Read();

        }
    }

테스트 과정에서 비교적 작은 첨부 파일이나 그림을 사용하면 성공적으로 발송될 수 있고 모든 것이 정상적이지만 큰 첨부 파일은 일반적으로 실패하기 때문에 코드에 결함이 존재한다. 그 원인은 복잡한 네트워크 환경에서 동기화 발송을 사용하여 이상이 발생하거나 서버가 응답을 잃거나 데이터 흐름에 대한 조작이 신중하지 못하거나 겸비될 수 있기 때문에 더욱 깊이 연구해야 한다.

좋은 웹페이지 즐겨찾기