这是一个很无聊的作业。大约花了我一天的时间。
介绍下实验工具
邮件服务器用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 {
LinkedList<GetMailInfo> mails;
public MailList()
{
mails=new LinkedList<GetMailInfo>();
}
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<mailsCount;i++)
{
GetMailInfo e =mails.getMail();
System.out.println("信件");
System.out.print("信件主题:");
System.out.println(e.subject);
System.out.print("发信人:");
System.out.println(e.received_from);
System.out.println("");
System.out.println(e.content);
System.out.println("");
}
}
}
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。用来解析出来这个服务器的返回的东西。。
```java
public 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要把调试输出信息和标准的输入输出信息分开。现在的程序对于程序底层的所有输入输出都发送到控制台,最好可以用日志系统,把程序底层的输出放日志系统里面去