本文介绍了如何利用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)的规定,电子邮件由很多行组成,每行由(ASCII代码13和ASCII代码10)结束。每行的最大长度为998个字符。其中有些行提供了收发电子邮件所必需的信息,这些行被称为头(Header),所有的头构成了头域(Header Field)。其他的行用于保存邮件的具体内容。
头域提供了很多信息,其中包括邮件的来源;邮件的目的地和邮件的主题等。每个头由名称和冒号加上相应的值构成。例如From:、Send:和Reply-To:中记录了邮件的来源。在From:中记录的是邮件的作者;在Sender:中指定了发送邮件的代理(可以是邮件地址,也可以是机器名称);Reply-To:中指定了接受回信的邮箱地址。
一封邮件可能有多个作者,因此From:中可以指定一个或多个邮箱地址。下面给出了一个个From:的例子:
From: Ray Feng , 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
To: bogus
Cc: John
Subject: Test Email
附件
在MIME中允许在电子邮件中添加二进制文件,被添加的文件叫做附件。附件的内容可以作为邮件的一部分进行传输。MIME是如果实现这个功能的呢?在MIME中引入了很多头,其中和附件相关的最重要的就是Content-Type:和Content-Tracnsfer-Encoding:。为了在一封电子邮件中区分不同的部分,MIME要求在Content-Type: multipart/mixed头中包含一个边界参数。边界参数的值是一个在双引号中的字符串。通过这个字符串,程序就可以区分电子邮件的不同部分。在传输电子邮件的内容前,程序先传输一个,两个连字符和边界参数。当完成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.
--***--
发送电子邮件
基于互联网的电子邮件通常是利用SMTP网络协议进行传输的。根据SMTP,当电子邮件程序需要发送电子邮件时,该程序首先同一个SMTP服务程序建立起双向的通讯通道(通常是通过套接字建立这种通道的)。这个基本的SMTP服务程序或许是这份电子邮件的最终目的地,也可能只是通向另一个SMTP服务程序的跳板。总而言之,当电子邮件程序同SMTP服务程序建立起双相的传输通道后,电子邮件程序会向SMTP服务程序发送一系列基于ASCII字符的命令,而SMTP服务程序会对这些命令产生相应的回应来表明相应的操作是成功还是失败了。
让我们假设所有的操作都成功了,那么电子邮件程序将把邮件发送到SMTP服务程序,如果电子邮件的接收地址正好是该SMTP服务程序运行的服务器,那么SMTP服务程序就会将邮件加入邮件数据库中,否则SMTP服务程序将把邮件转发到在其他SMTP服务器上的SMTP服务程序,直到到达目的地为止。图二通过图示说明了这一点。
SMTP可以识别很多电子邮件用来与SMTP服务程序通讯的命令。某些命令需要参数,某些命令则不需要。但是每个命令后必须跟一个。最常用的六个命令是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 .
Subject: Test Email
This is the test Email.
.
S:250 2.6.0 HOMEOulkEZ00VNuHKDy00000002@home.digital.com Queued mail for delivery
接下来是输入邮件的内容。发送DATA命令后,等待服务器发送回命令被成功接收的回应消息。当接收到以354开头的回应消息时,就可以输入电子邮件的内容了。完成后以.结束。
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工具的接收功能。
分享到:
相关推荐
【标签】:“JAVA Email”指的是使用JAVA语言处理电子邮件的程序设计,这可能涉及到JavaMail API,这是一个开源库,为开发者提供了处理邮件的标准API,可以实现邮件的发送、接收和解析。邮件客户端则是用户与邮件...
Java Email工具类是Java开发中用于发送电子邮件的实用程序,主要涉及到JavaMail API。这个API提供了处理MIME协议的能力,使开发者能够构建复杂的邮件,包括文本、图片和附件。MIME(Multipurpose Internet Mail ...
本篇将深入讲解如何使用Apache Commons Email工具类来发送带有验证码的电子邮件。 首先,Apache Commons Email是Apache软件基金会的一个项目,它提供了一组丰富的API,简化了Java中发送电子邮件的过程。这个库包含...
如果 用myEclipse进行开发时,如果程序中需要发送邮件,运行时可能会出现以下的异常: Exception in thread "main" java.lang.NoClassDefFound Error: com/sun/mail/util/LineInputStream 原因:邮件发送的jar包...
本篇将通过一个具体的实例——“Java邮件开发实例”,来详细介绍如何在Java项目中实现邮件发送功能。 #### 二、163邮箱服务器配置 在开始邮件发送之前,首先需要配置邮件服务器。这里以163邮箱为例: - **授权码*...
Java开发的邮件发送功能是Java开发者经常遇到的任务之一,它主要涉及到JavaMail API以及相关的库。在本场景中,我们使用的工具是`commons-email`,一个由Apache Commons项目提供的库,用于简化邮件发送过程。同时,...
Java邮件发送组件是Java开发中用于发送电子邮件的工具,它使得开发者能够轻松地在应用程序中实现邮件功能。Java Mail API是实现这一功能的核心库,它提供了丰富的接口和类,允许程序进行邮件的创建、发送以及处理。...
在Java编程语言中,发送邮件是一项常见的任务,特别是在开发Web应用或者需要验证用户身份时,如发送验证码。本文将详细讲解如何使用Java实现这个功能,主要基于`javax.mail`库来创建一个简单的邮件发送程序。 首先...
以下是一个基于`commons-email-1.1`库的Java发邮件实例: 首先,确保你的项目已经正确地引入了`commons-email`库。这通常通过Maven或Gradle的依赖管理来完成。如果你使用的是Maven,可以在`pom.xml`文件中添加以下...
Java邮件发送功能是Java开发中常见的一项任务,用于在应用程序中发送电子邮件。在这个场景中,我们关注的是两个关键的jar包:`commons-email-1.5.jar`和`javax.mail.jar`,它们对于构建Java邮件发送系统至关重要。 ...
本篇文章将深入探讨如何使用Java实现邮件发送功能,主要基于`javax.mail`库,这是一个标准的Java邮件API。 首先,理解Java Mail API的工作原理至关重要。它提供了一组接口和类,用于处理邮件服务器上的邮件消息。...
本教程将深入探讨如何使用Java实现邮件验证,包括发送邮件和进行邮件验证的过程。 首先,我们需要引入第三方的JavaMail API库,通常我们会使用Apache Commons Net库和JavaMail API来处理邮件相关任务。在Maven项目...
在Java开发领域中,JavaMail API是一项非常重要的技术,它使得Java开发者能够方便地处理电子邮件操作,如发送、接收邮件等。本文将深入探讨JavaMail API的相关概念、工作原理以及如何使用JavaMail API进行邮件开发。...
在实际开发中,还可以使用第三方库如Apache Commons Email或JavaMailSender Interface(Spring框架的一部分)来简化邮件发送的过程,它们提供了更友好的API来处理邮件。 总结一下,Java发送带有附件的邮件涉及到...
总结起来,EWS Java API为Java开发者提供了与Exchange Server交互的强大工具,使得在Java应用程序中发送邮件、管理日历等任务变得简单。通过熟练掌握EWS API,可以构建出高效的企业级邮件管理系统。
阿里巴巴邮件发送工具类是Java开发中用于实现电子邮件发送功能的一个实用工具集,它简化了邮件发送过程,并且依赖于特定的库文件。这个工具类包括了`Email1688.java`和`MailModel.java`两个核心类,以及配置文件`...