POP3有三种状态: AUTHORIZATION(授权),TRANSACTION(处理),UPDATE(更新).
当TCP建立起来时,POP3进入"授权"状态,客户需要使用USER/PASS进行身份验证. 通过验证后,POP3进入"处理"状态,客户可以发送LIST,RETR等命令来查询,获取邮件. 当客户在此状态下发送"QUIT"命令后,POP3进入"更新"状态,服务器处理完命令后又回到"授权"状态.
POP3协议的主要命令:
1.stat命令格式:stat 无需参数
2.list命令格式:list [n] 参数n可选,n为邮件编号
3.uidl命令格式:uidl [n] 同上
4.retr命令格式:retr n 参数n不可省,n为邮件编号
5.dele命令格式:dele n 同上
6.top 命令格式:top n m 参数n,m不可省,n为邮件编号,m为行数
7.noop命令格式:noop 无需参数
8.quit命令格式:quit 无需参数
Jmail com.sun.mail.pop3.Protocol类封装完成pop3协议的指令:
该协议在设计是遵守了多用组合,少用继承的设计原则:
讲用到的socket和socket的输入输出流全部作为成员变量,如下所示
通过开关的形式控制debug:
Socket通过构造方法得到他的实例:
详细例如以下方法:
synchronized String login(String user, String password)//保证了同步
synchronized boolean quit()
synchronized int list(int msg) synchronized InputStream list()
synchronized InputStream retr(int msg, int size)
synchronized boolean retr(int msg, OutputStream os)
synchronized InputStream top(int msg, int n)
synchronized boolean dele(int msg)
synchronized String uidl(int msg)
synchronized boolean uidl(String[] uids)
synchronized boolean noop()
synchronized boolean rset()
synchronized boolean stls()
该协议在封装指令时,讲共通的部分进行抽取:
例如:
//表示命令开始
simpleCommandStart(cmd);
//向服务器发出命令
issueCommand(cmd);
//执行读取服务器段输出
Response r = readResponse();
//标示命令结束
simpleCommandEnd();
private void issueCommand(String cmd)
throws IOException
{
if (this.socket == null) {
throw new IOException("Folder is closed");
}
if (cmd != null) {
if (this.debug)
this.out.println("C: " + cmd);
cmd = cmd + "\r\n";
//向服务器段发出命令
this.output.print(cmd);
this.output.flush();
}
}
Response类封装了:服务器返回对象
Ok该次操作是否正确
Data 返回的信息
Bytes用户输入信息
private Response readResponse()
throws IOException
{
String line = null;
try {
//读取服务器返回的第一行命令
line = this.input.readLine();
}
catch (InterruptedIOException iioex)
{
try
{
//如果出现异常,就关闭该次链接
this.socket.close(); } catch (IOException cex) {
}
throw iioex;
}
//如果line为空,也是服务器异常
if (line == null) {
if (this.debug)
this.out.println("S: EOF");
throw new EOFException("EOF on socket");
}
//根据第一行开始字符串,判断是否正确解析
if (this.debug)
this.out.println("S: " + line);
Response r = new Response();
if (line.startsWith("+OK"))
r.ok = true;
else if (line.startsWith("-ERR"))
r.ok = false;
else
throw new IOException("Unexpected response: " + line);
int i;
if ((i = line.indexOf(' ')) >= 0)
//获得指定信息,封装到Response类中
r.data = line.substring(i + 1);
return r;
}
该协议中的经典地方:
Md5加密:
private String getDigest(String password) {
String key = this.apopChallenge + password;
byte[] digest;
try {
MessageDigest md = MessageDigest.getInstance("MD5");
digest = md.digest(key.getBytes("iso-8859-1"));
} catch (NoSuchAlgorithmException nsae) {
return null;
} catch (UnsupportedEncodingException uee) {
return null;
}
return toHex(digest);
}
准换成十六进制(byte 有十六位,分为低八位和高八位)
private static String toHex(byte[] bytes)
{
char[] result = new char[bytes.length * 2];
int index = 0;
for (int i = 0; index < bytes.length; index++) {
//得到值本身
int temp = bytes[index] & 0xFF;
//右移四位,得到高八位对应得十六进制数
result[(i++)] = digits[(temp >> 4)];
//和0xF与得到低八位对应的值
result[(i++)] = digits[(temp & 0xF)];
}
return new String(result);
}
static
{
digits = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
}
//PropUtil工具类分析,工具类目的是为其他类提供方法,并不属于某一个对象
//所以它在内存中只需要加载一份字节码就可以了,故它的方法都是静态方法
//因为Property类没有提供类型处理的方法,所以做了一个Prop工具类
public class PropUtil
{
//得到props中是int类型的方法
public static int getIntProperty(Properties props, String name, int def)
{
return getInt(getProp(props, name), def);
}
//得到props中是boolean类型的方法
public static boolean getBooleanProperty(Properties props, String name, boolean def)
{
return getBoolean(getProp(props, name), def);
}
public static int getIntSessionProperty(Session session, String name, int def)
{
return getInt(getProp(session.getProperties(), name), def);
}
public static boolean getBooleanSessionProperty(Session session, String name, boolean def)
{
return getBoolean(getProp(session.getProperties(), name), def);
}
public static boolean getBooleanSystemProperty(String name, boolean def)
{
try
{
return getBoolean(getProp(System.getProperties(), name), def);
}
catch (SecurityException sex)
{
try
{
String value = System.getProperty(name);
if (value == null)
return def;
if (def) {
return !value.equalsIgnoreCase("false");
}
return value.equalsIgnoreCase("true"); } catch (SecurityException sex) {
}
}
return def;
}
//得到props对象中的名称为name的对象,
private static Object getProp(Properties props, String name)
{
Object val = props.get(name);
//该props中是否存在name的对象,如果不存在的到属性对象
if (val != null) {
return val;
}
return props.getProperty(name);
}
//转换成整形对象
private static int getInt(Object value, int def)
{
if (value == null)
return def;
if ((value instanceof String))
try {
return Integer.parseInt((String)value);
} catch (NumberFormatException nfex) {
}
if ((value instanceof Integer))
return ((Integer)value).intValue();
return def;
}
//转换为boolean类型
private static boolean getBoolean(Object value, boolean def)
{
if (value == null)
return def;
if ((value instanceof String))
{
if (def) {
return !((String)value).equalsIgnoreCase("false");
}
return ((String)value).equalsIgnoreCase("true");
}
if ((value instanceof Boolean))
return ((Boolean)value).booleanValue();
return def;
}
}
//装饰模式写的LineInputStream类
public class LineInputStream extends FilterInputStream
{
private char[] lineBuffer = null;
private static int MAX_INCR = 1048576;
public LineInputStream(InputStream in) {
super(in);
}
public String readLine()
throws IOException
{
char[] buf = this.lineBuffer;
if (buf == null) {
buf = this.lineBuffer = new char['€'];
}
int room = buf.length;
int offset = 0;
int c1;
//LF (NL line feed, new line) 换行键
while (((c1 = this.in.read()) != -1) &&
(c1 != 10))
{
//CR (carriage return) 回车键
if (c1 == 13)
{
boolean twoCRs = false;
if (this.in.markSupported())
this.in.mark(2);
int c2 = this.in.read();
if (c2 == 13) {
twoCRs = true;
c2 = this.in.read();
}
if (c2 == 10)
{
break;
}
if (this.in.markSupported()) {
this.in.reset(); break;
}
if (!(this.in instanceof PushbackInputStream))
this.in = new PushbackInputStream(this.in, 2);
if (c2 != -1)
((PushbackInputStream)this.in).unread(c2);
if (!twoCRs) break;
((PushbackInputStream)this.in).unread(13); break;
}
room--; if (room < 0) {
if (buf.length < MAX_INCR)
buf = new char[buf.length * 2];
else
buf = new char[buf.length + MAX_INCR];
room = buf.length - offset - 1;
System.arraycopy(this.lineBuffer, 0, buf, 0, offset);
this.lineBuffer = buf;
}
buf[(offset++)] = (char)c1;
}
if ((c1 == -1) && (offset == 0)) {
return null;
}
return String.copyValueOf(buf, 0, offset);
}
}
//