分享
 
 
 

用Java实现SMTP服务器

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

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

◆ SMTP 协议

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

◆ POP3 协议

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

◆ IMAP 协议

Internet 消息访问协议(Internet Message Access Protocol,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()函数实现将窗体启动时置于屏幕的正中心,窗口定义代码如下:

public static void main(String[] args) {

mailSendFrame mailSendFrame = new mailSendFrame();

Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();

Dimension frameSize = 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()方法设置窗体的左上角坐标,使窗体的中心和屏幕的中心正好重合,从而将窗体居中。

//组件实例变量的定义

Panel panelMain = new Panel();

Panel panelUp = new Panel();

Panel panel3 = new Panel();

Panel panel4 = new Panel();

Panel panel6 = new Panel();

Panel panel7 = new Panel();

TextField txtServer = new TextField();

TextField txtTo = new TextField();

TextField txtFrom = new TextField();

TextField txtSubject = new TextField();

Panel panel8 = new Panel();

Label lblFile = new Label();

Button cmdBrowse = new Button();

Panel panelDown = new Panel();

TextArea txtMail = new TextArea();

Panel panel10 = new Panel();

Button cmdSend = new Button();

Button cmdExit = new Button();

.......

.......

panelMain.add(panelUp, null);

panelUp.add(panel3, null);

panel3.add(new Label("发信服务器:"), null);

panel3.add(txtServer, null);

panelUp.add(panel4, null);

panel4.add(new Label("收件人:"), null);

panel4.add(txtTo, null);

panelUp.add(panel6, null);

panelUp.add(panel7, null);

panel7.add(new Label("主题:"), null);

panel7.add(txtSubject, null);

panel6.add(new Label("发件人:"), null);

panel6.add(txtFrom, null);

panelUp.add(panel8, null);

panel8.add(new Label("附件: "), 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(new Label(" "), BorderLayout.EAST);

panelDown.add(new Label(" "), BorderLayout.WEST);

........

........

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

2.窗体中的事件处理

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

private FileDialog openFileDialog= new FileDialog(this,"打开文件",FileDialog.LOAD);

public mailSendFrame() {

try {

Init();

}

catch(Exception e) {

e.printStackTrace();

}

}

......

......

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

cmdSend.addActionListener(new java.awt.event.ActionListener() {

public void actionPerformed(ActionEvent e) {

cmdSend_actionPerformed(e);

}

}

实现cmdSend_actionPerformed()方法如下:

void cmdSend_actionPerformed(ActionEvent e) {

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(new java.awt.event.ActionListener() {

public void actionPerformed(ActionEvent e) {

cmdExit_actionPerformed(e);

}

}

this.addWindowListener(new java.awt.event.WindowAdapter() {

public void windowClosing(WindowEvent e) {

this_windowClosing(e);

}

}

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

void cmdExit_actionPerformed(ActionEvent e) {

System.exit(0);

}

void this_windowClosing(WindowEvent e) {

System.exit(0);

}

3.SmtpMail 类的实现

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

public int open(String serverName, int port){

try{

mailSocket = new Socket(serverName, port);

send = new PrintWriter(mailSocket.getOutputStream(), true);

recv = new BufferedReader(new InputStreamReader(mailSocket.getInputStream()));

String s1 = recv.readLine();

char c = s1.charAt(0);

if((c == '4') (c == '5'))

return 0;

}

catch(Exception e){

return 0;

}

return 1;

}

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

public int transmit(){

boolean flag = true;

//发送HELO 命令

if(domain.length() != 0){

int i = sendString("HELO " + domain);

if(i != 1)

return 0;

}

//发送MAIL FROM 命令(发件人)

if(from.length() != 0){

int j = sendString("MAIL FROM:" + from);

if(j != 1)

return 0;

}

//发送RCPT TO 命令(收件人)

if(to.length() != 0){

int k = sendString("RCPT TO:" + to);

if(k != 1)

return 0;

}

//发送邮件正文(DATA 命令)

if(sendString("DATA") != 1)

return 0;

//发送邮件头信息

for(int l = 0; l < x_set.size(); l += 2){

String s = (String)x_set.elementAt(l);

send.println(s + ": " + x_set.elementAt(l + 1));

}

//发送时间及相关内容格式说明

if(x_set.indexOf("Date") < 0)

send.println("Date: " + (new Date()).toString());

........

........

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

private int sendString(String s){

String s1 = "";

try{

send.println(s);

s1 = recv.readLine();

}

catch(Exception e){

System.out.print(s1);

return 0;

}

if(s1.length() == 0)

return 0;

char c = s1.charAt(0);

return !((c == '4') (c == '5')) ? 1 : 0;

}

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

public int close(){

int i = 0;

try{

i += sendString("QUIT");

mailSocket.close();

}

catch(Exception e){

return 0;

}

return i == 0 ? 1 : 0;

}

mailSendFrame.java源程序代码如下:

import java.awt.*;

import java.awt.event.*;

public class mailSendFrame extends Frame {

smtpMail mailSender=new smtpMail();

Panel panelMain = new Panel();

Panel panelUp = new Panel();

Panel panel3 = new Panel();

Panel panel4 = new Panel();

Panel panel6 = new Panel();

Panel panel7 = new Panel();

TextField txtServer = new TextField();

TextField txtTo = new TextField();

TextField txtFrom = new TextField();

TextField txtSubject = new TextField();

Panel panel8 = new Panel();

Label lblFile = new Label();

Button cmdBrowse = new Button();

Panel panelDown = new Panel();

TextArea txtMail = new TextArea();

Panel panel10 = new Panel();

Button cmdSend = new Button();

Button cmdExit = new Button();

private FileDialog openFileDialog

= new FileDialog(this,"打开文件",FileDialog.LOAD);

public mailSendFrame() {

try {

Init();

}

catch(Exception e) {

e.printStackTrace();

}

}

public static void main(String[] args) {

mailSendFrame mailSendFrame = new mailSendFrame();

Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();

Dimension frameSize = 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();

}

private void Init() throws Exception {

this.setLayout(new BorderLayout());

panelMain.setLayout(new GridLayout(2,1));

panelUp.setLayout(new GridLayout(6,1));

panel3.setLayout(new FlowLayout());

this.setVisible(true);

.......

.......

//smtpMail.java 的源代码

import java.io.*;

import java.net.Socket;

import java.util.*;

public class smtpMail{

private boolean sendConf=false;

public static final int OK = 1;

public static final int ERROR = 0;

private static final String TEXT = "1";

private static final String TFILE = "2";

private static final String BFILE = "3";

private static final String CPR = "Java 1.0";

private static final String MAILER = "X-Mailer";

private static final int BUFFER_SIZE = 48;

private String DELIMETER;

private String SEPARATOR;

private static final int HOW_LONG = 6;

private static final char SMTP_ERROR_CODE1 = 52;

private static final char SMTP_ERROR_CODE2 = 53;

private static final int fillchar = 61;

private static final String cvt =

"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

private Socket mailSocket;

private BufferedReader recv;

private PrintWriter send;

private String from;

private String to;

private String domain;

private Vector x_set;

private Vector body;

private Vector attach;

public smtpMail(){

DELIMETER = "";

SEPARATOR = "";

mailSocket = null;

recv = null;

send = null;

from = "";

to = "";

domain = "";

x_set = new Vector();

body = new Vector();

attach = new Vector();

DELIMETER = getId();

SEPARATOR = System.getProperty("file.separator");

}

.........

.........

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
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- 王朝網路 版權所有