java基于Socket设计一个Mail的收发客户端

这是一个很无聊的作业。大约花了我一天的时间。
介绍下实验工具
邮件服务器用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)我们就能把数据类里面的参数嵌入到模板中。这样在更改指令处理流的时候不用更改参数的传入,减少软件的耦合度。但是如果改变参数,那么模板必须要改变了(不然谁来处理新加入的参数?)

数据流向大约是这样的java基于Socket设计一个Mail的收发客户端_第1张图片

先来看看 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;
    }
}

当然客户端大体上就是这样的一个结构。最后给出我这个项目的一个截图。。
java基于Socket设计一个Mail的收发客户端_第2张图片

总结:
1虽然这次充分实现了这个程序的功能。但是我没有把程序的逻辑和输入输出分开。在将来进行软件移植的时候很困难
2要把调试输出信息和标准的输入输出信息分开。现在的程序对于程序底层的所有输入输出都发送到控制台,最好可以用日志系统,把程序底层的输出放日志系统里面去

你可能感兴趣的:(java基于Socket设计一个Mail的收发客户端)