2022年邮件客户机smtp编程计算机网络 .pdf
作业 2:邮件客户机作业名 : 邮件客户机编程环境: Elicpse 3.5 , jdk1.7 报告分为 4 部分,分别是界面布局, 发送邮件的具体处理, 其他监听事件, 相关类的注意事项。(一) 界面布局:主 界 面 使 用 BorderLayout 布 局 界 面 管 理 器 进 行 布 局 , 把 子 容 器 分 别 用BorderLayout.NORTH, BorderLayout.CENTER, BorderLayout.SOUTH加入。getContentPane().setLayout(new BorderLayout(); getContentPane().add(pp1,BorderLayout.NORTH); getContentPane().add(pp3,BorderLayout.CENTER); getContentPane().add(pp2,BorderLayout.SOUTH);3 个子容器为 pp1,pp2,pp3,依次对三个子容器进行空间布局。在 pp1中, 主要包括用户账号 (Username), 密码(Password), 发送方邮箱地址 (From,接收方邮箱地址 (To),邮件主题 (Subject)以及文本框标识( Message) 。From =new JLabel(From); To=new JLabel(To); Subject=new JLabel(Subject); Message=new JLabel(Message); Username=new JLabel(Username); Password=new JLabel(Password); 对控件初始化后,为了让布局空间美观,可以把每行另设一子容器进行布局。JPanel p1 =new JPanel(); /邮件发送方 From 的容器J Panel p2 =new JPanel(); /邮件接收方 To的容器J Panel p3 =new JPanel(); /邮件主题 Subject的容器J Panel p4 =new JPanel(); /正文标识 Message的容器J Panel p5_1 =new JPanel(); /用户账号 Username的容器J Panel p5_2=new JPanel(); /用户密码 Password的容器J Panel p5=new JPanel(); /承载 p5_1 ,P5_2 的容器然后就是往这些子容器中添加控件,完成位于主容器 BorderLayout.NORTH 的子容器们的布局p1.add(From ,BorderLayout.WEST); p1.add(Text_from,BorderLayout.CENTER); p2.add(To,BorderLayout.WEST); p2.add(Text_to,BorderLayout.CENTER); p3.add(Subject,BorderLayout.WEST); p3.add(Text_subject,BorderLayout.CENTER); p4.add(Message ,BorderLayout.WEST); 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 1 页,共 15 页 - - - - - - - - - p5_1.add(Username,BorderLayout.WEST); p5_1.add(Text_username,BorderLayout.CENTER); p5_2.add(Password,BorderLayout.WEST); p5_2.add(psw ,BorderLayout.CENTER); p5.add(p5_1); p5.add(p5_2); 最后,把它们添加到 pp1中,pp1.setLayout(new GridLayout(5,1); /在主容 BorderLayout.NORTH的布局子容器中,按表格序添加控件pp1.add(p5); pp1.add(p1); pp1.add(p2); pp1.add(p3); pp1.add(p4); 在 pp2 中,主要处理主容器位于BorderLayout.SOUTH的布局,其中就是3 个按钮,发送( Send ) ,清屏( Clear) ,退出( Quit) ,pp2.setLayout(new GridLayout(1,3); /在主容器BorderLayout.SOUTH的布局子容器中,按表格序添加控件pp2.add(Send ); pp2.add(Clear); pp2.add(Quit); 在 pp3 中, 只有 1 个控件,就是正文区,位于主容器的 BorderLayout.CENTER 中。此控件为 TextField型,为了避免当输入文字超出文本框的现实范围的情况,因此设置了可拉动的滚动条于此TextField控件中,Text_message.setLineWrap(true); /设置文本控件属性为可以线性滚动Text_message.setWrapStyleWord(true); /设置文本控件属性为可以拖动查看文字Text_message.setEditable(true); /设置文本内容可见s croll = new JScrollPane(Text_message); /当文本内容超过文本控件控件大小时,用JScrollPane可通过滚动条拉动/文本框,查阅文本全部内容pp3.add(scroll,BorderLayout.CENTER); /把文本添加进位于主布局的 BorderLayout.CENTER位置中子控件pp3pp3.add(newJScrollBar(JScrollBar.HORIZONTAL ),BorderLayout.SOUTH); /添加水平滚动条pp3.add(newJScrollBar(JScrollBar.VERTICAL ),BorderLayout.EAST); /添加垂直滚动条最后,弄完 pp13 了,仅需放入继承了 Frame 的主布局中, 并设置好主布局的一些属性名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 2 页,共 15 页 - - - - - - - - - getContentPane().setLayout(new BorderLayout(); /把3个子控件添加进主布局的三个位置中getContentPane().add(pp1,BorderLayout.NORTH); getContentPane().add(pp3,BorderLayout.CENTER); getContentPane().add(pp2,BorderLayout.SOUTH); 最终结果如下图:(二)发送邮件的具体处理在本程序中,此部分也是作为按钮Send的监听响应处理事件的,目的是按下按钮后,即可完成邮件的发送用 tcp 实现邮件的发送,则必须用套接字在smtp 协议下传送一些特殊的 “ 交流”字段,具体流程如下图:名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 3 页,共 15 页 - - - - - - - - - 在套接口的连接上,首先要解决的是ip,port。我以 163 邮箱为例子来说明。作为用户的邮件代理,我们的套接口要与163 的 smtp 服务器沟通,把邮件发送方,邮件接收方, 及其标题、 正文等信息发送给服务器,完成如上面那张图的流程即可。剩下具体如何识别邮件地址、 如何发送正文这些事就交给服务器来做了。因此,查了下其实各个邮箱的smtp 服务器都是字符串 “smtp”加上邮箱域名,如163 就是 。然后就是 port,为 25。因此,套接口的ip,port 即可解决,只要提取发送方之后的域名,做个连接即可。port =25; /SMTP协议端口号i nt i=String_from.indexOf( ); /从 From 中提取 后边的字符串,即邮箱域名,如 String s=String_from.substring(i+1); Socket socket=new Socket(host.concat(s),port); /host为 smtp ,连接上域名之后,即smtp服务器,如DNS的事 Java已为我们做好了。可是,还有一个问题,那就是对于smtp 服务器来说,需要对用户名及密码的验证通过了才能对代理的邮件进行转发。因此,要把发送方邮箱的用户名、密码发送过去。而且,在发送之前,必须进过base64的编码才可以这里有两种办法, 一种是引用系统的 sun 里边的 Base64的类的 encode 方法,我选择的是自己写个Base64_Encode类,用类的 encode方法实现编码(此类详细代码见后边的源码)名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 4 页,共 15 页 - - - - - - - - - 准备事宜做好了,接下来只需按流程图向smtp 服务器进行 “ 交流” 了先创建套接字的输入、输出流BufferedReader in = new BufferedReader(newInputStreamReader(socket.getInputStream(); /套接字输出流PrintStream out =new PrintStream(socket.getOutputStream(); /套接字输入流接着, 用输入流向 smtp服务器输送字符串,这些字符串包含了 smtp协议的命令:HELO,MAIL,FROM,RCPT TO,DATA,QUIT String from_server = in.readLine(); /返回 220 ,读取建立连接后smtp 服务器的返回信息System.out .println(HELO + String_from); out.print(HELO + String_from +rn); /发送 HELO给服务器from_server = in.readLine(); /返回 250 System.out .println(from_server); out.print(RSET +rn); from_server = in.readLine(); /返回 250 ,验证账号前的重置 System.out .println(from_server); out.print(AUTH LOGIN+rn); from_server = in.readLine(); /返回 334 ,告诉服务器接下来发送的是账号,密码信息 System.out .println(Login+from_server); out.print(enc .encode(username.getBytes()+rn); /必须用base64进行编码后发送给服务器 from_server = in.readLine(); /返回 334 System.out .println(username+from_server); out.print(enc .encode(password.getBytes()+rn); /必须用 base64进行编码后发送给服务器 from_server = in.readLine(); System.out .println(password+from_server); / 返回 250 System.out .println(Mail From: ); out.print(MAIL From: +rn); /发送邮件发送方的邮箱 from_server = in.readLine(); System.out .println(from_server); /返回 250名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 5 页,共 15 页 - - - - - - - - - System.out .println(RCPT TO: ); out.print(RCPT TO: +rn); /发送邮件接收方邮箱 from_server = in.readLine(); /返回 250 System.out .println(from_server); System.out .println(DATA ); out.print(DATA +rn); /告诉服务器, 接下来要发送正文内容了 from_server = in.readLine(); System.out .println(from_server); /返回 354 System.out .println(.); out.print(Text_message+rn); out.print(.+rn); /发送 “. ” ,表示正文发送结束 from_server = in.readLine(); /返回 250 System.out .println(from_server); System.out .println(QUIT); out.print(QUIT+rn); /告诉服务器,要退出了 from_server = in.readLine(); System.out .println(from_server); /返回 221 (三)其他监听事件清屏( Clear)按钮的监听响应事件,Clear.addActionListener(new ActionListener() publicvoid actionPerformed(ActionEvent e) Text_from.setText( ); Text_to.setText( ); Text_subject.setText( ); Text_message.setText( ); Text_username.setText( ); Text_password.setText( ); ); 这要把相关文本置为空字符串(“”)即可实现清屏退出( Quit )按钮的监听响应事件:Quit.addActionListener(new ActionListener() publicvoid actionPerformed(ActionEvent e) System.exit(0) ; 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 6 页,共 15 页 - - - - - - - - - ); 调用系统函数完成程序正常运行时,截图如下这是 cs 双方“对话”过程(四)相关类的注意事项首先,可能在 JDK中不支持 mail 类,所以要自己去下载mail 类包后,导入项目,然后 import javax.mail.* 才可以。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 7 页,共 15 页 - - - - - - - - - 如果编完代码, 调试无误却始终无法发送邮件, 那么,有可能是邮箱未开启smtp服务,应该先进入邮箱开启此项服务若依旧无法成功发送, 那么稍等几小时再试一试, 可能是网络或是邮箱服务代理有点问题验证发送方的用户名及密码, 一定要编码之后发送才可验证成功,还有就是用户名是完整邮箱用户名,如 以下为源代码:import java.awt.*; import java.io.*; import .*; import java.awt.event.*; import java.util.Properties; import javax.mail.*; import javax.swing.*; publicclass MailClient public MailClient() new Frame(); publicstaticvoid main(String args) new MailClient(); class Frame extends JFrame privatestaticfinallongserialVersionUID = 1L; private JButton Send ; private JButton Clear; private JButton Quit; private JTextArea Text_message; private JTextField Text_from; private JTextField Text_to; private JTextField Text_subject; private JTextField Text_username; private JTextField Text_password; private JPasswordField psw ; private JLabel From ; private JLabel To; private JLabel Subject; 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 8 页,共 15 页 - - - - - - - - - private JLabel Message ; private JLabel Username; private JLabel Password; privateintport; Base64_Encode enc ; String username; String password; static String host=smtp.; JScrollPane scroll ; public Frame() i nitComponents(); Send .addActionListener(new ActionListener() /Send按钮的监听事件publicvoid actionPerformed(ActionEvent e) try Send_listener(e); catch (IOException e1) / TODO Auto-generated catch blocke1.printStackTrace(); ); Clear.addActionListener(new ActionListener() /Clear按钮的监听事件publicvoid actionPerformed(ActionEvent e) Text_from.setText( ); Text_to.setText( ); Text_subject.setText( ); Text_message.setText( ); Text_username.setText( ); Text_password.setText( ); ); Quit.addActionListener(new ActionListener() /Quit按钮的监听事件publicvoid actionPerformed(ActionEvent e) 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 9 页,共 15 页 - - - - - - - - - System.exit(0) ; ); privatevoid initComponents() /主要负责控件的布局/ /p1,p2,p3为主布局的北,中,南三个位置Text_message = new JTextArea(); Text_from = new JTextField(); Text_to = new JTextField(); Text_subject = new JTextField(); Text_username = new JTextField(); Text_password = new JTextField(); Send = new JButton(Send); Clear = new JButton(Clear); Quit = new JButton(Quit); From =new JLabel(From); To=new JLabel(To); Subject=new JLabel(Subject); Message=new JLabel(Message); Username=new JLabel(Username); Password=new JLabel(Password); enc =new Base64_Encode(); psw=new JPasswordField(); J Panel p1 =new JPanel(); J Panel p2 =new JPanel(); J Panel p3 =new JPanel(); J Panel p4 =new JPanel(); J Panel p5_1 =new JPanel(); J Panel p5_2=new JPanel(); J Panel p5=new JPanel(); p1.setLayout(new BorderLayout(); p2.setLayout(new BorderLayout(); p3.setLayout(new BorderLayout(); p4.setLayout(new BorderLayout(); p5_1.setLayout(new BorderLayout(); p5_2.setLayout(new BorderLayout(); p5.setLayout(new GridLayout(1,2); p1.add(From ,BorderLayout.WEST); p1.add(Text_from,BorderLayout.CENTER); 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 10 页,共 15 页 - - - - - - - - - p2.add(To,BorderLayout.WEST); p2.add(Text_to,BorderLayout.CENTER); p3.add(Subject,BorderLayout.WEST); p3.add(Text_subject,BorderLayout.CENTER); p4.add(Message ,BorderLayout.WEST); p5_1.add(Username,BorderLayout.WEST); p5_1.add(Text_username,BorderLayout.CENTER); p5_2.add(Password,BorderLayout.WEST); p5_2.add(psw ,BorderLayout.CENTER); p5.add(p5_1); p5.add(p5_2); J Panel pp1 =new JPanel( ); pp1.setLayout(new GridLayout(5,1); pp1.add(p5); pp1.add(p1); pp1.add(p2); pp1.add(p3); pp1.add(p4); J Panel pp2 =new JPanel(); pp2.setLayout(new GridLayout(1,3); pp2.add(Send ); pp2.add(Clear); pp2.add(Quit); J Panel pp3 =new JPanel(); pp3.setLayout(new BorderLayout(); Text_message.setLineWrap(true); /设置文本控件属性为可以线性滚动Text_message.setWrapStyleWord(true); /设置文本控件属性为可以拖动Text_message.setEditable(true); /设置文本内容可见s croll = new JScrollPane(Text_message); /当文本内容超过文本控件控件大小时,用JScrollPane可通过滚动条拉动pp3.add(scroll,BorderLayout.CENTER); pp3.add(newJScrollBar(JScrollBar.HORIZONTAL ),BorderLayout.SOUTH); pp3.add(new名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 11 页,共 15 页 - - - - - - - - - JScrollBar(JScrollBar.VERTICAL ),BorderLayout.EAST); getContentPane().setLayout(new BorderLayout(); getContentPane().add(pp1,BorderLayout.NORTH); getContentPane().add(pp3,BorderLayout.CENTER); getContentPane().add(pp2,BorderLayout.SOUTH); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); setTitle(Java Mailclient); setLocationByPlatform(true); setMinimumSize(new Dimension(180, 150); setResizable(false); setVisible(true); setSize(600,400); privatevoid Send_listener(ActionEvent evt) throws IOException / Send按钮的监听响应事件,是本程序的发送邮件的核心 String String_subject=Text_subject.getText(); String String_from=Text_from.getText(); String String_to=Text_to.getText(); username=Text_username.getText(); password=psw .getPassword().toString(); port =25; /SMTP协议端口号i nt i=String_from.indexOf( ); /从From 中提取 后边的字符串, 即邮箱域名,如 String s=String_from.substring(i+1); Socket socket=new Socket(host.concat(s),port); /host为smtp ,连接上域名之后,即smtp 服务器,如 BufferedReader in = new BufferedReader(newInputStreamReader(socket.getInputStream(); /套接字输出流PrintStream out =new PrintStream(socket.getOutputStream(); /套接字输入流i f (in=null | out=null) System.out .println(Faile to open the stream); return ; 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 12 页,共 15 页 - - - - - - - - - String from_server = in.readLine(); /返回 220 ,读取建立连接后smtp服务器的返回信息System.out .println(from_server); System.out .println(HELO + String_from); out.print(HELO + String_from +rn); /发送 HELO给服务器 from_server = in.readLine(); /返回 250 System.out .println(from_server); out.print(RSET +rn); from_server = in.readLine(); /返回 250 ,验证账号前的重置 System.out .println(from_server); out.print(AUTH LOGIN+rn); from_server = in.readLine(); /返回 334 ,告诉服务器接下来发送的是账号,密码信息 System.out .println(Login+from_server); out.print(enc .encode(username.getBytes()+rn); /必须用base64进行编码后发送给服务器 from_server = in.readLine(); /返回 334 System.out .println(username+from_server); out.print(enc .encode(password.getBytes()+rn); /必须用base64进行编码后发送给服务器 from_server = in.readLine(); System.out .println(password+from_server); /返回 250 System.out .println(Mail From: ); out.print(MAIL From: +rn); /发送邮件发送方的邮箱 from_server = in.readLine(); System.out .println(from_server); /返回 250 System.out .println(RCPT TO: ); out.print(RCPT TO: +rn); /发送邮件接收方邮箱 from_server = in.readLine(); /返回 250 System.out .println(from_server); System.out .println(DATA ); out.print(DATA +rn); /告诉服务器,接下来要发送正文内容 from_server = in.readLine(); System.out .println(from_server); /返回 354名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 13 页,共 15 页 - - - - - - - - - System.out .println(.); out.print(Text_message+rn); out.print(.+rn); /发送 “. ” ,表示正文发送结束 from_server = in.readLine(); /返回 250 System.out .println(from_server); System.out .println(QUIT); out.print(QUIT+rn); /告诉服务器,要退出了 from_server = in.readLine(); System.out .println(from_server); /返回 221if(from_server.matches(221*); JOptionPane.showMessageDialog( null, Mail sends successful!); class Base64_Encode privatestaticchar codec_table = A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W , X, Y, Z, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +, / ; public Base64_Encode() public String encode(byte a) int totalBits = a.length * 8; int nn = totalBits % 6; int curPos = 0;/ process bits StringBuffer toReturn = new StringBuffer(); while (curPos 2); break; case 2: 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 14 页,共 15 页 - - - - - - - - - toReturn.append(codec_table(abytePos & 0 x3f); break; case 4: if (bytePos = a.length - 1) toReturn .append(codec_table(abytePos & 0 x0f) 2) & 0 x3f); else int pos = (abytePos & 0 x0f) 6) & 0 x3f; toReturn.append(codec_tablepos); break; case 6: if (bytePos = a.length - 1) toReturn .append(codec_table(abytePos & 0 x03) 4) & 0 x3f); else int pos = (abytePos & 0 x03) 4) & 0 x3f; toReturn.append(codec_tablepos); break; default: break; curPos+=6; if(nn=2) toReturn.append(=); elseif(nn=4) toReturn.append(= ); return toReturn.toString(); 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 15 页,共 15 页 - - - - - - - - -