用java开发Email工具之发送邮件

发送邮件

作者:冯睿

本文介绍了如何利用Java的网络API来实现一个电子邮件工具程序。通常Email工具都是使用SMTP(简单邮件传输协议, Simple Mail Transfer Protocol)来发送邮件,使用POP3协议来接受电子邮件。在本文中只对这两个协议作简单介绍。如果有兴趣的读者可以参考以下站点:

POP3: ftp://ftp.isi.edu/in-notes/rfc1939.txt

SMTP: ftp://ftp.isi.edu/in-notes/rfc2821.txt

Java中虽然提供了JavaMail API,但是由于在这篇文章中我将从底层来探讨电子邮件软件是如何工作的,因此不会使用JavaMail API。本文中的例子是在J2SE 1.4下开发的。

电子邮件的格式

在开发Email软件之前,你需要了解电子邮件的格式。根据RFC 2882(http://www.faqs.org/rfcs/rfc2822.html)的规定,电子邮件由很多行组成,每行由〈CRLF〉(ASCII代码13和ASCII代码10)结束。每行的最大长度为998个字符。其中有些行提供了收发电子邮件所必需的信息,这些行被称为头(Header),所有的头构成了头域(Header Field)。其他的行用于保存邮件的具体内容。

头域提供了很多信息,其中包括邮件的来源;邮件的目的地和邮件的主题等。每个头由名称和冒号加上相应的值构成。例如From:、Send:和Reply-To:中记录了邮件的来源。在From:中记录的是邮件的作者;在Sender:中指定了发送邮件的代理(可以是邮件地址,也可以是机器名称);Reply-To:中指定了接受回信的邮箱地址。

一封邮件可能有多个作者,因此From:中可以指定一个或多个邮箱地址。下面给出了一个个From:的例子:

From: Ray Feng 〈rayfeng@yahoo.com.cn〉, bogus@yahoo.com.cn

在一封电子邮件中只能有一个Sender。因此Sender:的值只能包含一个邮箱地址。如果在From:中只有一个作者,而且Sender:的值和From:的值相同,则Sender:就不会出现在电子邮件中,否则会出现信息冗余;反之Sender:则应该出现在邮件中。下面是一个Sender:的例子:

Sender: Ray Feng rayfeng@yahoo.com.cn

在电子邮件中可以指定将回信发送到多个邮箱地址中。因此Reply-To:中可以包含一个或多个邮箱地址,每个地址之间用逗号隔开。如果邮件中有Reply-To:,回信会被发送到罗列在Reply-To:中的所有地址;如果邮件中没有Reply-To:,则回信会被发送到罗列在From:中的地址。那么谁会收到邮件呢?To:和Cc:中保存了接受邮件的邮箱地址。两者的值都可以包含多个邮箱地址。

除了邮件的来源和接受者,RFC 2882中还定义了其他一些头,例如Subject:中包含了电子邮件的主题。下面是一个电子邮件头域的例子:

From: Ray Feng 〈rayfeng@yahoo.com.cn〉
To: bogus 〈bogus@yahoo.com.cn〉
Cc: John 〈John@yahoo.com.cn〉
Subject: Test Email

附件

在MIME中允许在电子邮件中添加二进制文件,被添加的文件叫做附件。附件的内容可以作为邮件的一部分进行传输。MIME是如果实现这个功能的呢?在MIME中引入了很多头,其中和附件相关的最重要的就是Content-Type:和Content-Tracnsfer-Encoding:。为了在一封电子邮件中区分不同的部分,MIME要求在Content-Type: multipart/mixed头中包含一个边界参数。边界参数的值是一个在双引号中的字符串。通过这个字符串,程序就可以区分电子邮件的不同部分。在传输电子邮件的内容前,程序先传输一个〈CRLF〉,两个连字符和边界参数。当完成Email内容的传输后,程序会在最后传输边界参数和两个连字符。

下面的电子邮件中包含了两个部分,一个部分是由iso-8859-1字符组成的文本,一部分是名为file.txt的附件。这里没有包含Content-Transfer-Encoding:头,表明使用缺省的7位ASCII字符。

Content-Type: multipart/mixed; boundary="***"
--***
Content-Type: text/plain; charset="iso-8859-1"
This message has an attachment.
--***
Content-Type: text/plain; name="file.txt"
Attachment text.
--***--

发送电子邮件

screen.width-333)this.width=screen.width-333;">

基于互联网的电子邮件通常是利用SMTP网络协议进行传输的。根据SMTP,当电子邮件程序需要发送电子邮件时,该程序首先同一个SMTP服务程序建立起双向的通讯通道(通常是通过套接字建立这种通道的)。这个基本的SMTP服务程序或许是这份电子邮件的最终目的地,也可能只是通向另一个SMTP服务程序的跳板。总而言之,当电子邮件程序同SMTP服务程序建立起双相的传输通道后,电子邮件程序会向SMTP服务程序发送一系列基于ASCII字符的命令,而SMTP服务程序会对这些命令产生相应的回应来表明相应的操作是成功还是失败了。

让我们假设所有的操作都成功了,那么电子邮件程序将把邮件发送到SMTP服务程序,如果电子邮件的接收地址正好是该SMTP服务程序运行的服务器,那么SMTP服务程序就会将邮件加入邮件数据库中,否则SMTP服务程序将把邮件转发到在其他SMTP服务器上的SMTP服务程序,直到到达目的地为止。图二通过图示说明了这一点。

SMTP可以识别很多电子邮件用来与SMTP服务程序通讯的命令。某些命令需要参数,某些命令则不需要。但是每个命令后必须跟一个〈CRLF〉。最常用的六个命令是HELO,MAIL,RCPT,DATA,RSET和QUIT。

按照上面的顺序给出这六个命令并非偶然。除了RSET外,其他的命令必须按照特定的顺序发送,这是因为SMTP服务程序是基于状态的。对于每一个建立了双向通讯通道的电子邮件程序,SMTP服务程序都会保存当前的通讯状态。

当一个电子邮件程序和SMTP服务程序建立联系后,SMTP服务程序将向电子邮件程序发送初始化消息。该消息包含了一个三位回应码,这个回应码是用来标识SMTP服务程序的。除此之外,在SMTP服务程序发送给电子邮件程序的消息的头部也带有回应码,它们被用来表示操作成功或者失败。电子邮件程序接收到这些回应码后,可以根据其中包含的信息完成相应的工作。而消息的文本部分是给人看的,电子邮件程序可以忽略文本部分。

在收到初始化消息后,电子邮件程序通过发送HELO命令来开始传输邮件。HELO命令有一个参数,该参数标志了SMTP服务程序所在服务器的域名。它将在SMTP服务程序中标识出SMTP服务程序。作为回应,SMTP服务程序进行一些初始化工作,将自己设定到初始状态以接收电子邮件。当这些工作成功完成后,它发送回一条成功的回应消息给电子邮件程序,该回应消息以回应码250开头。

在HELO命令之后,电子邮件程序会发送MAIL命令。MAIL命令将在SMTP服务程序中标识出发送者,它有两个参数:FROM:和一个电子邮件地址。如果SMTP服务程序能够成功地解析电子邮件地址的话,通常它将返回以250开头的回应消息;否者将发送回表示操作失败的回应消息。

在MAIL之后是RCPT命令。RCPT命令在SMTP服务程序中标识出一个邮件的接收者,它也有两个参数:TO:和一个电子邮件地址。如果邮件由多个接收者,则程序需要多次发送RCPT命令。

RCPT命令之后,程序需要发送电子邮件本身了。程序先发送一个DATA命令,当接收到表示成功的回应消息后,将电子邮件逐行发送给SMTP服务程序,当所有的行都发送完毕后,程序发送一行由句号组成的行。在此之后,电子邮件程序等待SMTP服务程序的回应消息,以确定邮件被SMTP服务程序正常接收了。这一切都成功后,程序可以发送RSET命令来退出邮件传输过程。最后,当要断开和SMPT服务程序建立的连接时,程序发送QUIT命令。主要提醒的一点是,虽然上面的命令都是大写的,但是在实际的协议对大小写不敏感。
现在也许你关心的问题是回应码的格式是怎样的。最左边的一位数字代表操作是否成功,1代表收到命令,2代表操作成功完成,3代表等待后续命令,4代表操作临时未能完成(电子邮件程序可以在当前的邮件传输过程中重新发送命令),5代表操作不能完成(电子邮件程序不能在当前的邮件传输过程中重新发送命令)。第二位数字代表回应的领域,0代表语法错误,1代表消息请求,2代表传输通道,3和4没有指定,5代表与邮件系统相关。最有一位数字对第二位数字做补充说明,这里就不再详述。根据上面的信息,我们可以看出250代表请求的命令已经成功完成;220代表SMTP服务程序正在等待HELO命令;而503代表命令顺序错误。有兴趣的朋友可以参见RFC 2821。

下面提供了一个基于命令行的例子SMTPDemo,这个例子可以帮助你理解基于SMTP的邮件传输机制。这个程序将利用标准端口25连接到一个SMTP服务程序上。为了使程序能够运行,你需要将home更改为你使用的邮件服务器的地址。

// SMTPDemo.java
import java.io.*;
import java.net.*;
class SMTPDemo
{
public static void main (String [] args)
{
String SMTPServer = "home
int SMTPPort = 25;
Socket client = null;
try
{
// 向SMTP服务程序建立一个套接字连接。
client = new Socket (SMTPServer, SMTPPort);
// 创建一个BufferedReader对象,以便从命令行读取用户输入。
BufferedReader stdin;
stdin = new BufferedReader (new InputStreamReader (System.in));
// 创建一个BufferedReader对象,以便从套接字读取输出。
InputStream is = client.getInputStream ();
BufferedReader sockin;
sockin = new BufferedReader (new InputStreamReader (is));
// 创建一个PrintWriter对象,以便向套接字写入内容。
OutputStream os = client.getOutputStream ();
PrintWriter sockout;
sockout = new PrintWriter (os, true);
// 显示同SMTP服务程序的握手过程。
System.out.println ("S:" + sockin.readLine ());
while (true)
{
System.out.print ("C:");
// 读取用户输入。
String cmd = stdin.readLine ();
// 将用户输入的命令发送到SMTP服务程序。
sockout.println (cmd);
// 从套接字读取SMTP服务程序的回应消息并显示在屏幕上。
String reply = sockin.readLine ();
System.out.println ("S:" + reply);
// 如果发送了DATA命令并且获得成功的回应消息,从输入设备读取行,
// 直到读取到完全由句号组成的行时停止, 这些行构成了电子邮件。
if (cmd.toLowerCase ().startsWith ("data") &&
reply.substring (0, 3).equals ("354"))
{
do
{
cmd = stdin.readLine ();
if (cmd != null && cmd.length () 〉 1 &&
cmd.charAt (0) == ´.´)
cmd = ".";
sockout.println (cmd);
if (cmd.equals ("."))
break;
}
while (true);
// 从SMTP服务程序中读取回应消息并显示。
reply = sockin.readLine ();
System.out.println ("S:" + reply);
continue;
}
// 如果用户输入QUIT命令,则退出程序。
if (cmd.toLowerCase ().startsWith ("quit"))
break;
}
}
catch (IOException e)
{
System.out.println (e.toString ());
}
finally
{
try
{
if (client != null)
client.close ();
}
catch (IOException e)
{
}
}
}
}

当运行SMTPDemo时,你将会看到下面的输出。其中C:后面是用户的输入,S:后面是SMTP服务程序返回的信息。

S:220 home.digital.com Microsoft ESMTP MAIL Service, Version: 4.0.2195.2966 ready
at Fri, 13 Dec 2002 15:06:58 +0800

当运行SMTPDemo后,邮件服务程序返回了初始化信息。

C:helo digital.com
S:250 home.digital.com Hello [23.2.254.53]

通过发送helo digital.com命令开始邮件传输过程。digital.com是邮件服务器所在域的域名。然后邮件服务程序返回了以250开头的欢迎信息。

C:mail from: rayfeng@digital.com
S:250 2.1.0 rayfeng@digital.com....Sender OK

接下来是输入邮件发送者的信息mail from:。邮件服务程序返回了成功信息。

C:rcpt to: rayfeng@digital.com
S:250 2.1.5 rayfeng@digital.com

然后是通过rcpt to:指定邮件的接收者。

C:data
S:354 Start mail input; end with 〈CRLF〉.〈CRLF〉
Subject: Test Email
This is the test Email.
.
S:250 2.6.0 HOMEOulkEZ00VNuHKDy00000002@home.digital.com Queued mail for delivery

接下来是输入邮件的内容。发送DATA命令后,等待服务器发送回命令被成功接收的回应消息。当接收到以354开头的回应消息时,就可以输入电子邮件的内容了。完成后以〈CRLF〉.〈CRLF〉结束。

C:quit
S:221 2.0.0 home.digital.com closing connection

最后退出发送电子邮件的过程。请注意回应码221,最左边的2代表操作成功,中间的2表示传输通道,1表示连接关闭。

前面我曾讨论过关于附件的问题。通过SMTPDemo也可以发送附件。通过向邮件服务程序发送下面的命令,就可以在邮件中加入file.txt作为附件。

helo digital.net
mail from: rayfeng@digital.com
rcpt to: rayfeng@digital.com
data
Subject: Attachment Demo
Content-Type: multipart/mixed; boundary="***"
--***
Content-Type: text/plain; charset="iso-8859-1"
This message has an attachment.
--***
Content-Type: text/plain; name="file.txt"
Attachment text.
--***--
quit

到此为止,我们介绍了如何用Java实现Email工具的发送功能,并从地层分析了邮件发送的机制,不知您是否已经掌握了这些内容。在下一篇文章中,我们将一起来研究Email工具的接收功能。

时间: 2024-08-23 16:29:57

用java开发Email工具之发送邮件的相关文章

Java开发工具配置 UltraEdit

确定你的jdk环境变量配置是正确的.对于java初学者来说,编译和运行java程序是一件很头疼的东西.显然,在命令方式下编译和运行java程序用久了,确实是很麻烦,而我们又不想使用大型的开发工具时,UltraEdit是一个不错的选择,很多初学者都是用它来编写java源文件的,可以高亮度显示关键字.1.关键字高亮度显示. 打开UltraEdit->视图->高亮显示类型->java; 此时在UltraEdit输入java程序时,关键字和java包下的类可以高亮度显示, 还有注释也是啊,如果你

Eclipse 3.2 Java开发工具的新特性

Eclipse是一个流行的针对Java编程的集成开发环境(IDE).它还可以用作编写其他语言(比如C++和Ruby)的环境,合并各种种类工具的框架,以及创建桌面或服务器应用程序的富客户端平台.如今,Eclipse开源社区拥有数十个项目,其范围从商务智能到社会网络等各个方面.Eclipse同时也是管理这些项目的非赢利性组织的名称.(而且,尽管我相当肯定它并非地板蜡,还是存在Eclipse汽车.足球队和口香糖.) Eclipse version 3.2在Eclipse Callisto 发行历史上具

扩展Eclipse的Java开发工具

由于 Eclipse 具有功能强大的 Java 开发环境,这使它获得了人们的一致好评.这个 Java 开发环境(再加上团队环境和其它基础功能)使 Eclipse 成为引人注目的集成开发环境,对 Java 开发人员来说,这是个好消息.而且,Eclipse 还是一个开放源码项目.但真正使人们对 Eclipse 感到兴奋的是它提供了扩展的可能性. 许多基于 Eclipse 的商用产品都显示出这种提供集成产品的方法的实际意义.例如,IBM WebSphere Application Developer

十四种Java开发工具点评

在计算机开发语言的历史中,从来没有哪种语言象Java那样受到如此众多厂商的支持,有如此多的开发工具,Java菜鸟们如初入大观园的刘姥姥,看花了眼,不知该何种选择.的确,这些工具各有所长,都没有绝对完美的,就算是老鸟也很难做出选择.在本文中我简要介绍了常见的十四种Java开发工具的特点,管中窥"器",希望能对大家有所帮助. 1.JDK (Java Development Kit) 2.Java Workshop 3.NetBeans 与Sun Java Studio 5 4.Borlan

Java开发工具IntelliJ IDEA单元测试和代码覆盖率图解

原文 http://www.cnblogs.com/xiongmaopanda/p/3314660.html Java开发工具IntelliJ IDEA使用教程:单元测试和代码覆盖率 本文将展示如何使用IntelliJ IDEA开发单元测试和分析覆盖率. 1 创建新的项目 创建名为UnitTestingApp的Java项目. 2 创建一个类进行测试 创建一个新的类用于测试. 添加方法sayHello返回Hello字符串. 3 创建测试源根目录 为了不将测试添加到源中,用户可以创建根目录.在这种情

《Java程序员面试秘笈》—— 面试题4 列出你熟悉的Java开发工具

面试题4 列出你熟悉的Java开发工具 考点:该问题一般会在面试时出现,主要是看求职者是否熟悉常用的Java开发工具,因为一个项目开发团队会使用一种固定的开发工具,只有求职者能够熟练使用该工具,才能够快速融入该团队. 出现频率: [面试题解析]Java作为一种跨平台开发语言,由于其开源性,各个公司和社区组织,都推出了各自的Java开发工具,这也是Java开发体系同微软的开发体系的区别之一.不同的项目开发团队一般都有自己熟悉的开发工具或者开发环境,求职者只有熟练使用不同的开发工具,才能适应项目开发

从代码构建到性能分析,Java开发人员的首选工具

Java 由于其实用性.跨平台性.可移植性等众多优点一直备受开发者青睐,在各大编程语言排行榜中也一直居高不下,因此本文为 Java 开发者们总结了这八个工具,它们几乎涵盖了Java开发的全部范围,从代码构建到错误压缩.这些工具可以帮助您提高代码的质量,并成为更高效的Java开发人员. 集成开发环境 Eclipse Eclipse是著名的跨平台的自由集成开发环境(IDE).最初主要用来Java语言开发,但是目前亦有人通过插件使其作为其他计算机语言比如C++和Python的开发工具. Eclipse

工具-有没有 快速搭建,配置 java开发环境的软件

问题描述 有没有 快速搭建,配置 java开发环境的软件 每次换了系统,换了电脑,换了工作环境都要重头装一遍开发工具,配置开发环境,不知道有没有快捷的软件省去这些重复的工作 解决方案 你完全可以用Java_home啊 用不了多少时间 解决方案二: 没有吧,话说用了这么久还觉得配环境麻烦么?没有多少时间啊 解决方案三: JDK与IDE使用绿色版,配置一下路径都是很快的 解决方案四: 偷懒的方式,你可以把环境装在非系统盘,这样换系统的话,可能不需要重新安装环境了.(只格式化系统盘,没问题) 换电脑的

《Java 开发从入门到精通》—— 2.3 使用IDE工具序

2.3 使用IDE工具    知识点讲解:光盘:视频\PPT讲解(知识点)\第2章\使用IDE工具.mp4 在本章2.2节中的体验Java程序的过程中,发现这样编写.编译.运行程序的过程非常烦琐.为了提高开发效率,我们可以使用第三方工具来帮助我们.在本节的内容中,将讲解几种主流IDE开发工具的基本知识,以帮助读者提高开发Java程序效率. 2.3.1 最受欢迎的工具--Eclipse Eclipse是一个开放源代码的软件开发项目,是一个开放源代码的.基于Java的可扩展开发平台.就其本身而言,E