参考书籍《张孝详 java邮件开发详解》
(1)按例程6-6编写一个名为SMTPSender.java的程序,这个程序向属于多个不同域的收件人发送邮件,它直接连接到每个收件人所在域的SMTP服务器进行邮件发送。
例程6-6 SMTPSender.java
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import java.util.Properties;
import java.util.Date;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.Message;
import javax.mail.internet.MimeMessage;
public class SMTPSender
{
public static void main(String[] args) throws Exception
{
//下面是邮件要群发给的多个收件人地址
String [] to = {"it315_test@sohu.com","it315_test@sina.com",
"it315_test@163.com","it315_test@126.com"};
//创建Session对象
Properties props = new Properties();
/*mail.smtp.localhost属性用于设置SMTP协议的EHLO命令中的
主机名,其他SMTP服务器可能需要使用这个主机名确定发件SMTP
服务器的身份,这个信息可以从JavaMail的javadocs文档中的
com.sun.mail.smtp包的帮助页面内查看到。
*/
props.setProperty("mail.smtp.localhost","mail.itcast.cn");
Session session = Session.getInstance(props);
session.setDebug(true);
Message msg = createMessage(session);
for(int i=0; i<to.length; i++)
{
try
{
sendMessage(session,msg,to[i]);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
public static Message createMessage(Session session)
throws Exception
{
String from = "it315_test@sina.com ";//发件人地址
String subject = "SMTPSender demo";//邮件主题
String body = "SMTPSender demo";//邮件内容
//创建代表邮件的MimeMessage对象,不包含收件人地址
MimeMessage msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(from));
msg.setSentDate(new Date());
msg.setSubject(subject);
msg.setText(body);
return msg;
}
public static void sendMessage(Session session,Message msg,String to)
throws Exception
{
//设置邮件内容的收件人并生成邮件消息内容
msg.setRecipients(Message.RecipientType.TO,
InternetAddress.parse(to));
msg.saveChanges();
//连接收件人地址所在的SMTP服务器并发送邮件
Transport transport = session.getTransport("smtp");
String domain = to.substring(to.indexOf("@")+1);
String smtpServer = getSmtpServer(domain,null);
transport.connect(smtpServer,null,null);
transport.sendMessage(msg,
msg.getRecipients(Message.RecipientType.TO));
transport.close();
}
public static String getSmtpServer(String domain,String dnsServer)
throws Exception
{
DirContext ctx = new InitialDirContext();
Attributes attrsMx = null;
if(dnsServer != null)
{
attrsMx = ctx.getAttributes("dns:" + "//" + dnsServer +
"/" + domain, new String[]{"MX"});
}
else
{
attrsMx = ctx.getAttributes("dns:" + "/" + domain, new String[]{"MX"});
}
String recordMx = (String)attrsMx.get("MX").get();
String smtpServer = recordMx.substring(
recordMx.indexOf(" ") + 1);
return smtpServer;
}
}
实际的SMTP服务器程序要比上面的程序复杂得多,它们每天要向大量的其他SMTP服务器发送大量的邮件,当它们连接上一台其他的SMTP服务器并发送一封邮件后,是否要立即与这个服务器断开连接呢?这就是一个比较难以处理的问题了,如果不断开连接,而随后一段时间内又没有要发送给该SMTP服务器的邮件了,这样就造成网络连接资源浪费;如果发送一封邮件后立即断开与该SMTP服务器的连接,而随后的邮件又是要发送到的该SMTP服务器,又要重新与该SMTP服务器建立连接,这样明显是一种低效率的方法。关于这个问题,有兴趣的读者可以去看一些开源的SMTP服务器程序的源代码,我们就不再这里讨论如何解决这些问题了。
(2)在Windows命令行窗口中编译并运行这个java程序,通常情况下,这个程序发送邮件的过程都会以失败告终,如图6.15所示。
图6.15
出现图6.15中的错误是因为现在的大多数SMTP服务器都增加了一些防垃圾邮件的措施,如果邮件发送程序以SMTP服务器的身份向它们发送邮件,它们要求运行该邮件发送程序的计算机必须在Internet上注册了主机名(也就是A记录),并且这台计算机的IP地址没有被列入进它们的黑名单,而我和读者们使用的计算机往往都无法满足这些条件。
对于sina这样的大型网站,它们提供了完全独立的SMTP服务器来分别与普通邮件客户端程序和其他SMTP服务器进行通信。邮件客户端程序连接的SMTP服务器是smtp.sina.com.cn,smtp.sina.com.cn服务器总是要求对方进行用户身份验证。其他smtp服务器连接的sina的SMTP服务器是sina.com域中MX记录所对应的计算机,例如,sinamx.sina.com.cn,这些注册成了MX记录的计算机不要求对方进行用户身份验证,但也采取各种方式来防止对方发送垃圾邮件。显然,smtp.sina.com.cn是不能被注册为MX记录的,因为它需要对方传递用户身份信息,其他SMTP无法根本无法与之正常通信。
对于小型网站,它们用同一台SMTP服务器来与普通邮件客户端程序和其他SMTP服务器进行通信,它们通常根据对方在ehlo命令中提供的主机名是否与该计算机的ip地址反向解析到的主机名一致来判断对方是普通的邮件客户端程序,还是其他SMTP服务器。如果对方在ehlo命令中指定的主机名与对方的IP地址不匹配,小型网站的SMTP服务器程序则认为对方是普通的邮件客户端程序,需要对方传递用户认证信息;如果对方在ehlo命令中指定的主机名与对方的IP地址匹配,小型网站的SMTP服务器程序则认为对方也是一台SMTP服务器,不需要对方提供用户认证信息即可直接发送邮件。