NET中动用SMTP发送邮件,Java发送邮件
分类:计算机编程

SMTP协议

定义

SMTP 是一种TCP协议支持的提供可靠且有效电子邮件传输的应用层协议。SMTP 是建立在 TCP上的一种邮件服务,主要用于传输系统之间的邮件信息并提供来信有关的通知,使用25端口。

忘记是从哪里看来的了。。。

这是一篇转载,可能对大家很有用啊,放首页看看是否有参考价值。本文提到的方案仍然不能算是完全解决所有问题,最佳的dotNET下通过SMTP(带验证)发送邮件的机制是什么,不知道大家有什么好的看法! 

当我们使用PHP的第三方库或工具类进行邮件发送的时候,是否想过一个问题:

过程

(1)建立TCP 连接。
(2)客户端发送HELO命令以标识发件服务器域名,服务器端以OK作为响应,表示准备接收。(HELO命令现在比较少用,一般都使用EHLO命令,邮件服务器收到EHLO命令以后,不仅会返回250状态码,还会返回自己支持的各种扩展的列表。)
(3)客户端发送AUTH LOGIN请求认证,输入base64编码了的用户名和密码
(4)客户端发送MAIL和RCPT命令,以标识该电子邮件的计划接收人和发送者,可以有多个RCPT行;服务器端则表示是否愿意为收件人接收邮件。
(5)协商结束,发送邮件,用命令DATA发送。
(6)以“.”号表示结束输入内容一起发送出去,结束此次发送,用QUIT命令退出。

网上一大堆

摘要

为什么我们不能自己写php代码实现邮件发现,而要用别人的库呢?php发送邮件到底是如何实现的?

使用telnet连接演示

C: telent SMTP.163.com 25  //以telenet方式连接163邮件服务器  
S: 220 163.com Anti-spam GT for Coremail System //220为响应数字,其后的为欢迎信息  
C: HELO SMTP.163.com //跟服务器打招呼
S: 250-mail  
S: 250-AUTH LOGIN PLAIN  
S: 250-AUTH=LOGIN PLAIN  
S: 250 8BITMIME //最后一个响应数字应答码之后跟的是一个空格,而不是'-'   
C: AUTH LOGIN   //请求认证  
S: 334 dxNlcm5hbWU6  //服务器的响应——经过base64编码了的“Username”=  
C: Y29zdGFAYW1heGl0Lm5ldA==  //发送经过BASE64编码了的用户名  
S: 334 UGFzc3dvcmQ6  //经过BASE64编码了的"Password:"=  
C: MTk4MjIxNA==  //客户端发送的经过BASE64编码了的密码  
S: 235 auth successfully  //认证成功   
C: MAIL FROM: bripengandre@163.com  //发送者邮箱  
S: 250 … .  //“…”代表省略了一些可读信息  
C: RCPT TO: bripengandre@smail.hust.edu.cn //接收者邮箱  
S: 250 … .    // “…”代表省略了一些可读信息  
C: DATA //请求发送数据  
S: 354 Enter mail, end with "." on a line by itself  
C: Enjoy Protocol Studing  
C: .  
S: 250 Message sent  
C: QUIT //退出连接   
S: 221 Bye

Java发送邮件有2大类

本文简单介绍了SMTP协议(RFC2554)发送邮件的过程,并讨论了在 .NET 中使用SMTP发送邮件由简到繁的三种不同方案、各自可能遇到的问题及其解决办法。

首先我们要了解发送邮件的基本原理,本文基于SMTP协议实现邮件发送

常用命令

HELO <domain> <CRLF>。向服务器标识用户身份。发送者能欺骗,说谎,但一般情况下服务器都能检测到,后用来填写返回域名(具体含义请参阅RFC821),但该命令并不检查后面的参数,用户可以随意填写

MAIL FROM: <reverse-path> <CRLF>。<reverse-path>为发送者地址,此命令用来初始化邮件传输,即用来对所有的状态和缓冲区进行初始化。

RCPT TO:<forward-path> <CRLF>。 <forward-path>用来标志邮件接收者的地址,常用在MAIL FROM后,可以有多个RCPT TO。

DATA <CRLF>。将之后的数据作为数据发送,以<CRLF>.<CRLF>标志数据的结尾。

REST <CRLF>。重置会话,当前传输被取消。

NOOP <CRLF>。要求服务器返回OK应答,一般用作测试。

QUIT <CRLF>。结束会话。
  1. 用别人写的jar包来发送,比较简单安心,快速

  2. 自己更具SMTP协议来发送邮件,比较烦,但是更好玩


SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议。简单来说它定义了一组规则,我们只需要依照这个规则来告诉SMTP服务器,我们要发送邮件的发送人,接收人,内容,主题等信息。


目录

然后SMTP服务器依照这组规则来解析我们发送的信息,最后进行邮件发送。像163,qq等邮件服务器都有提供SMTP服务,我们只要连接上他们的SMTP服务器,然后write数据,就能实现邮件发送了。

  1. 使用javax.mail来发送代码:
  • 简介
  • .NET的SMTP类
  • 使用CDO组件发送邮件
  • 使用Socket撰写邮件发送程序
  • 总结
  • 更多的信息

其实我们可以不写代码,直接借用Linux的telnet工具来连接smtp服务,进行邮件发送。借此来了解邮件发送的整个流程。

MailSenderInfo(发送邮件需要使用的基本信息)


telnet进行邮件发送

package sendJavaMail;

import java.util.Properties;

/**
 * 发送邮件需要使用的基本信息
 */
public class MailSenderInfo {
    // 发送邮件的服务器的IP和端口
    private String mailServerHost;
    private String mailServerPort = "25";
    // 邮件发送者的地址
    private String fromAddress;
    // 邮件接收者的地址
    private String toAddress;
    // 登陆邮件发送服务器的用户名和密码
    private String userName;
    private String password;
    // 是否需要身份验证
    private boolean validate = false;
    // 邮件主题
    private String subject;
    // 邮件的文本内容
    private String content;
    // 邮件附件的文件名
    private String[] attachFileNames;

    /**
     * 获得邮件会话属性
     */
    public Properties getProperties() {
        Properties p = new Properties();
        p.put("mail.smtp.host", this.mailServerHost);
        p.put("mail.smtp.port", this.mailServerPort);
        p.put("mail.smtp.auth", validate ? "true" : "false");
        return p;
    }

    public String getMailServerHost() {
        return mailServerHost;
    }

    public void setMailServerHost(String mailServerHost) {
        this.mailServerHost = mailServerHost;
    }

    public String getMailServerPort() {
        return mailServerPort;
    }

    public void setMailServerPort(String mailServerPort) {
        this.mailServerPort = mailServerPort;
    }

    public boolean isValidate() {
        return validate;
    }

    public void setValidate(boolean validate) {
        this.validate = validate;
    }

    public String[] getAttachFileNames() {
        return attachFileNames;
    }

    public void setAttachFileNames(String[] fileNames) {
        this.attachFileNames = fileNames;
    }

    public String getFromAddress() {
        return fromAddress;
    }

    public void setFromAddress(String fromAddress) {
        this.fromAddress = fromAddress;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getToAddress() {
        return toAddress;
    }

    public void setToAddress(String toAddress) {
        this.toAddress = toAddress;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String textContent) {
        this.content = textContent;
    }
}

简介

我们可以在linux环境下,使用telnet命令,连接163的smtp服务,25端口,借此来理解smtp的传输流程。

SimpleMailSender(简单邮件(不带附件的邮件)发送器)

邮件发送功能常常是许多.NET应用,尤其是带网络功能的应用中不可缺少的模块之一,本文就此介绍了使用.NET的SMTP类库和另两种分别通过CDO(Collaboration Data Objects)及Socket来实现发送邮件功能的方法。

telnet smtp.163.com 25 
package sendJavaMail;

import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.Properties;

import javax.mail.Address;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

/**
 * 简单邮件(不带附件的邮件)发送器
 */
public class SimpleMailSender {
    /**
     * 以文本格式发送邮件
     * 
     * @param mailInfo
     *            待发送的邮件的信息
     */
    public boolean sendTextMail(MailSenderInfo mailInfo) {
        // 判断是否需要身份认证
        MyAuthenticator authenticator = null;
        Properties pro = mailInfo.getProperties();
        if (mailInfo.isValidate()) {
            // 如果需要身份认证,则创建一个密码验证器
            authenticator = new MyAuthenticator(mailInfo.getUserName(),
                    mailInfo.getPassword());
        }
        // 根据邮件会话属性和密码验证器构造一个发送邮件的session
        Session sendMailSession = Session
                .getDefaultInstance(pro, authenticator);
        try {
            // 根据session创建一个邮件消息
            Message mailMessage = new MimeMessage(sendMailSession);
            // 创建邮件发送者地址
            Address from;
            try {
                // 设置自定义发件人昵称
                String nick = javax.mail.internet.MimeUtility
                        .encodeText("昵称wxsTest");
                from = new InternetAddress(mailInfo.getFromAddress(), nick);
            } catch (UnsupportedEncodingException e) {
                from = new InternetAddress(mailInfo.getFromAddress());
            }
            // 设置邮件消息的发送者
            mailMessage.setFrom(from);
            // 创建邮件的接收者地址,并设置到邮件消息中
            Address to = new InternetAddress(mailInfo.getToAddress());
            // mailMessage.setRecipient(Message.RecipientType.TO, to); //单人
            mailMessage.setRecipients(Message.RecipientType.TO, new Address[] {
                    to, from }); // 多人
            // cc设置抄送人
            mailMessage.setRecipients(Message.RecipientType.CC,
                    new Address[] { to });
            // bcc设置暗抄送人
            mailMessage.setRecipients(Message.RecipientType.BCC,
                    new Address[] { from });
            // 设置邮件消息的主题
            mailMessage.setSubject(mailInfo.getSubject());
            // 设置邮件消息发送的时间
            mailMessage.setSentDate(new Date());
            // 设置邮件消息的主要内容
            String mailContent = mailInfo.getContent();
            mailMessage.setText(mailContent);
            // 发送邮件
            Transport.send(mailMessage);
            return true;
        } catch (MessagingException ex) {
            ex.printStackTrace();
        }
        return false;
    }

    /**
     * 以HTML格式发送邮件
     * 
     * @param mailInfo
     *            待发送的邮件信息
     */
    public static boolean sendHtmlMail(MailSenderInfo mailInfo) {
        // 判断是否需要身份认证
        MyAuthenticator authenticator = null;
        Properties pro = mailInfo.getProperties();
        // 如果需要身份认证,则创建一个密码验证器
        if (mailInfo.isValidate()) {
            authenticator = new MyAuthenticator(mailInfo.getUserName(),
                    mailInfo.getPassword());
        }
        // 根据邮件会话属性和密码验证器构造一个发送邮件的session
        Session sendMailSession = Session
                .getDefaultInstance(pro, authenticator);
        try {
            // 根据session创建一个邮件消息
            Message mailMessage = new MimeMessage(sendMailSession);
            // 创建邮件发送者地址
            Address from;
            try {
                // 设置自定义发件人昵称
                String nick = javax.mail.internet.MimeUtility
                        .encodeText("昵称wxsTest");
                from = new InternetAddress(mailInfo.getFromAddress(), nick);
            } catch (UnsupportedEncodingException e) {
                from = new InternetAddress(mailInfo.getFromAddress());
            }
            // 设置邮件消息的发送者
            mailMessage.setFrom(from);
            // 创建邮件的接收者地址,并设置到邮件消息中
            Address to = new InternetAddress(mailInfo.getToAddress());
            // Message.RecipientType.TO属性表示接收者的类型为TO
            // mailMessage.setRecipient(Message.RecipientType.TO, to); //单人
            mailMessage.setRecipients(Message.RecipientType.TO, new Address[] {
                    to, from }); // 多人
            // cc设置抄送人
            mailMessage.setRecipients(Message.RecipientType.CC,
                    new Address[] { to });
            // bcc设置暗抄送人
            mailMessage.setRecipients(Message.RecipientType.BCC,
                    new Address[] { from });
            // 设置邮件消息的主题
            mailMessage.setSubject(mailInfo.getSubject());
            // 设置邮件消息发送的时间
            mailMessage.setSentDate(new Date());
            // MiniMultipart类是一个容器类,包含MimeBodyPart类型的对象
            Multipart mainPart = new MimeMultipart();
            // 创建一个包含HTML内容的MimeBodyPart
            BodyPart html = new MimeBodyPart();
            // 设置HTML内容
            html.setContent(mailInfo.getContent(), "text/html; charset=utf-8");
            mainPart.addBodyPart(html);
            // 将MiniMultipart对象设置为邮件内容
            mailMessage.setContent(mainPart);
            // 发送邮件
            Transport.send(mailMessage);
            return true;
        } catch (MessagingException ex) {
            ex.printStackTrace();
        }
        return false;
    }
}

然后会得到以下结果,说明我们连接成功了

MyAuthenticator(服务器认证)

.NET的SMTP类

Trying 220.181.12.16...Connected to smtp.163.com.Escape character is '^]'.220 163.com Anti-spam GT for Coremail System 
package sendJavaMail;

import javax.mail.*;

public class MyAuthenticator extends Authenticator {
    String userName = null;
    String password = null;

    public MyAuthenticator() {
    }

    public MyAuthenticator(String username, String password) {
        this.userName = username;
        this.password = password;
    }

    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication(userName, password);
    }
}

首先,我们来介绍一下.NET类库种自带的SMTP类。在.NET中的System.Web.Mail名字空间下,有一个专门使用SMTP协议来发送邮件的类:SmtpMail,它已能满足最普通的发送邮件的需求。这个类只有一个自己的公共函数--Send()和一个公共属性—SmtpServer,如下图:

接着我们执行以下命令,告诉对方我们的身份标识来自哪里

Main方法

图片 1

HELO smtp.163.com
    public static void main(String[] args) {
        // 这个类主要是设置邮件
        MailSenderInfo mailInfo = new MailSenderInfo();
        mailInfo.setMailServerHost("smtp.server.com"); //smtp服务器地址
        mailInfo.setMailServerPort("25"); //smtp服务器端口号,一般是25
        mailInfo.setValidate(true);
        mailInfo.setUserName("username"); // 自己登录用户名
        mailInfo.setPassword("password");// 自己登录密码

        mailInfo.setFromAddress("my@gmail.com"); // /自己邮箱地址
        mailInfo.setToAddress("you@gmail.com"); // 对方邮箱地址
        mailInfo.setSubject("Title,タイトル、测试");
        mailInfo.setContent("<font style='color:red;'><b>Test,テスト内容、测试内容</b></font>");

        // 这个类主要来发送邮件
        SimpleMailSender sms = new SimpleMailSender();
        sms.sendTextMail(mailInfo);// 发送文体格式
        sms.sendHtmlMail(mailInfo); // 发送html格式

    }

您必须通过SmtpServer属性来指定发送邮件的服务器的名称(或IP地址),然后再调用

对方会返回给我们一个250 OK

 

Send()函数来发送邮件。

再执行AUTH LOGIN告诉对方我们要开始进行身份认证,然后对方会回应我们一些消息。

2. 根据SMTP协议发送邮件

代码示例如下:

后面我们会再输入我们的用户名,密码,发送邮件的内容,发送人,接受人等信息,然后结束对话,smtp服务器就会帮我们把邮件发送出去。

首先是SMTP协议简介:

(in C#)

由于smtp协议对邮件内容格式有严格的要求,在命令行中不好执行,所以这里没有将整个过程执行完毕,后面会使用php代码完整实现。

using System.Web.Mail;
public void sendMail()
{                  
      try
      {
            System.Web.Mail.MailMessage myMail=new MailMessage();
            myMail.From = "myaccount@test.com";
            myMail.To = "myaccount@test.com";
            myMail.Subject = "MailTest";
            myMail.Priority = MailPriority.Low;
            myMail.BodyFormat = MailFormat.Text;
            myMail.Body = "Test";
            SmtpMail.SmtpServer="smarthost"; //your smtp server here

            SmtpMail.Send(myMail);                        
      }                  
      catch(Exception e)
      {
            throw e;                              
      }
}

从上面使用telnet连接smtp邮件的过程可以看出来,发送邮件的过程其实很简单,就是连接smtp服务的25端口,依照协议告诉对方我们要发什么邮件即可。这与平台,与编程语言无关。

SMTP 命令简介

您可以在Send函数的参数MailMessage对象中设置邮件的相关属性,如优先级、附件等等。除了以 MailMessage对象为参数(如上述代码),Send函数还可以简单的直接以邮件的4个主要信息(from,to,subject, messageText)作为字符串参数来调用。

无论我们用C语言,还是Java或者PHP,只要使用Socket连接SMTP服务器,就能实现邮件发送。

什么是 SMTP
SMTP (Simple Mail Transfer Protocol) : 电子邮件从客户机传输到服务器或从某一个服务器传输到另一个服务器使用的传输协议。 SMTP 是请求/响应协议,命令和响应都是基于 ASCII 文本,并以 CR 和 LF 符结束。响应包括一个表示返回状态的三位数字代码。SMTP 在 TCP 协议 25 端口监听连接请求。


SMTP指令

什么是 ESMTP
ESMTP (Extended SMTP),顾名思义,扩展 SMTP 就是对标准 SMTP 协议进行的扩展。它与 SMTP 服务的区别仅仅是,使用 SMTP 发信不需要验证用户帐户,而用 ESMTP 发信时,服务器会要求用户提供用户名和密码以便验证身份。验证之后的邮件发送过程与 SMTP 方式没有两样。

使用CDO组件发送邮件

上面我们使用telnet连接smtp服务时,输入了一些HELO ,AUTH LOGIN等,大家可能会有疑问这些是什么。

SMTP 命令包括:
HELO 向服务器标识用户身份。发送者能欺骗,说谎,但一般情况下服务器都能检测到。
EHLO 向服务器标识用户身份。发送者能欺骗,说谎,但一般情况下服务器都能检测到。
MAIL FROM 命令中指定的地址是发件人地址
RCPT TO 标识单个的邮件接收人;可有多个 RCPT TO;常在 MAIL 命令后面。
DATA 在单个或多个 RCPT 命令后,表示所有的邮件接收人已标识,并初始化数据传输,以 CRLF.CRLF 结束
VRFY 用于验证指定的用户/邮箱是否存在;由于安全方面的原因,服务器常禁止此命令
EXPN 验证给定的邮箱列表是否存在,扩充邮箱列表,也常被禁用
HELP 查询服务器支持什么命令
NOOP 无操作,服务器应响应 OK
RSET 重置会话,当前传输被取消
QUIT 结束会话

CDO是Collaboration Data Objects的简称,它是一组高层的COM对象集合,并经历了好几个版本的演化,现在在Windows2000和Exchange2000中使用的都是 CDO2.0的版本(分别为cdosys.dll和cdoex.dll)。CDOSYS构建在SMTP协议和NNTP协议之上,并且作为 Windows2000 Server的组件被安装,您可以在系统目录(如c:winnt或c:windows)的system32子目录中找到它(cdosys.dll)。

其实很简单,这些就是SMTP协议定义的指令,或者说规则,smtp服务器就是通过这些指令才知道我们是想干啥。

连接到 Postfix 使用 SMTP 命令发送邮件
例如:安装 Postfix 的邮件服务器IP是192.168.0.1 (蓝色字体内容由客户端输入,红色字体内容是服务返回的)

CDO组件相对于先前介绍的SmtpMail对象功能更为丰富,并提供了一些SmtpMail类所没有提供的功能,如通过需要认证的SMTP服务器发送邮件等。

指令

telnet 192.168.0.1 25 ------------------------------------------------- 使用 telnet 命令连接服务器 25 端口
helo test.com -----------------------------------------------------------向服务器标识用户身份发送 mail from 命令
ehlo test.com ----------------------------------------------------------- ESMTP 命令,发信需要认证。
auth login ----------------------------------------------------------------进行用户身份认证
334 VXNlcm5hbWU6
Y29zdGFAYW1heGl0Lm5ldA== ----------------------------------- BASE64 加密后的用户名
334 UGFzc3dvcmQ6
MTk4MjIxNA== -------------------------------------------------------- BASE64 加密后的密码
235 authentication successfully -------------------------------- 身份认证成功
(535 authentication failed --------------------------------- ------身份认证失败)
发到本系统中域名下的账户可跳过身份认证。
mail from: <test1@domain.com> ---------------------------- mail from 地址 test1@domain.com
250 ok ----------------------------------------------------- ----------命令执行成功
rcpt to: <test2@domain.com> -------------------------------- 递送给地址 test2@domain.com
250 ok ----------------------------------------------------- ----------命令执行成功
data ------------------------------------------------------- -----------数据传输初始化
354 End data with .----------------------------------------- -----开始传输数据
From: test1@domain.com
To: test2@domain.com
Date: Mon, 25 Oct 2004 14:24:27 0800
Subject: test mail

下面一段代码就展示了如何使用CDO组件通过需要认证的SMTP服务器发送邮件的过程:

作用

Hi, test2
This is a test mail, you don't reply it.

(in C#)

HELO 向对方邮件服务器发出的标识自己的身份的命令 AUTH LOGIN 即将进行身份认证 MAIL FROM 告诉对方本次邮件发送人是谁 RCPT TO 发送给谁 DATA 告诉对方本次邮件,接下来我们发送邮件具体内容了 QUIT 邮件内容输入完毕后,执行该指令退出

.

数据内容,包括BASE64加密后的邮件内容, 以 CRLF.CRLF 结束数据传输
250 OK: queued as 2F6DE3929--------------------------------- 命令执行成功
quit ------------------------------------------------------- 结束会话
221 Bye
Connection closed by foreign host .------------------------- 断开连接

以上就是一个邮件发送的基本的命令。

再说一下邮件发送的基本过程:

如果你的邮件地址是a@host.com,而你要用这个邮箱发送一封邮件到to@tohost.com,你需要连接到服务器host.com上,当然这个连接可能需要认证,现在基本上都要验证,然后是发送邮件到服务器host.com上,关闭连接。在host.com上,你所发送的邮件进入发送队列中,轮到你要发送的邮件时,host.com主机再联系tohost.com,将邮件传输到服务器tohost.com上。

代码流程:
Main入口
1.set数据[发件人,发送人,用户名,密码,标题,内容,smtp服务地址,端口等]
2.socket建立连接[smtp服务地址,端口]
3.执行发送邮件命令:
3.1.通过socket建立BufferedReader,BufferedWriter
3.2.helo命令[判断服务器连接是否成功]
3.3.authLogin命令
3.3.1.发送命令:"AUTH LOGIN"
3.3.2.发送BASE64Encoder加密后 登录用户名
3.3.3.发送BASE64Encoder加密后 登录密码
3.4.mailfrom指定发件人邮箱
3.4.1.发送命令:"MAIL FROM:<" mail ">"
3.5.rcpt指定接受人邮箱
3.5.1.发送命令:"RCPT TO:<" mail ">"[发给多个人就多几次]
3.6.data传输数据
3.6.1.发送命令:"DATA"
3.6.2.发送命令:"From: " xxx[昵称]
3.6.3.发送命令:"To: " xxx[可以添加Cc:,Bcc:命令,但是地址必须在3.5.1中发送过RCPT TO:命令的; 多个的话,可以用"; "分割]
3.6.4.发送命令:"Subject: " xxx
3.6.5.发送命令:邮件内容
3.6.6.句号加回车结束邮件内容输入
3.7.退出smtp服务器
3.7.1.发送命令:"QUIT"

 

代码:

MailMessage(一些基本信息的设置)

package sendSMTPMail;

/**
 * 这个类其实就是一个基本的JavaBean, 用于完成一些基本信息的设置, 也可以不要这个东西 直接在程序中写明就可以, 不过这样条理较清楚一些,
 * 而且修改也方便一些.
 * 
 */
public class MailMessage {
    private String from;
    private String to;
    private String datafrom;
    private String datato;
    private String subject;
    private String content;
    private String date;
    private String user;
    private String password;

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getDatafrom() {
        return datafrom;
    }

    public void setDatafrom(String datafrom) {
        this.datafrom = datafrom;
    }

    public String getDatato() {
        return datato;
    }

    public void setDatato(String datato) {
        this.datato = datato;
    }

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    public String getFrom() {
        return from;
    }

    public void setFrom(String from) {
        this.from = from;
    }

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public String getTo() {
        return to;
    }

    public void setTo(String to) {
        this.to = to;
    }

}

SMTPClient(主要的功能)

package sendSMTPMail;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.StringTokenizer;
import java.util.TimeZone;

import sun.misc.BASE64Encoder;

/**
 * 
 * 主要的功能就在这里面完成了
 * 
 */
@SuppressWarnings("restriction")
public class SMTPClient {
    private boolean debug = true;
    BASE64Encoder encode = new BASE64Encoder();// 用于加密后发送用户名和密码

    public static void main(String[] args) throws UnknownHostException,
            IOException {
        MailMessage message = new MailMessage();
        message.setFrom("my@gmail.com");// 发件人
        message.setTo("you@gmail.com");// 收件人
        String server = "smtp.com";// 邮件服务器
        message.setSubject("test 测试 テスト");// 邮件主题
        message.setContent("<b>test  测试 テスト</b>");// 邮件内容
        message.setDatafrom("my@gmail.com");// 发件人,在邮件的发件人栏目中显示
        message.setDatato("you@gmail.com");// 收件人,在邮件的收件人栏目中显示
        message.setUser("username");// 登陆邮箱的用户名
        message.setPassword("password");// 登陆邮箱的密码

        SMTPClient smtp = new SMTPClient(server, 25);
        boolean flag;
        flag = smtp.sendMail(message, server);
        if (flag) {
            System.out.println("邮件发送成功!");
        } else {
            System.out.println("邮件发送失败!");
        }

    }

    private Socket socket;

    public SMTPClient(String server, int port) throws UnknownHostException,
            IOException {
        try {
            socket = new Socket(server, 587);// 一般是25
        } catch (SocketException e) {
            System.out.println(e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("已经建立连接!");
        }

    }

    // 注册到邮件服务器
    public void helo(String server, BufferedReader in, BufferedWriter out)
            throws IOException {
        int result;
        result = getResult(in);
        // 连接上邮件服务后,服务器给出220应答
        if (result != 220) {
            throw new IOException("连接服务器失败");
        }
        result = sendServer("HELO "   server, in, out);
        // HELO命令成功后返回250
        if (result != 250) {
            throw new IOException("注册邮件服务器失败!");
        }
    }

    private int sendServer(String str, BufferedReader in, BufferedWriter out)
            throws IOException {
        out.write(str);
        out.newLine();
        out.flush();
        if (debug) {
            System.out.println("已发送命令:"   str);
        }
        return getResult(in);
    }

    public int getResult(BufferedReader in) {
        String line = "";
        try {
            line = in.readLine();
            if (debug) {
                System.out.println("服务器返回状态:"   line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 从服务器返回消息中读出状态码,将其转换成整数返回
        StringTokenizer st = new StringTokenizer(line, " ");
        return Integer.parseInt(st.nextToken());
    }

    public void authLogin(MailMessage message, BufferedReader in,
            BufferedWriter out) throws IOException {
        int result;
        result = sendServer("AUTH LOGIN", in, out);
        if (result != 334) {
            throw new IOException("用户验证失败!");
        }

        result = sendServer(encode.encode(message.getUser().getBytes()), in,
                out);
        if (result != 334) {
            throw new IOException("用户名错误!");
        }
        result = sendServer(encode.encode(message.getPassword().getBytes()),
                in, out);

        if (result != 235) {
            throw new IOException("验证失败!");
        }
    }

    // 开始发送消息,邮件源地址
    public void mailfrom(String source, BufferedReader in, BufferedWriter out)
            throws IOException {
        int result;
        result = sendServer("MAIL FROM:<"   source   ">", in, out);
        if (result != 250) {
            throw new IOException("指定源地址错误");
        }
    }

    // 设置邮件收件人
    public void rcpt(String touchman, BufferedReader in, BufferedWriter out)
            throws IOException {
        int result;
        result = sendServer("RCPT TO:<"   touchman   ">", in, out);
        result = sendServer("RCPT TO:<"   "to2@gmail.com"   ">", in, out); // 发送给多个收信人方法
        result = sendServer("RCPT TO:<"   "to3@gmail.com"   ">", in, out); // 发送给多个收信人方法
        result = sendServer("RCPT TO:<"   "to4@gmail.com"   ">", in, out); // 发送给多个收信人方法
        if (result != 250) {
            throw new IOException("指定目的地址错误!");
        }
    }

    // 邮件体
    public void data(String from, String to, String subject, String content,
            BufferedReader in, BufferedWriter out) throws IOException {
        int result;
        result = sendServer("DATA", in, out);
        // 输入DATA回车后,若收到354应答后,继续输入邮件内容
        if (result != 354) {
            throw new IOException("不能发送数据");
        }
        out.write("Content-Type: text/html;charset="UTF-8""); //可以是纯文本:text/plain
        out.newLine();
        out.write("From: "   from);// 可以用email地址 // 中文有时会乱码,下面的方法在同域下可行,跨域后会有问题:"=?UTF-8?B?" encode.encode(("昵称").getBytes()) "?="
        out.newLine();
        out.write("To: "   to  "; to2@gmail.com"); // 设置收信栏显示内容,可以设置多个,但真正发送需要使用命令 RCPT TO:
        out.newLine();
        out.write("Cc: "   "to3@gmail.com"); // CC
        out.newLine();
        out.write("Bcc: "   "to4@gmail.com"); // BCC
        out.newLine();
        out.write("Subject: "   subject);
        out.newLine();
        TimeZone tz = TimeZone.getTimeZone( "GMT 01:00" );
        Date now = new Date();
        out.write("Date: "   formatSMTPDate( now, tz ) );
        out.newLine();
        out.newLine();
        out.write(content);
        out.newLine();
        // 句号加回车结束邮件内容输入
        result = sendServer(".", in, out);
        System.out.println(result);
        if (result != 250) {
            throw new IOException("发送数据错误");
        }
    }

    /* Tue, 16 Sep 1997 15:39:01 -0400 (EDT) */

    public String formatSMTPDate(Date date, TimeZone tz)
            throws IllegalArgumentException {
        SimpleDateFormat dateFormat;
        Locale loc = Locale.US;

        dateFormat = new SimpleDateFormat("EEE", loc);
        dateFormat.setTimeZone(tz);
        String day = dateFormat.format(date);
        day = day.substring(0, 3);

        dateFormat = new SimpleDateFormat("MMM", loc);
        dateFormat.setTimeZone(tz);
        String month = dateFormat.format(date);
        month = month.substring(0, 3);

        dateFormat = new SimpleDateFormat("dd", loc);
        dateFormat.setTimeZone(tz);
        String dayNum = dateFormat.format(date);

        dateFormat = new SimpleDateFormat("yyyy HH:mm:ss", loc);
        dateFormat.setTimeZone(tz);
        String rest = dateFormat.format(date);

        String result = new String(day   ", "   dayNum   " "   month   " "
                  rest   "  0000");

        return result;
    }

    // 退出
    public void quit(BufferedReader in, BufferedWriter out) throws IOException {
        int result;
        result = sendServer("QUIT", in, out);
        if (result != 221) {
            throw new IOException("未能正确退出");
        }
    }

    // 发送邮件主程序
    public boolean sendMail(MailMessage message, String server) {
        try {
            BufferedReader in = new BufferedReader(new InputStreamReader(
                    socket.getInputStream()));
            BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
                    socket.getOutputStream()));
            helo(server, in, out);// HELO命令
            authLogin(message, in, out);// AUTH LOGIN命令
            mailfrom(message.getFrom(), in, out);// MAIL FROM
            rcpt(message.getTo(), in, out);// RCPT
            data(message.getDatafrom(), message.getDatato(),
                    message.getSubject(), message.getContent(), in, out);// DATA
            quit(in, out);// QUIT
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
}

备注:关于引包问题

sun.misc.BASE64Encoder找不到jar包的解决方法

解决方案1(推荐): 
只需要在project build path中先移除JRE System Library,再添加库JRE System Library,重新编译后就一切正常了。 
解决方案2: 
Windows -> Preferences -> Java -> Compiler -> Errors/Warnings -> 
Deprecated and trstricted API -> Forbidden reference (access rules): -> change to warning

 

CSDN代码下载:

 

public void CDOsendMail()
{
      try
      {                        
            CDO.Message oMsg = new CDO.Message();
                   
            oMsg.From = "myaccount@test.com";
            oMsg.To = "myaccount@test.com";
            oMsg.Subject = "MailTest";
             oMsg.HTMLBody = "<html><body>Test</body></html>";
            CDO.IConfiguration iConfg = oMsg.Configuration;
            ADODB.Fields oFields = iConfg.Fields;
                                                            
oFields["http://schemas.microsoft.com/cdo/configuration/sendusing"].Value=2;
oFields["http://schemas.microsoft.com/cdo/configuration/sendemailaddress"]
      .Value="myaccount@test.com"; //sender mail
oFields["http://schemas.microsoft.com/cdo/configuration/smtpaccountname"]
      .Value="myaccount@test.com"; //email account
oFields["http://schemas.microsoft.com/cdo/configuration/sendusername"]
      .Value="username";
oFields["http://schemas.microsoft.com/cdo/configuration/sendpassword"]
      .Value="password";      
oFields["http://schemas.microsoft.com/cdo/configuration/smtpauthenticate"]
      .Value=1;
//value=0 代表Anonymous验证方式(不需要验证)
//value=1 代表Basic验证方式(使用basic (clear-text) authentication. 
//The configuration sendusername/sendpassword or postusername/postpassword fields 
      are used to specify credentials.)
//Value=2 代表NTLM验证方式(Secure Password Authentication in Microsoft Outlook Express)
oFields["http://schemas.microsoft.com/cdo/configuration/languagecode"].Value=0x0804;
oFields["http://schemas.microsoft.com/cdo/configuration/smtpserver"].Value="smtp.21cn.com";

            oFields.Update();
            oMsg.BodyPart.Charset="gb2312";
            oMsg.HTMLBodyPart.Charset="gb2312"; 

            oMsg.Send();
            oMsg = null;
      }                  
      catch (Exception e)
      {
            throw e;
      }
}

php实现邮件发送

注意:由于Exchange2000的CDO组件cdoex.dll会更新原有的Windows2000的CDO组件cdosys.dll,所以如果您希望继续使用cdosys.dll,您必须先通过regsrv32.exe卸载掉cdoex.dll。

class Mailer{ private $host; private $port = 25; private $user; private $pass; private $debug = false; private $sock; public function __construct($host,$port,$user,$pass,$debug = false) { $this->host = $host; $this->port = $port; $this->user = base64_encode; //用户名密码一定要使用base64编码才行 $this->pass = base64_encode; $this->debug = $debug; //socket连接 $this->sock = fsockopen($this->host,$this->port); if{ exit; } //读取smtp服务返回给我们的数据 $response = fgets; $this->debug; //如果响应中有220返回码,说明我们连接成功了 if(strstr === false){ exit; } }//发送SMTP指令,不同指令的返回码可能不同 public function execCommand{ fwrite; $response = fgets;//输出调试信息 $this->debug('cmd:'.$cmd .';response:'.$response); if(strstr($response,$return_code) === false){ return false; } return true; } public function sendMail($from,$to,$subject,$body){//detail是邮件的内容,一定要严格按照下面的格式,这是协议规定的 $detail = 'From:'.$from."rn"; $detail .= 'To:'.$to."rn"; $detail .= 'Subject:'.$subject."rn"; $detail .= 'Content-Type: Text/html;'."rn"; $detail .= 'charset=gb2312'."rnrn"; $detail .= $body; $this->execCommand("HELO ".$this->host."rn",250); $this->execCommand; $this->execCommand($this->user."rn",334); $this->execCommand($this->pass."rn",235); $this->execCommand("MAIL FROM:rn",250); $this->execCommand("RCPT TO:rn",250); $this->execCommand; $this->execCommand($detail."rn.rn",250); $this->execCommand; } public function debug{ if{ echo 'Debug:'.$message . PHP_EOL .''; } } public function __destruct() { fclose; }}

$port = 25;$user = 'username'; //请替换成你自己的smtp用户名$pass = 'pass'; //请替换成你自己的smtp密码$host = 'smtp.163.com';$from = 'xxxxx@163.com'; $to = 'xxxx@qq.com';$body = 'hello world';$subjet = '我是标题';$mailer = new Mailer($host,$port,$user,$pass,true);$mailer->sendMail($from,$to,$subjet,$body);

在执行指令时有输出调试信息,输出了我们每次执行的指令以及smtp服务返回给我们的响应数据。

使用Socket撰写邮件发送程序

因此我们可以看到以下结果

当然,如果您觉得SmtpMail不能满足您的需求,CDO又不够直截了当,那就只能自己动手了;其实如果您很熟悉Socket编程,自己写一个发送邮件的程序并不很难,以下就是一个例子。

Debug:220 163.com Anti-spam GT for Coremail System Debug:cmd:HELO smtp.163.com ;response:250 OKDebug:cmd:AUTH LOGIN ;response:334 dXNlcm5hbWU6Debug:cmd:aXR6aG91anVuYmxvZ0AxNjMuY29t ;response:334 UGFzc3dvcmQ6Debug:cmd:QzBjSGRRNe32xiNGFYUE5oag== ;response:235 Authentication successfulDebug:cmd:MAIL FROM: ;response:250 Mail OKDebug:cmd:RCPT TO:<380472723@qq.com> ;response:250 Mail OKDebug:cmd:DATA ;response:354 End data with .Debug:cmd:From:itzhoujunblog@163.com To:380472723@qq.com Subject:我是标题 Content-Type: Text/html; charset=gb2312 hello world . ;response:250 Mail OK queued as smtp11,D8CowACXHE5APdNYCo0hAQ--.19144S2 1490238785Debug:cmd:QUIT ;response:221 Bye

首先,我们简单介绍一下带验证的SMTP服务器如何使用AUTH原语进行身份验证,其详细的定义可以参考RFC2554。

总结

具体如下:

使用socket连接smtp服务 使用smtp指令进行对话,输入身份信息,邮件信息等 结束对话

1)首先,需要使用EHLO而不是原先的HELO。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

2)EHLO成功以后,客户端需要发送AUTH原语,与服务器就认证时用户名和密码的传递方式进行协商。

3)如果协商成功,服务器会返回以3开头的结果码,这是就可以把用户名和密码传给服务器。

4)最后,如果验证成功,就可以开始发信了。

下面是一个实际的例子,客户端在WinXP的Command窗口中通过"telnet smtp.263.NET 25"命令连接到263的smtp服务器发信:

220 Welcome to coremail System(With Anti-Spam) 2.1
EHLO 263.NET
250-192.168.30.29
250-PIPELINING
250-SIZE 10240000
250-ETRN
250-AUTH LOGIN
250 8BITMIME
AUTH LOGIN
334 VXNlcm5hbWU6
bXlhY2NvdW50
334 UGFzc3dvcmQ6
bXlwYXNzd29yZA==
235 Authentication successful
MAIL FROM:myaccount@263.NET
250 Ok
RCPT TO:myaccount@263.NET
250 Ok
Data
354 End data with <CR><LF>.<CR><LF>
This is a testing email.
haha.
.
250 Ok: queued as AC5291D6406C4
QUIT
221 Bye

上面的内容就是发信的全过程。其中与身份验证有关的主要是第九到第十四行:

AUTH LOGIN '客户端输入

334 VXNlcm5hbWU6 '服务器提示“Username:="

bXlhY2NvdW50 '客户端输入“myaccount="的Base64编码

334 UGFzc3dvcmQ6 '服务器提示“Password:="

bXlwYXNzd29yZA== '客户端输入“mypassword="的Base64编码

235 Authentication successful '服务器端通过验证

从上面的分析可以看出,在这个身份验证过程中,服务器和客户端都直接通过Socket传递经过标准Base64编码的纯文本。这个过程可以非常方便的用C#实现,或者直接添加到原有的源代码中。

另外,有些ESMTP服务器不支持AUTH LOGIN方式的认证,只支持AUTH CRAM-MD5方式验证。但是这两者之间的区别只是文本的编码方式不同。

实现此功能的源代码可以在SourceForge.NET 上找到下载。下面给出了一个简单的伪码:

public void SendMail(MailMessage msg)                  
{            
      NetworkStream nwstream = GetConnection();

      WriteToStream(ref nwstream, "EHLO "   smtpHost   "rn");
      string welcomeMsg = ReadFromStream(ref nwstream);

      // implement HELO command if EHLO is unrecognized.
      if (IsUnknownCommand(welcomeMsg))
      {
            WriteToStream(ref nwstream, "HELO "   smtpHost   "rn");
      }
      CheckForError(welcomeMsg, ReplyConstants.OK);                        

      // Authentication is used if the u/p are supplied
      AuthLogin(ref nwstream);

      WriteToStream(ref nwstream, "MAIL FROM: <"   msg.From.Address   ">rn");
      CheckForError(ReadFromStream(ref nwstream), ReplyConstants.OK);

      SendRecipientList(ref nwstream, msg.To);
      SendRecipientList(ref nwstream, msg.CC);
      SendRecipientList(ref nwstream, msg.BCC);

      WriteToStream(ref nwstream, "DATArn");
      CheckForError(ReadFromStream(ref nwstream), ReplyConstants.START_INPUT);

      if (msg.ReplyTo.Name != null && msg.ReplyTo.Name.Length != 0)
            { WriteToStream(ref nwstream, "Reply-To: ""   msg.ReplyTo.Name   "" <"   
                  msg.ReplyTo.Address   ">rn"); }
      else
            { WriteToStream(ref nwstream, "Reply-To: <"   msg.ReplyTo.Address   ">rn"); }
            
      if (msg.From.Name != null && msg.From.Name.Length != 0)
            { WriteToStream(ref nwstream, "From: ""   msg.From.Name   "" <"   
                  msg.From.Address   ">rn"); }
      else
            { WriteToStream(ref nwstream, "From: <"   msg.From.Address   ">rn"); }
      
      WriteToStream(ref nwstream, "To: "   CreateAddressList(msg.To)   "rn");
      
      if (msg.CC.Count != 0)
            { WriteToStream(ref nwstream, "CC: "   CreateAddressList(msg.CC)   "rn"); }

      WriteToStream(ref nwstream, "Subject: "   msg.Subject   "rn");

      if (msg.Priority != null)
      { WriteToStream(ref nwstream, "X-Priority: "   msg.Priority   "rn"); }

      if (msg.Headers.Count > 0)
      {
            SendHeaders(ref nwstream, msg);
      }
      
      if (msg.Attachments.Count > 0 || msg.HtmlBody != null)
      {
            SendMessageBody(ref nwstream, msg);
      }
      else
      {
            WriteToStream(ref nwstream, msg.Body   "rn"); 
      }
      
      WriteToStream(ref nwstream, "rn.rn");
      CheckForError(ReadFromStream(ref nwstream), ReplyConstants.OK);


      WriteToStream(ref nwstream, "QUITrn");
      CheckForError(ReadFromStream(ref nwstream), ReplyConstants.QUIT);

      CloseConnection();
}

private bool AuthLogin(ref NetworkStream nwstream)
{
if (username != null && username.Length > 0 && 
      password != null && password.Length > 0)
{
      WriteToStream(ref nwstream, "AUTH LOGINrn");
      if (AuthImplemented(ReadFromStream(ref nwstream)))
      {
            WriteToStream(ref nwstream, Convert.ToBase64String(
                        Encoding.ASCII.GetBytes(this.username.ToCharArray()))   "rn");

            CheckForError(ReadFromStream(ref nwstream), ReplyConstants.SERVER_CHALLENGE);

            WriteToStream(ref nwstream, Convert.ToBase64String(Encoding.ASCII.GetBytes(
                                    this.password.ToCharArray()))   "rn");
            CheckForError(ReadFromStream(ref nwstream), ReplyConstants.AUTH_SUCCESSFUL);
            return true;
      }
}
return false;
}

总结

本文介绍了.NET中三种不同的使用SMTP协议发送邮件的方法,其中第一种(使用SmtpMail类)方案能满足大部分基本的发送邮件的功能需求,而第二种(使用CDO组件)和第三种(使用Socket自己撰写SMTP类)方案提供更自由和完整的定制方法,比如他们都能实现第一种方案不能做到的通过带认证的SMTP服务器发送邮件的功能。

本文由澳门新葡8455手机版发布于计算机编程,转载请注明出处:NET中动用SMTP发送邮件,Java发送邮件

上一篇:resource上传文件到七牛的实例代码,nodejs利用aj 下一篇:没有了
猜你喜欢
热门排行
精彩图文