最近在做《计网》的课设,要求完成一个邮件服务系统,我们组采用的是HTML+Servlet+Tomcat的方式设计管理端,项目还未完全完成。已经实现了管理端+SMTP+POP3。虽然我个人负责POP3部分的工作,但总的来说还是受益匪浅。总结一下迄今为止的收获:
1.数据库设计时,有三个点我认为非常有意思。
一个是如何储存邮件。一般来说我们都会理所当然地认为mail表中存储user_to_id, user_from_id, date, subject, content等字段以便于信息的读存,但其实这样在存储群发邮件时会造成很大的冗余,因此,最后决定用mail表存储mail_id, content,再增加一个mail_of_user(mail_id, watched)来保存收发对应关系,以减少冗余。(但是没有考虑到用户查看收件信息,因此决定改动数据库……增加字段sender用来标记是收件/发件关系)
此外,也要注意,删除某封邮件最后一个对应关系时应当连同这封邮件在mail中的记录一起删除。
二是是否需要增加用户昵称字段。user(user_id, password, author, admin),大佬的观点是不需要昵称,因为昵称在邮件服务协议中并没有规定,即使数据库存储了用户昵称,也只能在我们的客户端显示,并不具有普适性(比如在别人的客户端登陆并不能显示昵称)。
三是管理员的密码不应当与登录邮箱的密码相同。考虑到邮件服务协议在数据传输的时候并不能保证安全性,所以最好区分“密码”与“授权码” ,即管理员在登录管理界面与收发邮件的密码是不同的。
2.安全性的考虑。
一是在管理员界面的“修改密码”的功能。如果未输入并不现实真实密码,只有当改动时才将改动的密码存入数据库,即“只存不读”,防止密码被盗取。
二是对于数据库中的密码以及邮件内容加密。
3.POP3协议的实现。
关于如何实现dele与rset。本来我强烈要求在数据库的mail_of_user中增加字段,标记可能删除的邮件但大佬说这样开销很大,因为要遍历一次数据库,因此决定通过保存可能删除的mail_of_user数据,再逐一与mail表中的信息比对而获得非可能删除的邮件List,实践过程中发现也很麻烦,代码重用性极低,最后通过维护2个ArrayList实现了功能:
List<Integer> deleMailList = new ArrayList<Integer>();//保存所有可能删除的邮件id的list,List为接口,用ArrayList实现
List<Integer> totalMailList = new ArrayList<Integer>();//维护当前可以显示的邮件id的list,可能删除的不显示
只需要在读取邮件时通过当前user_id与mail_id即可以对存储关系进行操作,而不是直接读取数据库中的mail_of_user(因为如果这样做还要筛选出在deleMailList中的数据,很不方便)。rset则更新deleMailList、totalMailList即可。quit则直接删除在deleMailList表中且对应mail_of_user中的数据即可。
USER | USER 邮箱账号\r\n | 登录认证 |
PASS | PASS 邮箱密钥\r\n | 登录认证,登录成功后,返回邮件总数和总字节数 |
APOP | APOP 用户名,MD5消息摘要\r\n | APOP方式登录认证 |
STAT | STAT\r\n | 邮件总数和总字节数 |
UIDL | UIDL 邮件编号\r\n UIDL\r\n | 方式一:获取指定邮件唯一标识符(每一个邮件都有一个唯一标识符) 方式二:获取所有邮件的唯一标识符(每一个编码,对应它的ID) |
LIST | LIST 邮件编号\r\n LIST\r\n | 方式一:获取指定邮件的字节大小(注意:不包括".\r\n"的长度) 方式二:获取所有邮件的字节大小(每一个编码,对应它的大小) |
RETR | RETR 邮件编号\r\n | 下载指定邮件,服务器会发送两个封包,先发送长度(注意:不包括".\r\n"的长度) 再发送整个邮件 |
DELE | DELE 邮件编号\r\n | 将指定邮件标记为删除,当退出连接或QUIT命令时删除指定的邮件 被标识删除的邮件,不能再使用其他命令对其进行访问 |
RSET | RSET\r\n | 撤销所有DELE命令 |
TOP | TOP 邮件编号 前n行\r\n | 下载邮件的前n行内容,服务器会发送两个封包,先发送长度 (注意:是总长度,而且不包括".\r\n"的长度) 再发送包括信头的前n行邮件内容 |
NOOP | NOOP\r\n | 让服务器返回一个无意义的响应,仅能在操作状态中使用 |
QUIT | QUIT\r\n | 返回到确认状态(需重新身份验证),并将标记为删除的邮件都删除 |
4.温习Java的一些方法。
remove(Object o)与remove(int i)。
清空ArrayList不要用list = null,应该用:
list.clear();
比较对象不用!=,应该用:
str.equals("quit");
5.其他。
小组项目一定要做好版本控制,不然合代码会想哭。
自己在修改代码的时候也要注意做好备份,不要自信满满一次性全部改完,不然都不知道错在哪里。