博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java基于Socket设计一个Mail的收发客户端
阅读量:2341 次
发布时间:2019-05-10

本文共 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 {    LinkedList
mails; 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要把调试输出信息和标准的输入输出信息分开。现在的程序对于程序底层的所有输入输出都发送到控制台,最好可以用日志系统,把程序底层的输出放日志系统里面去

你可能感兴趣的文章
Xeon E3-1500 v5 GPU
查看>>
skylake AVC性能
查看>>
RTSP 协议分析 (一)
查看>>
RTSP协议分析(二)
查看>>
IPTV的前世今生与发展
查看>>
x264中的汇编x86inc.asm
查看>>
X264中的sad-a.asm
查看>>
x264中的cpu-a.asm
查看>>
x264中的DCT变换 dct-a.asm
查看>>
X264的时耗分析
查看>>
H.264 Profile、Level、Encoder三张简图
查看>>
NEON指令集综述
查看>>
FFmpeg的H.264解码器源代码简单分析:概述
查看>>
linux下编译调试x264
查看>>
debug和release版本的区别
查看>>
x86 指令集发展历程
查看>>
逐行Porgressive隔行Interlaced扫描的超详细讲解
查看>>
使用FFmpeg实现抠图合并功能(chroma key)
查看>>
长宽比 (视频)
查看>>
Pan & Scan和Letterbox
查看>>