分享
 
 
 

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

王朝java/jsp·作者佚名  2008-05-31
窄屏简体版  字體: |||超大  

本文介绍了如何利用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;

socki

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有