本文共 10060 字,大约阅读时间需要 33 分钟。
这是一个很无聊的作业。大约花了我一天的时间。
介绍下实验工具 邮件服务器用winmail 编程工具用Eclipse 键盘是cherry的红轴机械键盘高键位(不伤手,用立白) 桌子是自由升降桌(站着编程,有利于身体骨骼健康)首先为了便于程序之间的数据交换,要先设计几个结构体
分别是 GlobalVar 用于存储全局数据,如服务器地址,端口号 UserInfo 存储用户的用户名和密码 GetMailInfo存储接受的邮件信息 MailList 建立一个链表,存储从服务器得来的所有的邮件,里面没一个数据是一个GetMailInfo SendMailInfo发送邮件的数据结构public class GlobalVar { public static String serverHost="192.168.153.133"; public static int SMTPPort=25; public static int POP3Port=110;}public class GetMailInfo { public String received_from; public String subject; public String content;}public class SendMailInfo { public String serverHost="192.168.153.132"; public int serverPort=25; public String mail_from; public String rcpt_to; public String subject; public String from; public String content;}public class UserInfo { public String serverHost; public int serverPort; public String user; public String password;}
mailList.java
import java.util.LinkedList;public class MailList { LinkedListmails; public MailList() { mails=new LinkedList (); } public void putMail(GetMailInfo mail) { mails.push(mail); } public GetMailInfo getMail() { return mails.pop(); } public int mailCount() { return mails.size(); }}
软件的架构。
采用了一定的分层架构。当然不是MVC。因为我并没有把软件的输入和程序的逻辑分开,这个实在不应该。在处理发送邮件和接受邮件的指令流上,我的设计模式是模板(GetMailTemple和SendMailTemple),向模板类传入适当的数据类(SendMailInfo,UserInfo)我们就能把数据类里面的参数嵌入到模板中。这样在更改指令处理流的时候不用更改参数的传入,减少软件的耦合度。但是如果改变参数,那么模板必须要改变了(不然谁来处理新加入的参数?)数据流向大约是这样的
先来看看 ClientMail的代码
import java.util.Scanner;public class ClientMain { public static void main(String args[]) { Scanner in=new Scanner(System.in); hrer:while(true) { System.out.println("pub a to send a mail,put b to receive a mail,q to quit"); String input=in.nextLine(); switch (input) { case "a": SendMailLogc sendMailLogc=new SendMailLogc(); sendMailLogc.start(); break; case "b": GetMailLogc getMailLogc=new GetMailLogc(); getMailLogc.start(); break; case "q": break hrer; default: break; } } in.close(); }}
非常简单的命令选择。
SendMailLogc主要负责发送邮件的逻辑处理。构建发送邮件的数据结构SendMailInfo。调用处理模板进行Socket命令的发送
import java.util.Scanner;public class SendMailLogc { public void start() { @SuppressWarnings("resource") Scanner in=new Scanner(System.in); SendMailInfo mailTosend=new SendMailInfo(); mailTosend.serverHost=GlobalVar.serverHost; mailTosend.serverPort=GlobalVar.SMTPPort; System.out.println("input your mailAddress"); mailTosend.mail_from=in.nextLine(); System.out.println("intput mailAddress you want to send"); mailTosend.rcpt_to=in.nextLine(); System.out.println("input subject"); mailTosend.subject=in.nextLine(); mailTosend.from=mailTosend.mail_from; System.out.println("now input content ##DONE to quit"); StringBuilder tempStr=new StringBuilder(); String tempStr1; while(true) { tempStr1=in.nextLine(); if(tempStr1.equals("##DONE")) { break; } tempStr.append(tempStr1); } mailTosend.content=tempStr.toString(); SendMailTemple.sendmail(mailTosend); }}
sendMailTemple主要是根据传进来的SendMailInfo与服务器进行通信
里面包括了很多通信的细节,所以要单端列出来。以便处理,这样再更改发送邮件服务器的时候,可以直接更改TEmple。不用更在sendmailLogc。import java.io.BufferedReader;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.net.ConnectException;import java.net.Socket;import javax.naming.spi.DirStateFactory.Result;public class SendMailTemple { public static boolean sendmail(SendMailInfo sendMailInfo) { boolean sendResult=false; try { Socket socket = new Socket(sendMailInfo.serverHost,sendMailInfo.serverPort); PrintWriter output = new PrintWriter(new OutputStreamWriter(socket .getOutputStream())); BufferedReader input = new BufferedReader(new InputStreamReader(socket .getInputStream())); System.out.println(input.readLine()); output.println("helo"); output.flush(); System.out.println(input.readLine()); //发件人 output.println("mail from:"+sendMailInfo.mail_from); output.flush(); System.out.println(input.readLine()); //收件人 output.println("rcpt to:" + sendMailInfo.rcpt_to); output.flush(); System.out.println(input.readLine()); //内容 output.println("data"); output.flush(); System.out.println(input.readLine()); String con = "From:"+sendMailInfo.from+"\r\n"; con += "To: " +sendMailInfo.rcpt_to+ "\r\n"; con = con + "Subject:"+sendMailInfo.subject+"\r\n"; con = con+ "\r\n"; con = con + sendMailInfo.content+"\r\n"; con = con + ".\r\n"; output.print(con); output.flush(); System.out.println(input.readLine()); output.println("quit"); output.flush(); System.out.println(input.readLine()); socket.close(); input.close(); output.close(); System.out.println("Done"); } catch (ConnectException e) { System.out.println("connectException"); } catch (Exception e) { e.printStackTrace(); } return sendResult; }}
以上就是发送一个邮件的过程。我们可以看看读取邮件的过程。。
GetMailLogc就是读取邮件逻辑。包括获取用户名和密码放到UserInfo里面。然后调用处理模板和服务器进行通信,之后在调用显示出来
import java.util.Scanner;public class GetMailLogc { UserInfo user; public void start() { @SuppressWarnings("resource") Scanner in=new Scanner(System.in); user=new UserInfo(); user.serverHost=GlobalVar.serverHost; user.serverPort=GlobalVar.POP3Port; System.out.println("input username"); user.user=in.nextLine(); System.out.println("input password"); user.password=in.nextLine(); MailList mails=GetMailTemple.getMail(user); int mailsCount=mails.mailCount(); for (int i=0;i
GetMailTemple和sendmailTemple一样的原理,因为里面有很多涉及底层的东西,我们要把他独立出来.但是这个Temple并不会吧服务器传回来的邮件内容进行处理,而已调用一个专门处理邮件内容的类来进行处理,他只负责和服务器的通信,而不涉及服务器内容的格式化。
“`java import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.ConnectException; import java.net.Socket;public class GetMailTemple {
public static MailList getMail(UserInfo user){ MailList getMails=new MailList(); try { Socket socket = new Socket(user.serverHost,user.serverPort); PrintWriter output = new PrintWriter(new OutputStreamWriter(socket .getOutputStream())); BufferedReader input = new BufferedReader(new InputStreamReader(socket .getInputStream())); String whatstheserverSay;//ha papapappapa,ho i hahahahahah System.out.println(input.readLine());//print welcome information //login output.println("user "+user.user); output.flush(); System.out.println(input.readLine()); output.println("pass "+user.password); output.flush(); whatstheserverSay=input.readLine(); if(!whatstheserverSay.startsWith("+OK")) { System.out.println("password error"); socket.close(); input.close(); output.close(); return getMails; } System.out.println(whatstheserverSay); //get mails int mailCount=Integer.parseInt(whatstheserverSay.substring(4,5)); for(int i=1;i<=mailCount;i++) { output.println("top "+i); output.flush(); StringBuilder receive=new StringBuilder(); String tempStr; while(true) { tempStr=input.readLine(); receive.append("\r\n"); receive.append(tempStr); if(tempStr.equals(".")) { break; } } getMails.putMail(SocketReturnStringProcess.process(receive.toString())); } output.println("quit"); output.flush(); System.out.println(input.readLine()); socket.close(); input.close(); output.close(); System.out.println("Done"); } catch (ConnectException e) { System.out.println("connectException"); } catch (Exception e) { e.printStackTrace(); } return getMails;}
}
SocketReturnStringProcess。这个类就像名字一样。专门处理服务器返回的String。用来解析出来这个服务器的返回的东西。。```javapublic class SocketReturnStringProcess { public static GetMailInfo process(String input) { GetMailInfo result=new GetMailInfo(); int startIndex=0; int offsite=0;//the gegin index of useful value eg received_form subject content int endIndex=0; startIndex=input.indexOf("Return-Path: <",0); offsite="Return-Path: <".length(); endIndex=input.indexOf(">",startIndex+offsite); result.received_from=input.substring(startIndex+offsite, endIndex); startIndex=input.indexOf("Subject:",endIndex); if(startIndex==-1) { startIndex=input.indexOf("subject:",endIndex); } offsite="Subject:".length(); endIndex=input.indexOf("\r\n",startIndex+offsite); result.subject=input.substring(startIndex+offsite, endIndex); result.content=input.substring(endIndex+2, input.length()-3); //System.out.println(input); return result; }}
当然客户端大体上就是这样的一个结构。最后给出我这个项目的一个截图。。
总结:
1虽然这次充分实现了这个程序的功能。但是我没有把程序的逻辑和输入输出分开。在将来进行软件移植的时候很困难 2要把调试输出信息和标准的输入输出信息分开。现在的程序对于程序底层的所有输入输出都发送到控制台,最好可以用日志系统,把程序底层的输出放日志系统里面去