C#基于SMTP协议和SOCKET通信,实现邮件内容和附件的发送,并可隐藏收件人

经过几天的努力,从完全不懂SMTP到折腾出个可以发送邮件内容和附件的DEMO。话少说,直接上代码。


 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.IO;

namespace UtilSp.ClassLib
{
    public class SmtpSp
    {
        #region Member

        #region boundary normal Property
        private string boundary_ = Guid.NewGuid().ToString();//Generate unique boundary.
        public string boundary_pro
        {
            get
            {
                return boundary_;
            }
            set
            {
                boundary_ = value;
            }
        }
        #endregion

        #region exception  Property
        private Exception exception_ = null;
        public Exception exception_pro
        {
            get
            {
                return exception_;
            }
            set
            {
                exception_ = value;
            }
        }
        #endregion

        #region encoding_smtp normal Property
        private Encoding encoding_smtp_ = Encoding.ASCII;//smtp encoding must be ascii.
        private Encoding encoding_smtp_pro
        {
            get
            {
                return encoding_smtp_;
            }
        }
        #endregion

        #region encoding_subject normal Property
        private Encoding encoding_subject_ = Encoding.UTF8;
        public Encoding encoding_subject_pro
        {
            get
            {
                return encoding_subject_;
            }
            set
            {
                encoding_subject_ = value;
            }
        }
        #endregion

        #region encoding_content normal Property
        private Encoding encoding_content_ = Encoding.GetEncoding("gb2312");
        public Encoding encoding_content_pro
        {
            get
            {
                return encoding_content_;
            }
            set
            {
                encoding_content_ = value;
            }
        }
        #endregion

        #region receiveInfo normal Property
        private string receiveInfo_ = "";
        public string receiveInfo_pro
        {
            get
            {
                return receiveInfo_;
            }
            set
            {
                receiveInfo_ = value;
            }
        }
        #endregion

        #region socket normal Property
        private Socket socket_;
        public Socket socket_pro
        {
            get
            {
                return socket_;
            }
            private set
            {
                socket_ = value;
            }
        }
        #endregion

        #endregion

        private sealed class SMTP_BACKS
        {
            public const string SERVER_READY_OK = "220";
            public const string OPERATE_COMPLETE = "250";
            public const string AUTH_LOGIN_OK = "334";
            public const string USER_NAME_LOGIN_OK = "334";
            public const string PASSWORD_LOGIN_OK = "235";
            public const string DATA_OK = "354";
        }

        private sealed class SMTP_CMD
        {
            public const string AUTH_LOGIN = "auth login\r\n";
            public const string MAIL_FROM = "mail from:";
            public const string RCPT_TO = "rcpt to:";
            public const string SEND_BODY = "data\r\n";
            public const string VERIFY = "ehlo hello\r\n";
        }

        public sealed class MailInfo
        {
            #region attachments normal Property
            private List<string> attachments_ = new List<string>();
            public List<string> attachments_pro
            {
                get
                {
                    return attachments_;
                }
                set
                {
                    attachments_ = value;
                }
            }
            #endregion

            #region content normal Property
            private string content_ = "";
            public string content_pro
            {
                get
                {
                    return content_;
                }
                set
                {
                    content_ = value;
                }
            }
            #endregion

            #region password normal Property
            private string password_ = "";
            public string password_pro
            {
                get
                {
                    return password_;
                }
                set
                {
                    password_ = value;
                }
            }
            #endregion

            #region receiverAddresses normal Property
            private List<string> receiverAddresses_ = new List<string>();
            public List<string> receiverAddresses_pro
            {
                get
                {
                    return receiverAddresses_;
                }
                set
                {
                    receiverAddresses_ = value;
                }
            }
            #endregion

            #region senderAddress normal Property
            private string senderAddress_ = "";
            public string senderAddress_pro
            {
                get
                {
                    return senderAddress_;
                }
                set
                {
                    senderAddress_ = value;
                }
            }
            #endregion

            #region subject normal Property
            private string subject_ = "";
            public string subject_pro
            {
                get
                {
                    return subject_;
                }
                set
                {
                    subject_ = value;
                }
            }
            #endregion

            #region userName normal Property
            private string userName_ = "";
            public string userName_pro
            {
                get
                {
                    return userName_;
                }
                set
                {
                    userName_ = value;
                }
            }
            #endregion
        }

        #region send Function
        public bool send(string mailHostIP, int port, MailInfo mailInfo)
        {
            try
            {
                exception_pro = null;
                if (!isLegal(mailHostIP, port, mailInfo))
                {
                    return false;
                }
                socket_pro = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                socket_pro.Connect(mailHostIP, port);
                if (!socket_pro.Connected)
                {
                    socket_pro.Close();
                    exception_pro = new Exception(Tip.ConnectFail);
                    return false;
                }

                if (!isServerReady())
                {
                    exception_pro = new Exception(Tip.ServerReadyFail + analyReceiveError());
                    socket_pro.Close();
                    return false;
                }

                if (!verify())
                {
                    exception_pro = new Exception(Tip.VerifyFail + analyReceiveError());
                    socket_pro.Close();
                    return false;
                }

                if (!authLogin())
                {
                    exception_pro = new Exception(Tip.AuthLoginFail + analyReceiveError());
                    socket_pro.Close();
                    return false;
                }

                if (!userNameLogin(mailInfo.userName_pro))
                {
                    exception_pro = new Exception(Tip.UserNameLoginFail + analyReceiveError());
                    socket_pro.Close();
                    return false;
                }

                if (!passowrdLogin(mailInfo.password_pro))
                {
                    exception_pro = new Exception(Tip.PasswordLoginFail + analyReceiveError());
                    socket_pro.Close();
                    return false;
                }

                if (!mailFrom(mailInfo.senderAddress_pro))
                {
                    exception_pro = new Exception(Tip.MailFromFail + analyReceiveError());
                    socket_pro.Close();
                    return false;
                }

                if (!rcptTo(mailInfo.receiverAddresses_pro))
                {
                    exception_pro = new Exception(Tip.RcptToFail + analyReceiveError());
                    socket_pro.Close();
                    return false;
                }

                if (!sendMail(mailInfo))
                {
                    exception_pro = new Exception(Tip.SendMailFail + analyReceiveError());
                    socket_pro.Close();
                    return false;
                }

                socket_pro.Close();
                return true;
            }
            catch (System.Exception ex)
            {
                exception_pro = ex;
                return false;
            }
        }

        private string analyReceiveError()
        {
            if (string.IsNullOrEmpty(receiveInfo_pro))
            {
                return Tip.ReceiveInfoEmpty;
            }
            return receiveInfo_pro;
        }

        #endregion

        private int socketSend(string sendStr)
        {
            byte[] sendBuffer = encoding_smtp_pro.GetBytes(sendStr);
            return socket_pro.Send(sendBuffer);
        }
        private string receive()
        {
            byte[] receiveData = new byte[10240];
            int receiveLen = socket_pro.Receive(receiveData);
            receiveInfo_pro = encoding_smtp_pro.GetString(receiveData, 0, receiveLen);
            Console.WriteLine(receiveInfo_pro);
            return receiveInfo_pro;
        }

        #region send ready functions

        #region isLegal Function
        public bool isLegal(string mailHostIP, int port, MailInfo mailInfo)
        {
            if (!IpPortSp.isPort(port.ToString()))
            {
                exception_pro = new Exception(Tip.PortIllegal);
                return false;
            }
            if (!MailSp.isMail(mailInfo.senderAddress_pro))
            {
                exception_pro = new Exception(Tip.SenderIllegal);
                return false;
            }
            return true;
        }
        #endregion

        #region isServerReady Function
        public bool isServerReady()
        {
            string back = receive();
            return back.Substring(0, 3).Equals(SMTP_BACKS.SERVER_READY_OK);//Response code from server always is 3 byte length.
        }
        #endregion

        #region verify Function
        public bool verify()
        {
            socketSend(SMTP_CMD.VERIFY);
            string[] verifyBacks = receive().Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
            for (int index = 0; index < verifyBacks.Length; index++)
            {
                string verifyBack = verifyBacks[index];
                if (verifyBack.Length <= 3)
                {
                    return false;
                }

                if (!verifyBack.Substring(0, 3).Equals(SMTP_BACKS.OPERATE_COMPLETE))
                {
                    return false;
                }
            }
            return true;
        }
        #endregion

        #region authLogin function
        private bool authLogin()
        {
            socketSend(SMTP_CMD.AUTH_LOGIN);
            string authLoginBack = receive();
            if (string.IsNullOrEmpty(authLoginBack) || authLoginBack.Length <= 3)
            {
                return false;
            }
            return authLoginBack.Substring(0, 3).Equals(SMTP_BACKS.AUTH_LOGIN_OK);
        }
        #endregion

        #region userNameLogin function
        private bool userNameLogin(string userName)
        {
            if (string.IsNullOrEmpty(userName))
            {
                return false;
            }
            string base64UserName = Base64Sp.tobase64Str(userName);
            socketSend(base64UserName + "\r\n");
            string userNameLoginBack = receive();
            if (string.IsNullOrEmpty(userNameLoginBack) || userNameLoginBack.Length <= 3)
            {
                return false;
            }
            return userNameLoginBack.Substring(0, 3).Equals(SMTP_BACKS.USER_NAME_LOGIN_OK);
        }


        #endregion

        #region passowrdLogin function
        private bool passowrdLogin(string password)
        {
            if (string.IsNullOrEmpty(password))
            {
                return false;
            }
            socketSend(Base64Sp.tobase64Str(password) + "\r\n");
            string passowrdLoginBack = receive();
            if (string.IsNullOrEmpty(passowrdLoginBack) || passowrdLoginBack.Length <= 3)
            {
                return false;
            }
            return passowrdLoginBack.Substring(0, 3).Equals(SMTP_BACKS.PASSWORD_LOGIN_OK);
        }


        #endregion

        #region mailFrom function
        private bool mailFrom(string senderAddress)
        {
            socketSend(SMTP_CMD.MAIL_FROM + "<" + senderAddress + ">\r\n");
            string mailFromBack = receive();
            if (string.IsNullOrEmpty(mailFromBack) || mailFromBack.Length <= 3)
            {
                return false;
            }
            return mailFromBack.Substring(0, 3).Equals(SMTP_BACKS.OPERATE_COMPLETE);
        }
        #endregion

        #region rcptTo function
        private bool rcptTo(List<string> receiverAddresses)
        {
            foreach (string receiverAddress in receiverAddresses)
            {
                socketSend(SMTP_CMD.RCPT_TO + "<" + receiverAddress + ">\r\n");
                string rcptBack = receive();
                if (string.IsNullOrEmpty(rcptBack) || rcptBack.Length <= 3)
                {
                    return false;
                }

                if (!rcptBack.Substring(0, 3).Equals(SMTP_BACKS.OPERATE_COMPLETE))
                {
                    return false;
                }
            }
            return true;
        }
        #endregion

        #endregion

        #region send mail body functions
        private bool sendMail(MailInfo mailInfo)
        {
            socketSend(SMTP_CMD.SEND_BODY);
            string back = receive();
            if (string.IsNullOrEmpty(back) || back.Length <= 3)
            {
                return false;
            }

            if (!back.Substring(0, 3).Equals(SMTP_BACKS.DATA_OK))
            {
                return false;
            }

            sendMailHeader(mailInfo);
            sendMailContent(mailInfo.content_pro);
            sendMailAttachment(mailInfo.attachments_pro);
            sendMailTail();

            back = receive();
            if (string.IsNullOrEmpty(back) || back.Length <= 3)
            {
                return false;
            }

            if (!back.Substring(0, 3).Equals(SMTP_BACKS.OPERATE_COMPLETE))
            {
                return false;
            }

            return true;
        }

        private void sendMailAttachment(List<string> attachments)
        {
            foreach (string attachment in attachments)//Add header per attachment.
            {
                if (!File.Exists(attachment))
                {
                    continue;
                }
                sendAttachmentHeader(attachment);
                sendAttachmentData(attachment);
            }
        }

        private void sendAttachmentData(string attachment)
        {
            byte[] attachmentData = FileSp.readFileBytes(attachment);
            socketSend(Convert.ToBase64String(attachmentData));
            socketSend("\r\n\r\n");
        }

        private void sendAttachmentHeader(string attachment)
        {
            string contentType = "application/octet-stream";//Easy to transmission more format file;
            string header = "--" + boundary_pro + "\r\n"
                    + "Content-Type:"+contentType+";name=" + attachment + "\r\n"
                    + "Content-Transfer-Encoding:base64\r\n"
                    + "Content-Disposition:attachment;filename=\"" + attachment + "\"\r\n";
            socketSend(header + "\r\n");
        }

        private void sendMailTail()
        {
            socketSend("\r\n.\r\n");//. is content end flag.        
        }

        private void sendMailHeader(MailInfo mailInfo)
        {
            string to = "";
            foreach (string receiverAddress in mailInfo.receiverAddresses_pro)
            {
                to += "To:" + receiverAddress + "\r\n";
            }

            //if you want to hide receiver,may assign to="To:abc@a.com\r\n";
            //string to = "To:abc@a.com\r\n";
            socketSend(to);


            string from = "From:" + mailInfo.senderAddress_pro + "\r\n";
            socketSend(from);

           //Avoid subject messy code,adopt encoding header statement.
            string subject = "Subject:=?" + encoding_subject_pro.BodyName + "?B?"
                            + Base64Sp.tobase64Str(mailInfo.subject_pro, Encoding.UTF8) + "?=\r\n";
            socketSend(subject);
            string header = "Mime-Version:1.0\r\n"
                      + "Content-type:multipart/mixed;"
                      + "boundary=\"" + boundary_pro + "\";\r\n"//define content type and boundary value.
                      + "Content-Transfer-Encoding:7bit\r\n"
                      + "This is a multi-part message in MIME format\r\n";
            socketSend(header + "\r\n");
        }

        private void sendMailContent(string content)
        {
            sendContentHeader();
            sendContentBody(content);
        }

        private void sendContentBody(string content)
        {
            //If content is too long,need split some blocks and send.
            //Length of one line is always 80 byte.
            string newContent = Base64Sp.tobase64Str(content, encoding_content_pro);
            socketSend(newContent + "\r\n");
        }

        private void sendContentHeader()
        {
            string header = "--" + boundary_pro + "\r\n"//boundary must begin --
                 + "Content-type:text/plain;charset=" + encoding_content_pro.BodyName + "\r\n"//Avoid messy code,add charset statement.
                 + "Content-Transfer-Encoding:base64\r\n"//Avoid messy code,use encoding base64.
            socketSend(header + "\r\n");
        }

        #endregion


        private class Tip//Easy to change other language tip.
        {
            public static string AuthLoginFail = "Auth login fail!";
            public static string ConnectFail = "Connect mail host fail!";
            public static string IpIllegal = "Ip is illegal!";
            public static string MailFromFail = "Input sender address fail!";
            public static string PasswordLoginFail = "Password login fail!";
            public static string PortIllegal = "Port is illegal!";
            public static string ReceiveInfoEmpty = "Receive info is empty!";
            public static string RcptToFail = "Input receiver address fail!";
            public static string SenderIllegal = "Sender mail address is illegal!";
            public static string SendMailFail = "Send mail fail!";
            public static string ServerReadyFail = "Server ready fail!";
            public static string UserNameLoginFail = "User name login fail!";
            public static string VerifyFail = "Verify identity fail!";
        }
    }
}


具体工程可以到 http://download.csdn.net/detail/xxdddail/5802901下载

 

 

posted @ 2013-07-24 20:01  爱生活,爱编程  阅读(1508)  评论(0编辑  收藏  举报