基於SMTP的JAVA郵件發送程序

這個程序沒有使用JavaMail API,而是根據SMTP協議的要求直接處理協議的細節發送郵件,雖然比較麻煩了一些,但是對了解郵件協議的細節很有幫助的。

本文分兩部分,第一部分是SMTP命令介紹(這個從別的地方抄的,嘿嘿);第二部分通過一個實例真正理解一下發送郵件的過程。

一:SMTP 命令簡介

什麼是 SMTP
SMTP (Simple Mail Transfer Protocol) : 電子郵件從客戶機傳輸到服務器或從某一個服務器傳輸到另一個服務器使用的傳輸協議。 SMTP 是請求/響應協議,命令和響應都是基於 ASCII 文本,並以 CR 和 LF 符結束。響應包括一個表示返回狀態的三位數字代碼。SMTP 在 TCP 協議 25 端口監聽連接請求。

什麼是 ESMTP
ESMTP (Extended SMTP),顧名思義,擴展 SMTP 就是對標準 SMTP 協議進行的擴展。它與 SMTP 服務的區別僅僅是,使用 SMTP 發信不需要驗證用戶帳戶,而用 ESMTP 發信時,服務器會要求用戶提供用戶名和密碼以便驗證身份。驗證之後的郵件發送過程與 SMTP 方式沒有兩樣。

SMTP 命令包括:
HELO 向服務器標識用戶身份。發送者能欺騙,說謊,但一般情況下服務器都能檢測到。
EHLO 向服務器標識用戶身份。發送者能欺騙,說謊,但一般情況下服務器都能檢測到。
MAIL FROM 命令中指定的地址是發件人地址
RCPT TO 標識單個的郵件接收人;可有多個 RCPT TO;常在 MAIL 命令後面。
DATA 在單個或多個 RCPT 命令後,表示所有的郵件接收人已標識,並初始化數據傳輸,以 CRLF.CRLF 結束
VRFY 用於驗證指定的用戶/郵箱是否存在;由於安全方面的原因,服務器常禁止此命令
EXPN 驗證給定的郵箱列表是否存在,擴充郵箱列表,也常被禁用
HELP 查詢服務器支持什麼命令
NOOP 無操作,服務器應響應 OK
RSET 重置會話,當前傳輸被取消
QUIT 結束會話

連接到 Postfix 使用 SMTP 命令發送郵件
例如:安裝 Postfix 的郵件服務器IP是192.168.0.1 (藍色字體內容由客戶端輸入,紅色字體內容是服務返回的)

telnet 192.168.0.1 25 ------------------------------------------------- 使用 telnet 命令連接服務器 25 端口
helo test.com -----------------------------------------------------------向服務器標識用戶身份發送 mail from 命令
ehlo test.com ----------------------------------------------------------- ESMTP 命令,發信需要認證。
auth login ----------------------------------------------------------------進行用戶身份認證
334 VXNlcm5hbWU6
Y29zdGFAYW1heGl0Lm5ldA== ----------------------------------- BASE64 加密後的用戶名
334 UGFzc3dvcmQ6
MTk4MjIxNA== -------------------------------------------------------- BASE64 加密後的密碼
235 authentication successfully -------------------------------- 身份認證成功
(535 authentication failed --------------------------------- ------身份認證失敗)
發到本系統中域名下的賬戶可跳過身份認證。
mail from: <[email protected]> ---------------------------- mail from 地址 [email protected]
250 ok ----------------------------------------------------- ----------命令執行成功
rcpt to: <[email protected]> -------------------------------- 遞送給地址 [email protected]
250 ok ----------------------------------------------------- ----------命令執行成功
data ------------------------------------------------------- -----------數據傳輸初始化
354 End data with .----------------------------------------- -----開始傳輸數據
From: [email protected]
To: [email protected]
Date: Mon, 25 Oct 2004 14:24:27 +0800
Subject: test mail

Hi, test2
This is a test mail, you don't reply it.

.
------------------------------------------------------------ 數據內容,包括BASE64加密後的郵件內容, 以 CRLF.CRLF 結束數據傳輸
250 OK: queued as 2F6DE3929--------------------------------- 命令執行成功
quit ------------------------------------------------------- 結束會話
221 Bye
Connection closed by foreign host .------------------------- 斷開連接

以上就是一個郵件發送的基本的命令。

再說一下郵件發送的基本過程:

如果你的郵件地址是[email protected],而你要用這個郵箱發送一封郵件到[email protected],你需要連接到服務器host.com上,當然這個連接可能需要認證,現在基本上都要驗證,然後是發送郵件到服務器host.com上,關閉連接。在host.com上,你所發送的郵件進入發送隊列中,輪到你要發送的郵件時,host.com主機再聯繫tohost.com,將郵件傳輸到服務器tohost.com上。

 二:實例應用

-----------------------------------------------------------------------------------------------------------------------

MailMessage.java

----------------------------------------

//這個類其實就是一個基本的JavaBean,用於完成一些基本信息的設置,也可以不要這個東西,直接在程序中寫明就可以,不過這樣條理較清楚一些,而且修改也方便一些.

package mail;

public class MailMessage {
 
 private String from;
 private String to;
 private String datafrom;
 private String datato;
 private String subject;
 private String content;
 private String date;
 private String user;
 private String password;

 public String getPassword() {
  return password;
 }

 public void setPassword(String password) {
  this.password = password;
 }

 public String getUser() {
  return user;
 }

 public void setUser(String user) {
  this.user = user;
 }

 public String getContent() {
  return content;
 }

 public void setContent(String content) {
  this.content = content;
 }

 public String getDatafrom() {
  return datafrom;
 }

 public void setDatafrom(String datafrom) {
  this.datafrom = datafrom;
 }

 public String getDatato() {
  return datato;
 }

 public void setDatato(String datato) {
  this.datato = datato;
 }

 public String getDate() {
  return date;
 }

 public void setDate(String date) {
  this.date = date;
 }

 public String getFrom() {
  return from;
 }

 public void setFrom(String from) {
  this.from = from;
 }

 public String getSubject() {
  return subject;
 }

 public void setSubject(String subject) {
  this.subject = subject;
 }

 public String getTo() {
  return to;
 }

 public void setTo(String to) {
  this.to = to;
 }

}

---------------------------------------------

SMTPClient .java

------------------------------

//主要的功能就在這裏面完成了

package mail;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.StringTokenizer;

import sun.misc.BASE64Encoder;

public class SMTPClient {

 private boolean debug=true;
 BASE64Encoder encode=new BASE64Encoder();//用於加密後發送用戶名和密碼
 public static void main(String[] args) throws UnknownHostException, IOException {
  // TODO Auto-generated method stub
  MailMessage message=new MailMessage();
  message.setFrom("mailto:[email protected]%22);//發件人
  message.setTo("mailto:[email protected]%22);//收件人
  String server="smtp.163.com";//郵件服務器
  message.setSubject("test");//郵件主題
  message.setContent("test");//郵件內容
  message.setDatafrom("mailto:[email protected]%22);//發件人,在郵件的發件人欄目中顯示
  message.setDatato("mailto:[email protected]%22);//收件人,在郵件的收件人欄目中顯示
  message.setUser("wasingmon");//登陸郵箱的用戶名
  message.setPassword("");//登陸郵箱的密碼
  
  SMTPClient smtp=new SMTPClient(server,25);
  boolean flag;
  flag=smtp.sendMail(message,server);
  if(flag){
   System.out.println("郵件發送成功!");
  }
  else{
   System.out.println("郵件發送失敗!");
  }

 }
 private Socket socket;
 public SMTPClient(String server,int port) throws UnknownHostException, IOException{
  try{
   socket=new Socket(server,25);
  }catch(SocketException e){
   System.out.println(e.getMessage());
  }catch(Exception e){
   e.printStackTrace();
  }finally{
   System.out.println("已經建立連接!");
  }

 }
 //註冊到郵件服務器
 public void helo(String server,BufferedReader in,BufferedWriter out) throws IOException{
  int result;
  result=getResult(in);
  //連接上郵件服務後,服務器給出220應答
  if(result!=220){
   throw new IOException("連接服務器失敗");
  }
  result=sendServer("HELO "+server,in,out);
  //HELO命令成功後返回250
  if(result!=250)
  {
   throw new IOException("註冊郵件服務器失敗!");
  }
 }
 
 private int sendServer(String str,BufferedReader in,BufferedWriter out) throws IOException{
  out.write(str);
  out.newLine();
  out.flush();
  if(debug)
  {
   System.out.println("已發送命令:"+str);
  }
  return getResult(in);
 }
 public int getResult(BufferedReader in){
  String line="";
  try{
   line=in.readLine();
   if(debug){
    System.out.println("服務器返回狀態:"+line);
   }
  }catch(Exception e){
   e.printStackTrace();
  }
  //從服務器返回消息中讀出狀態碼,將其轉換成整數返回
  StringTokenizer st=new StringTokenizer(line," ");
  return Integer.parseInt(st.nextToken());
 }
 
 public void authLogin(MailMessage message,BufferedReader in,BufferedWriter out) throws IOException{
  int result;
  result=sendServer("AUTH LOGIN",in,out);
  if(result!=334){
   throw new IOException("用戶驗證失敗!");
  }
  
   result=sendServer(encode.encode(message.getUser().getBytes()),in,out);
   if(result!=334){
   throw new IOException("用戶名錯誤!");
   }
   result=sendServer(encode.encode(message.getPassword().getBytes()),in,out);
  
   if(result!=235){
    throw new IOException("驗證失敗!");
  }
 }
 //開始發送消息,郵件源地址
 public void mailfrom(String source,BufferedReader in,BufferedWriter out) throws IOException{
  int result;
  result=sendServer("MAIL FROM:<"+source+">",in,out);
  if(result!=250){
   throw new IOException("指定源地址錯誤");
  }
 }
 // 設置郵件收件人
 public void rcpt(String touchman,BufferedReader in,BufferedWriter out) throws IOException{
  int result;
  result=sendServer("RCPT TO:<"+touchman+">",in,out);
  if(result!=250){
   throw new IOException("指定目的地址錯誤!");
  }
 }
 
 //郵件體
 public void data(String from,String to,String subject,String content,BufferedReader in,BufferedWriter out) throws IOException{
  int result;
  result=sendServer("DATA",in,out);
  //輸入DATA回車後,若收到354應答後,繼續輸入郵件內容
  if(result!=354){
   throw new IOException("不能發送數據");
  }
  out.write("From: "+from);
  out.newLine();
  out.write("To: "+to);
  out.newLine();
  out.write("Subject: "+subject);
  out.newLine();
  out.newLine();
  out.write(content);
  out.newLine();
  //句號加回車結束郵件內容輸入
  result=sendServer(".",in,out);
  System.out.println(result);
  if(result!=250)
  {
   throw new IOException("發送數據錯誤");
  }
 }
 
 //退出
 public void quit(BufferedReader in,BufferedWriter out) throws IOException{
  int result;
  result=sendServer("QUIT",in,out);
  if(result!=221){
   throw new IOException("未能正確退出");
  }
 }
 
 //發送郵件主程序
 public boolean sendMail(MailMessage message,String server){
  try{
   BufferedReader in=new BufferedReader(new InputStreamReader(socket.getInputStream()));
   BufferedWriter out=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
   helo(server,in,out);//HELO命令
   authLogin(message,in,out);//AUTH LOGIN命令
   mailfrom(message.getFrom(),in,out);//MAIL FROM
   rcpt(message.getTo(),in,out);//RCPT
   data(message.getDatafrom(),message.getDatato(),message.getSubject(),message.getContent(),in,out);//DATA
   quit(in,out);//QUIT
  }catch(Exception e){
   e.printStackTrace();
   return false;
   
  }
  return true;
 }

}

因爲現在一般SMTP服務器都需要SMTP驗證,所以本例子中也加入了這個驗證,要不然郵件時發不出去的(剛開始我就這樣)。

END!

 

http://www.cnblogs.com/haifeng71108409/archive/2009/12/27/1633301.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章