用Java实现SMTP服务器

发表于:2007-05-26来源:作者:点击数: 标签:
电子邮件传递可以由多种 协议 来实现。目前,在Internet网上最流行的三种电子邮件 协议 是SMTP、POP3和IMAP,下面分别简单介绍。 ◆SMTP 协议 简单邮件传输 协议 (SimpleMailTransferProtocol,SMTP)是一个运行在TCP/IP之上的 协议 ,用它发送和接收电子邮件

电子邮件传递可以由多种协议来实现。目前,在Internet网上最流行的三种电子邮件协议是SMTP、POP3和IMAP,下面分别简单介绍。

◆SMTP协议

简单邮件传输协议(SimpleMailTransferProtocol,SMTP)是一个运行在TCP/IP之上的协议,用它发送和接收电子邮件。SMTP服务器在默认端口25上监听。SMTP客户使用一组简单的、基于文本的命令与SMTP服务器进行通信。在建立了一个连接后,为了接收响应,SMTP客户首先发出一个命令来标识它们的电子邮件地址。如果SMTP服务器接受了发送者发出的文本命令,它就利用一个OK响应和整数代码确认每一个命令。客户发送的另一个命令意味着电子邮件消息体的开始,消息体以一个圆点“.”加上回车符终止。

◆POP3协议

邮局协议(PostOfficeProtocolVersion3,POP3)提供了一种对邮件消息进行排队的标准机制,这样接收者以后才能检索邮件。POP3服务器也运行在TCP/IP之上,并且在默认端口110上监听。在客户和服务器之间进行了初始的会话之后,基于文本的命令序列可以被交换。POP3客户利用用户名和口令向POP3服务器认证。POP3中的认证是在一种未加密的会话基础之上进行的。POP3客户发出一系列命令发送给POP3服务器,如:请求客户邮箱队列的状态、请求列出的邮箱队列的内容和请求检索实际的消息。POP3代表一种存储转发类型的消息传递服务。现在,大部分邮件服务器都采用SMTP发送邮件,同时使用POP3接收电子邮件消息。

◆IMAP协议

Internet消息访问协议(InternetMessageAclearcase/" target="_blank" >ccessProtocol,IMAP)是一种电子邮件消息排队服务,它对POP3的存储转发限制提供了重要的改进。IMAP也使用基于文本命令的语法在TCP/IP上运行,IMAP服务器一般在默认端口143监听。IMAP服务器允许IMAP客户下载一个电子邮件的头信息,并且不要求将整个消息从服务器下载至客户,这一点与POP3是相同的。IMAP服务器提供了一种排队机制以接收消息,同时必须与SMTP相结合在一起才能发送消息。

下面以SMTP发送电子邮件为例讲解怎样用Java实现SMTP服务器应用功能,从而完成邮件的发送的。


SMTP命令


SMTP协议是目前网上流行的发送E-Mail的协议,SMTP协议共有14条命令。不过,发一封E-Mail只需用如下5条命令就足够了,分别为:

◆HELO<SP><domain><CRLF>,与SMTP服务器握手,传送本机域名;

◆MAIL<SP>FROM:<reverse-path><CRLF>,传送发信者的信箱名称;

◆RCPT<SP>TO:<forward-path><CRLF>,传送接收者的信箱名称;

◆DATA<CRLF>,发送信件数据(包括信头和信体);

◆QUIT<CRLF>,退出与SMTP服务器的连接。


编程思路


首先我们设计一个邮件发送程序的交互界面,界面中包括用户输入邮件的收件人、发信人和主题组件的单行文本框,书写邮件内容的多行文本框等。然后为了能够实现E-mail的发送和设置,我们设计一个SmtpMail类,它封装了与邮件服务器之间的Socket通信操作,以及SMTP命令的发送和响应信息的接收。


编程技巧说明


1.设置窗体和组件

我们设计了一个MailSendFrame()类继承Frame对象,作为容纳组件的主窗体。Main()函数实现将窗体启动时置于屏幕的正中央,窗口定义代码如下:

publicstaticvoidmain(String[]args){
mailSendFramemailSendFrame=newmailSendFrame();
DimensionscreenSize=Toolkit.getDefaultToolkit().getScreenSize();
DimensionframeSize=mailSendFrame.getSize();
if(frameSize.height>screenSize.height){
frameSize.height=screenSize.height;
}
if(frameSize.width>screenSize.width){
frameSize.width=screenSize.width;
}
mailSendFrame.setLocation((screenSize.width-frameSize.width)/2,
(screenSize.height-frameSize.height)/2);
mailSendFrame.setVisible(true);
mailSendFrame.show();
}



在Main()函数中,首先利用代表系统信息的Toolkit对象得到当前系统中设置的屏幕分辨率,并且用分辨率和窗体的大小作比较,然后,调用MailSendFrame的SetLocation()方法设置窗体的左上角坐标,使窗体的中心和屏幕的中心正好重合,从而将窗体居中。

//组件实例变量的定义
PanelpanelMain=newPanel();
PanelpanelUp=newPanel();
Panelpanel3=newPanel();
Panelpanel4=newPanel();
Panelpanel6=newPanel();
Panelpanel7=newPanel();
TextFieldtxtServer=newTextField();
TextFieldtxtTo=newTextField();
TextFieldtxtFrom=newTextField();
TextFieldtxtSubject=newTextField();
Panelpanel8=newPanel();
LabellblFile=newLabel();
ButtoncmdBrowse=newButton();
PanelpanelDown=newPanel();
TextAreatxtMail=newTextArea();
Panelpanel10=newPanel();
ButtoncmdSend=newButton();
ButtoncmdExit=newButton();
.......
.......
panelMain.add(panelUp,null);
panelUp.add(panel3,null);
panel3.add(newLabel("发信服务器:"),null);
panel3.add(txtServer,null);
panelUp.add(panel4,null);
panel4.add(newLabel("收件人:"),null);
panel4.add(txtTo,null);
panelUp.add(panel6,null);
panelUp.add(panel7,null);
panel7.add(newLabel("主题:"),null);
panel7.add(txtSubject,null);
panel6.add(newLabel("发件人:"),null);
panel6.add(txtFrom,null);
panelUp.add(panel8,null);
panel8.add(newLabel("附件:"),null);
panel8.add(lblFile,null);
panel8.add(cmdBrowse,null);
panelMain.add(panelDown,null);
panelDown.add(txtMail,BorderLayout.CENTER);
panelDown.add(panel10,BorderLayout.SOUTH);
panel10.add(cmdSend,null);
panel10.add(cmdExit,null);
panelDown.add(newLabel(""),BorderLayout.EAST);
panelDown.add(newLabel(""),BorderLayout.WEST);
........
........



窗体组件的定义都是在Init()方法中完成,设置好收件人、发信人和主题组件的单行文本框,书写邮件内容的多行文本框,以及附件中的浏览按钮、发送和退出按钮。

2.窗体中的事件处理

事件处理也是在Init()方法中完成。选取附件文件的“浏览”按钮的事件处理,在单击该按钮时,打开一个OpenFileDialog文件对话框,读取用户所选取的文件名。打开文件对话框的“浏览”按钮的代码如下:

privateFileDialogopenFileDialog=newFileDialog(this,"打开文件",FileDialog.LOAD);
publicmailSendFrame(){
try{
Init();
}
catch(Exceptione){
e.printStackTrace();
}
}
......
......



单击“发送”按钮的事件处理,实现用户填写邮件信息的收集和邮件的发送操作。“发送”按钮的代码如下:

cmdSend.addActionListener(newjava.awt.event.ActionListener(){
publicvoidactionPerformed(ActionEvente){
cmdSend_actionPerformed(e);
}
}



实现cmdSend_actionPerformed()方法如下:

voidcmdSend_actionPerformed(ActionEvente){
mailSender.setFrom(txtFrom.getText().trim());
mailSender.setTo(txtTo.getText().trim());
mailSender.addHeader("Subject",txtSubject.getText().trim());
mailSender.addData(txtMail.getText());
if(!lblFile.getText().trim().equals(""))
mailSender.addAttachment(lblFile.getText().trim());
mailSender.open(txtServer.getText().trim(),25);
mailSender.transmit();
mailSender.close();
}



单击“退出”按钮的事件处理,实现程序的退出和窗体的关闭。“退出”按钮和侦听器的程序代码如下:

cmdExit.addActionListener(newjava.awt.event.ActionListener(){
publicvoidactionPerformed(ActionEvente){
cmdExit_actionPerformed(e);
}
}
this.addWindowListener(newjava.awt.event.WindowAdapter(){
publicvoidwindowClosing(WindowEvente){
this_windowClosing(e);
}
}



上面程序分别为退出和窗体注册事件的侦听器或适配器,它们处理各自的交互动作。实现cmdExit_actionPerformed()和this_windowClosing()方法如下:

voidcmdExit_actionPerformed(ActionEvente){
System.exit(0);
}
voidthis_windowClosing(WindowEvente){
System.exit(0);
}



3.SmtpMail类的实现

采用Open()方法,建立与邮件服务器之间的TCP/IP连接,创建套接字,并且得到发送命令所用的输出流Send和接收服务器相应所用的输入流Rev。Open()方法的代码如下:

publicintopen(StringserverName,intport){
try{
mailSocket=newSocket(serverName,port);
send=newPrintWriter(mailSocket.getOutputStream(),true);
recv=newBufferedReader(newInputStreamReader(mailSocket.getInputStream()));
Strings1=recv.readLine();
charc=s1.charAt(0);
if((c=='4')|(c=='5'))
return0;
}
catch(Exceptione){
return0;
}
return1;
}



在SmtpMail类中,通过Transmit()方法完成发送任务。Transmit()方法的代码如下:

publicinttransmit(){
booleanflag=true;
//发送HELO命令
if(domain.length()!=0){
inti=sendString("HELO"+domain);
if(i!=1)
return0;
}
//发送MAILFROM命令(发件人)
if(from.length()!=0){
intj=sendString("MAILFROM:"+from);
if(j!=1)
return0;
}
//发送RCPTTO命令(收件人)
if(to.length()!=0){
intk=sendString("RCPTTO:"+to);
if(k!=1)
return0;
}
//发送邮件正文(DATA命令)
if(sendString("DATA")!=1)
return0;
//发送邮件头信息
for(intl=0;l<x_set.size();l+=2){
Strings=(String)x_set.elementAt(l);
send.println(s+":"+x_set.elementAt(l+1));
}
//发送时间及相关内容格式说明
if(x_set.indexOf("Date")<0)
send.println("Date:"+(newDate()).toString());
........
........



使用SendString()方法来发送字符串命令,并且接收邮件服务器的响应信息,判断命令是否被接收。返回1表示命令被拒绝执行,返回0表示命令被接受。SendString()方法的代码如下:

privateintsendString(Strings){
Strings1="";
try{
send.println(s);
s1=recv.readLine();
}
catch(Exceptione){
System.out.print(s1);
return0;
}
if(s1.length()==0)
return0;
charc=s1.charAt(0);
return!((c=='4')|(c=='5'))?1:0;
}



使用Close()方法来关闭与服务器之间的套接字连接。该方法发送“QUIT”命令,收到响应消息后,才真正关闭连接。Close()方法的代码如下:

publicintclose(){
inti=0;
try{
i+=sendString("QUIT");
mailSocket.close();
}
catch(Exceptione){
return0;
}
returni==0?1:0;
}



mailSendFrame.java源程序代码如下:

importjava.awt.*;
importjava.awt.event.*;
publicclassmailSendFrameextendsFrame{
smtpMailmailSender=newsmtpMail();
PanelpanelMain=newPanel();
PanelpanelUp=newPanel();
Panelpanel3=newPanel();
Panelpanel4=newPanel();
Panelpanel6=newPanel();
Panelpanel7=newPanel();
TextFieldtxtServer=newTextField();
TextFieldtxtTo=newTextField();
TextFieldtxtFrom=newTextField();
TextFieldtxtSubject=newTextField();
Panelpanel8=newPanel();
LabellblFile=newLabel();
ButtoncmdBrowse=newButton();
PanelpanelDown=newPanel();
TextAreatxtMail=newTextArea();
Panelpanel10=newPanel();
ButtoncmdSend=newButton();
ButtoncmdExit=newButton();
privateFileDialogopenFileDialog
=newFileDialog(this,"打开文件",FileDialog.LOAD);
publicmailSendFrame(){
try{
Init();
}
catch(Exceptione){
e.printStackTrace();
}
}
publicstaticvoidmain(String[]args){
mailSendFramemailSendFrame=newmailSendFrame();
DimensionscreenSize=Toolkit.getDefaultToolkit().getScreenSize();
DimensionframeSize=mailSendFrame.getSize();
if(frameSize.height>screenSize.height){
frameSize.height=screenSize.height;
}
if(frameSize.width>screenSize.width){
frameSize.width=screenSize.width;
}
mailSendFrame.setLocation((screenSize.width-frameSize.width)/2,
(screenSize.height-frameSize.height)/2);
mailSendFrame.setVisible(true);
mailSendFrame.show();
}
privatevoidInit()throwsException{
this.setLayout(newBorderLayout());
panelMain.setLayout(newGridLayout(2,1));
panelUp.setLayout(newGridLayout(6,1));
panel3.setLayout(newFlowLayout());
this.setVisible(true);
.......
.......
//smtpMail.java的源代码
importjava.io.*;
importjava.net.Socket;
importjava.util.*;
publicclasssmtpMail{
privatebooleansendConf=false;
publicstaticfinalintOK=1;
publicstaticfinalintERROR=0;
privatestaticfinalStringTEXT="1";
privatestaticfinalStringTFILE="2";
privatestaticfinalStringBFILE="3";
privatestaticfinalStringCPR="Java1.0";
privatestaticfinalStringMAILER="X-Mailer";
privatestaticfinalintBUFFER_SIZE=48;
privateStringDELIMETER;
privateStringSEPARATOR;
privatestaticfinalintHOW_LONG=6;
privatestaticfinalcharSMTP_ERROR_CODE1=52;
privatestaticfinalcharSMTP_ERROR_CODE2=53;
privatestaticfinalintfillchar=61;
privatestaticfinalStringcvt=
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
privateSocketmailSocket;
privateBufferedReaderrecv;
privatePrintWritersend;
privateStringfrom;
privateStringto;
privateStringdomain;
privateVectorx_set;
privateVectorbody;
privateVectorattach;
publicsmtpMail(){
DELIMETER="";
SEPARATOR="";
mailSocket=null;
recv=null;
send=null;
from="";
to="";
domain="";
x_set=newVector();
body=newVector();
attach=newVector();
DELIMETER=getId();
SEPARATOR=System.getProperty("file.separator");
}
.........
.........

原文转自:http://www.ltesting.net