邮件发送程序设计
网络程序设计的精髓是什么?
就是客户端和服务器的对话和响应契约(协议)。 简单邮件传输协议SMTP可以很好地诠释这一点。
邮件传输协议包括SMTP(简单邮件传输协议,RFC821)及其扩充协议MIME;
邮件接收协议包括POP3和功能更强大的IMAP协议。
服务邮件发送的服务器其端口为25(开启ssl可能使用465或587端口),服务邮件接收的服务器端口为110。
邮箱设置一定要开启smtp/pop3服务,目前大部分邮箱开启服务时,需要设置第三方客户端使用的授权码,该授权码就是代替密码使用,目的是防止密码泄露, qq邮箱、163邮箱等均改成了这种使用方式,例如图7.1所示。
图7.1 QQ邮箱的授权码
一、 程序设计第一步:准备一段BASE64编码程序
新建一个Java包,建议包名:chapter07,并新建BASE64.java程序。
按SMTP过程,要成功地发送和接收邮件,用户名和密码需要BASE64编码后才能有效传输,这个类就是完成这个需求:
public class BASE64 {
public static void main(String[] args) {
String userName="你的完整邮箱名";
String authCode = "你的邮箱授权码";
//显示邮箱名的base64编码结果
System.out.println(encode(userName));
//显示授权码的base64编码结果
System.out.println(encode(authCode));
}
public static String encode(String str) {
return new sun.misc.BASE64Encoder().encode(str.getBytes());
}
}
注意:
(1)若用户密码验证过程中出现失败或提示需要开启SMTP终端服务,则通过浏览器的 web方式进入你的邮箱,更改邮箱的属性设置–>帐户,勾选或开启支持SMTP客户端软件;
(2)对于QQ邮箱还需要先设置独立密码,然后才能开启支持SMTP客户端软件(这个工作要提前一周做)。
二、telnet手动体验SMTP协议过程1
操作系统默认没有安装telnet,可以通过控制面板=》程序和功能=》启用或关闭windows功能中选择telnet客户端进行安装。安装后,打开操作系统的命令行窗口,键入:telnet smtp.qq.com 25(完成三次握手连接),然后完成如下的对话过程,其中加粗加亮字体是smtp协议支持的命令,必须输入一致,其余内容根据具体情况填写,双斜杠内容是注释,不用输入:
HELO hostname //hostname可以是IP或其他随意别名
AUTH LOGIN
//在输入auth login回车后,先粘贴哟个BASE64程序编码的完整邮箱名并回车;再粘贴编码的授权码并回车
MAIL FROM:<your mail address> //在这里填写自己的邮箱地址,用于发送邮件(注意冒号后面别有空格,建议使用注册时的邮箱地址)
RCPT TO:<your mail address> //接收方的邮箱,在这里暂时填写和上面一样的邮箱地址,即自己发送邮件给自己,验证是否成功
DATA //在输入data回车后,接下来开始发送邮件头相关内容
Subject: this is simple mail // 邮件的标题
//在这里再多发送一行空行,来分隔邮件内容,下面就是邮件正文内容
Hello!
很好!
//控制台中中文会变成?的乱码,但不影响接受者接收到中文内容
. //在邮件正文发送完毕后,单独用一行输入一个小圆点,作为结束标志,然后回车
QUIT //结束通信(含4次握手断开)
知识点:以上基于命令行窗口的邮件发送方法,可以让你不需要其他软件的帮助就可以完成简单邮件的发送与接收。
三、程序设计第二步:体验SMTP协议过程2
现在用自己的程序代替telnet,来验证smtp发送给邮件的过程:
(1)将第3讲原有的TCPClient.java程序复制重构到chapter07程序包下;
(2)将第3讲TCPClientThreadFX.java对话窗口程序,复制到chapter07程序包下,并重构命名为TCPMailClientFX.java,并添加针对用户名和密码的Base64编码功能;
(3)运行TCPMailClientFX.java程序,ip文本框填写smtp.qq.com,端口文本框填写25,连接后按照如下的对话顺序通过send按钮发送命令,注意,和第一次体验有所不同,增加了发件人信息和收件人信息:
HELO hostname //hostname可以是IP或其他随意别名
AUTH LOGIN
//发送auth login后,先发送Base64编码的邮箱名,然后再发送Base64编码的授权码
MAIL FROM:<your mail address> //在这里填写《互联网程序设计》实验注册的邮箱地址,用于发送邮件(注意冒号后面别有空格)
RCPT TO:<other mail address> //接收方的邮箱,在这里用自己另外的邮箱或同学的邮箱地址测试。
DATA //在输入data回车后,接下来开始设置邮件头,邮件头除了邮件标题,还包括发件人地址和收件人地址
From:@qq.com // 发件人的邮箱地址
Subject: 给同学的一封测试邮件 // 邮件的标题
To:@qq.com //会显示在邮件接收者栏上
// 在这里再多发送一行空行,来分隔邮件内容,下面是邮件正文内容
I am 同学 //邮件正文内容
. //在邮件正文发送完毕后,单独发送一个小圆点,作为结束标志
QUIT //发送quit结束通信(含4次握手断开)
用自己的图形界面进行smtp交互发送邮件,是否比telnet方便?
注意:
判断邮件是否成功发送,可查看你“已发送”箱中该邮件的发送状态或是否有发送失败的提示或收到自动回复邮件(仅供参考)。
四、窗口程序实现自动发邮件
(1)设置如图7.2所示的邮件发送窗口(供参考),并命名为TCPMailClientFX2.java,将手动操作的SMTP过程封装到程序中,实现邮件自动发送,注意不要短时间频繁发送,会被邮件服务器拒绝smtp服务。
图7.2 邮件发送窗口
(2)将TCPClient.java复制重构,并命名为TCPMailClient.java,并修改其中的发送方法如下:
public void send(String msg) {
//输出字符流,由Socket调用系统底层函数,经网卡发送字节流
pw.println(msg);
try {
//进行邮件交互、发送smtp指令之间应该暂停一段时间
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
注意:邮件命令需要一行一行地发送,使用程序自动发送过程中需要设置一定的睡眠等待时间。
(3)“发送按钮”的部分代码如下:
String smtpAddr = tfSmtpAddr.getText().trim();
String smtpPort = tfSmtpPort.getText().trim();
try {
tcpMailClient = new TCPMailClient(smtpAddr, smtpPort);
tcpMailClient.send("HELO myfriend");
tcpMailClient.send("AUTH LOGIN");
String username = "你的完整邮箱地址";
String authCode = "申请的授权码";
String msg = BASE64Encoder.encode(username);
tcpMailClient.send(msg);
msg = BASE64Encoder.encode(authCode);
tcpMailClient.send(msg);
msg = "MAIL FROM:<" + tfSenderAddr.getText().trim() + ">";
tcpMailClient.send(msg);
msg = "RCPT TO:<" + tfRecieverAddr.getText().trim() + ">";
tcpMailClient.send(msg);
msg = "DATA";
tcpMailClient.send(msg);
msg = "FROM:" + tfSenderAddr.getText().trim();
tcpMailClient.send(msg);
完整代码实现
TCPClientFX
package chapter07;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.*;
import java.net.Socket;
import java.util.Optional;
public class TCPClientFX extends Application {
private Button btnExit = new Button("退出");
private Button btnSend = new Button("发送");
private Button btnOpen = new Button("加载");
private Button btnSave = new Button("保存");
private Button btnConnect = new Button("连接");
//待发送信息的文本框
private TextField inputField = new TextField("这是邮件内容");
private TextField from = new TextField("2812329425@qq.com");
private TextField to = new TextField("1987151116@qq.com");
private TextField subject = new TextField("邮件标题");
//显示信息的文本区域
private TextArea receiveArea = new TextArea();
//接受ip地址的单行文本框
private TextField ipAddressField = new TextField("smtp.qq.com");
//接受port的单行文本框
private TextField ipPortField = new TextField("25");
// private static TCPClient tcpClient;
private Socket socket;
private BufferedReader bufferedReader;
private PrintWriter printWriter;
private TCPMailClient tcpMailClient;
public void start(Stage primaryStage) throws IOException {
// primaryStage.setTitle("登录查看成绩");
BorderPane mainPane = new BorderPane();
//输入区域
VBox vBox = setVBoxArea();
mainPane.setCenter(vBox);
//底部按钮区域
HBox hBox = setHBoxArea();
mainPane.setBottom(hBox);
//顶部ip相关区域
HBox hBox1 = setTopIpArea();
mainPane.setTop(hBox1);
btnExitAction();
btnSendAction();
btnOpenAction(primaryStage);
btnSaveAction(primaryStage);
btnConnectAction()