C#郵件發送問題(二)

三、C#下創建基於TcpClient發送郵件組件

在上一節在Dos命令行下測試SMTP服務器連接時,已經使用了SMTP的部分命令,但是當時無法對信息進行編碼和解碼,也就無法繼續進行身份驗證和信息傳輸。在.Net庫中,我們可以使用System.Net.Sockets.TcpClient類實現上一節發送郵件組件的同樣功能(其實OpenSmtp也同樣是基於這個組件開發的),這裏僅作爲測試以充分了解SMTP協議規範。

1、SMTP命令及其響應

郵件發送的基本過程是一問一答的方式與服務器交流的,所以我們需要先了解關於SMTP命令及其響應,詳情請查閱RFC821

常用的SMTP/ESMTP命令(命令的執行有一定順序)包括:

命令 作用
HELO 使用標準的SMTP,向服務器標識用戶身份
EHLO 使用ESMTP,向服務器標識用戶身份,針對支持ESMTP的服務器
STARTTLS 啓用TLS,將普通連接提升爲安全連接,針對支持STARTTLS 的服務器
AUTH LOGIN 開始認證程序
MAIL FROM 指定發件人地址
RCPT TO 指定單個郵件接收人;可以有多個RCPT TO
DATA 傳輸數據,服務器接收到<CRLF>.<CRLF>就停止接收數據
VRFY 驗證指定的用戶/郵箱是否存在,常被禁用
EXPN 驗證指定的郵箱列表是否存在,常被禁用
HELP 查詢服務器支持的命令
NOOP 無操作,服務器響應 250 OK
RSET 重置會話,取消當前傳輸,服務器響應 250 OK
QUIT 結束會話

常見SMTP服務器響應:

500 語法錯誤,未知命令
501 參數語法錯誤
502 命令未執行
503 命令順序錯誤
504 參數未賦值
211 系統狀態,或者系統幫助響應
214 幫助信息
220 <domain> 服務就緒
221 <domain> 服務正在關閉傳輸通道
421 <domain> 服務不可用,正在關閉傳輸通道
250 操作完成
251 非本地用戶;將轉發至 <forward-path>
450 操作未完成:郵箱不可用[例如:郵箱忙]
550 操作未完成:郵箱不可用[例如:郵箱不存在,不可訪問]
451 操作取消:處理過程中出錯
551 非本地用戶;請嘗試 <forward-path>
452 操作未完成:系統存儲空間不足
552 操作取消:超過分配的存儲空間
553 操作未完成:郵箱名不可用[例如:郵箱名語法錯誤]
354 開始郵件數據輸入,以 <CRLF>.<CRLF> 結束
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

同時設定一個內部類Message作爲數據載體,定義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\n");
                    CheckErrorCode(ReadStream(), "250");
                    WriteStream("AUTH LOGIN\r\n");
                    if (CheckReplyCode(ReadStream(), "334"))
                    {
                        WriteStream(ConvertToBase64(host.Username) + "\r\n");
                        CheckErrorCode(ReadStream(), "334");
                        WriteStream(ConvertToBase64(host.Password) + "\r\n");
                        CheckErrorCode(ReadStream(), "235");
                    }
                }
                else
                {
                    WriteStream("HELO " + Dns.GetHostName() + "\r\n");
                    CheckErrorCode(ReadStream(), "250");
                }
            }
        }

我們使用WriteStream()方法發送命令和數據,ReadStream()方法獲得服務器反饋,CheckErrorCode()和CheckReplyCode()方法判斷反饋的信息不是異常,以確保進行下一步。

由於TcpClient發送的數據是有限制的,因而當發送較長數據時最好將數據分幾次發送。其實這樣依然會帶來問題,由於我們採用同步寫入數據流的方式,大數據如附件的發送常常會因網絡傳輸或服務器交互問題造成異常,因而在LumiSoft項目採用的是異步方式,這裏我們全當測試,測試時使用較小的附件以避免這樣的問題。

        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-printablebase64

“Base64編碼是將輸入的數據全部轉換成由64個指定ASCII字符組成的字符序列,這64個字符由{'A'-'Z', 'a'-'z', '0'-'9', '+', '/'}構成。編碼時將需要轉換的數據每次取出6bit,然後將其轉換成十進制數字,這個數字的範圍最小爲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字段值來定義這個類型的傳輸編碼。而在標題和文件名等本身就是字段的,設定其值的傳輸編碼需要一種特殊方式,即ConvertHeaderToBase64()所要做的事。

這樣郵件標題字段的值會被定義爲:=?{字符編碼}?{傳輸編碼}?{編碼後的字符串}?=。其中傳輸編碼使用簡稱,B代表base64,Q代表quoted-printable,所以一箇中文標題可能會定義成這樣:=?utf-8?B?5Y+R6YCBIG0yIHZpYSBoMiBVc2VUY3BDbGllbnQg?=

ConvertFileToBase64()方法:

ConvertFileToBase64()方法將附件和其他內嵌資源由二進制的形式轉換成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方法

這個方法創建郵件的內容,但不包括附件和內嵌資源,只是正文。可以看到它主要是簡單的創建郵件內容字符串數組,以便在Data命令後,逐行發送到服務器。

        public void CreateMail(ConfigMail mail)
        {
            Mail = new Message(mail.From, mail.To);
            Mail.Data.Add("From: <" + mail.From + ">\r\n");
            foreach (var to in mail.To)
            {
                Mail.Data.Add("To: <" + mail.From + ">\r\n");
            }
            Mail.Data.Add("Subject: " + ConvertHeaderToBase64(mail.Subject) + "\r\n");
            Mail.Data.Add("Date: " + DateTime.Now.ToUniversalTime().ToString("R") + "\r\n");
            Mail.Data.Add("MIME-Version: 1.0\r\n");

            Mail.Data.Add("Content-Type: text/html;\r\n");
            Mail.Data.Add(" charset=\"" + Charset.WebName + "\"\r\n");
            Mail.Data.Add("Content-Transfer-Encoding: " + ContentTransferEncoding + "\r\n");
            Mail.Data.Add("\r\n"); // It is important, otherwise the body may be missing.
            Mail.Data.Add(ConvertToBase64(mail.Body) + "\r\n");
        }

實現CreateMultiMail方法

這個方法創建郵件的內容,且包括附件和內嵌資源。郵件內容將被分爲各個部分,各個部分標明瞭Content-Type和Charset,同時也設置了Content-Transfer-Encoding。上面已經討論過Content-Transfer-Encoding,現在我們需要詳細瞭解Content-Type。

Content-Type字段定義了郵件內容各部分的類型和相關屬性。郵件內容中處於外圍的都是multipart類型,而multipart包含3個子類型:multipart/mixed, multipart/related, multipart/alternative。這3種multipart的子類型在郵件內容中呈現的是一種嵌套關係:

multipart/mixed
multipart/related
multipart/alternative
text/plain
純文本正文
text/html
超文本正文
內嵌資源
附件

如上圖,如果包含附件則在附件外圍聲明multipart/mixed,如果包含內嵌資源則在內嵌資源外圍聲明multipart/related,如果同時存在text/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\n");
            foreach (var to in mail.To)
            {
                Mail.Data.Add("To: <" + mail.From + ">\r\n");
            }
            Mail.Data.Add("Subject: " + ConvertHeaderToBase64(mail.Subject) + "\r\n");
            Mail.Data.Add("Date: " + DateTime.Now.ToUniversalTime().ToString("R") + "\r\n");
            Mail.Data.Add("MIME-Version: 1.0\r\n");

            var mixedBoundary = Guid.NewGuid().ToString().Replace("-", "_");
            if (mail.Attachments != null && mail.Attachments.Length > 0)
            {
                Mail.Data.Add("Content-Type: multipart/mixed;\r\n");
                Mail.Data.Add(" boundary=\"" + mixedBoundary + "\"\r\n");
                Mail.Data.Add("\r\n");
                Mail.Data.Add("This is a multi-part message in MIME format.\r\n");

                Mail.Data.Add("\r\n");
                Mail.Data.Add("--" + mixedBoundary + "\r\n");
            }

            var relatedBoundary = Guid.NewGuid().ToString().Replace("-", "_");
            if (mail.Resources != null && mail.Resources.Length > 0)
            {
                Mail.Data.Add("Content-Type: multipart/related;\r\n");
                Mail.Data.Add(" boundary=\"" + relatedBoundary + "\"\r\n");

                Mail.Data.Add("\r\n");
                Mail.Data.Add("--" + relatedBoundary + "\r\n");
            }

            var altBoundary = Guid.NewGuid().ToString().Replace("-", "_");
            Mail.Data.Add("Content-Type: multipart/alternative;\r\n");
            Mail.Data.Add(" boundary=\"" + altBoundary + "\"\r\n");

            Mail.Data.Add("\r\n");
            Mail.Data.Add("--" + altBoundary + "\r\n");
            Mail.Data.Add("Content-Type: text/plain;\r\n");
            Mail.Data.Add(" charset=\"" + Charset.WebName + "\"\r\n");
            Mail.Data.Add("Content-Transfer-Encoding: " + ContentTransferEncoding + "\r\n");

            Mail.Data.Add("\r\n");
            Mail.Data.Add(ConvertToBase64("If you see this message, it means that your mail client does not support html.") + "\r\n");

            Mail.Data.Add("\r\n");
            Mail.Data.Add("--" + altBoundary + "\r\n");
            Mail.Data.Add("Content-Type: text/html;\r\n");
            Mail.Data.Add(" charset=\"" + Charset.WebName + "\"\r\n");
            Mail.Data.Add("Content-Transfer-Encoding: " + ContentTransferEncoding + "\r\n");
            Mail.Data.Add("\r\n");
            Mail.Data.Add(ConvertToBase64(mail.Body) + "\r\n");

            Mail.Data.Add("\r\n");
            Mail.Data.Add("--" + altBoundary + "--\r\n");

            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\n");
                        Mail.Data.Add("\r\n");
                        Mail.Data.Add("--" + relatedBoundary + "\r\n");
                        Mail.Data.Add("Content-ID: <" + ConvertToBase64(fileInfo.Name) + ">\r\n");
                        Mail.Data.Add("Content-Type: " + GetMimeType(fileInfo.Extension) + ";\r\n");
                        Mail.Data.Add(" name=\"" + ConvertHeaderToBase64(fileInfo.Name) + "\"\r\n");
                        Mail.Data.Add("Content-Transfer-Encoding: " + ContentTransferEncoding + "\r\n");
                        Mail.Data.Add("Content-Disposition: attachment;\r\n");
                        Mail.Data.Add(" filename=\"" + ConvertHeaderToBase64(fileInfo.Name) + "\"\r\n");

                        Mail.Data.Add("\r\n");
                        var fileStr = ConvertFileToBase64(resource);
                        Mail.Data.Add(fileStr + "\r\n");
                    }
                }
                Mail.Data.Add("\r\n\r\n--" + relatedBoundary + "--\r\n");
            }


            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\n");
                        Mail.Data.Add("\r\n");
                        Mail.Data.Add("--" + mixedBoundary + "\r\n");
                        Mail.Data.Add("Content-Type: " + GetMimeType(fileInfo.Extension) + ";\r\n");
                        Mail.Data.Add(" name=\"" + ConvertHeaderToBase64(fileInfo.Name) + "\"\r\n");
                        Mail.Data.Add("Content-Transfer-Encoding: " + ContentTransferEncoding + "\r\n");
                        Mail.Data.Add("Content-Disposition: attachment;\r\n");
                        Mail.Data.Add(" filename=\"" + ConvertHeaderToBase64(fileInfo.Name) + "\"\r\n");

                        Mail.Data.Add("\r\n");
                        var fileStr = ConvertFileToBase64(attachment);
                        Mail.Data.Add(fileStr + "\r\n");
                    }
                }

                Mail.Data.Add("\r\n");
                Mail.Data.Add("\r\n");
                Mail.Data.Add("--" + mixedBoundary + "--\r\n");
            }
        }


實現SendMail方法

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

                foreach (var item in Mail.Data)
                {
                    WriteStream(item);
                }
                WriteStream("\r\n.\r\n");
                CheckErrorCode(ReadStream(), "250");

                WriteStream("QUIT\r\n");
                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();

        }
    }

測試過程中使用較小的附件或圖片可以發送成功且一切正常,但大附件一般是失敗的,因而代碼是存在缺陷的,其原因可能是在複雜的網絡環境下使用同步發送出現異常或服務器失去響應,也可能是對數據流的操作不夠謹慎,或者兼而有之,這有待進一步深入研究。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章