tio-mail-wing简介
核心目标
- 实现邮件接收服务 (SMTP):能够接收来自其他邮件服务器(如 QQ、Gmail 发来的邮件)或邮件客户端(如 Foxmail、Outlook 发出的邮件)的邮件。
- 实现邮件读取服务 (POP3/IMAP):允许邮件客户端(如 QQ 邮箱 App、Gmail App、Thunderbird)连接到我们的服务器,拉取和管理邮件。
- 用户和邮箱管理:管理自己服务器上的邮箱账户和密码。
- 邮件存储:持久化存储接收到的邮件。
开发规划
第一阶段:项目初始化与基础架构
这是万里长征的第一步,搭建好骨架。
- 创建 tio-boot 项目
- 使用 Maven 创建一个标准的 tio-boot 项目。
- 在
pom.xml
中引入核心依赖:
- 配置服务器端口 在
app.properties
中规划好标准端口。
mail.server.smtp.port=25 mail.server.smtp.heartbeat-timeout=60000 mail.server.pop3.port=110 mail.server.pop3.heartbeat-timeout=60000 mail.server.imap.port=143 mail.server.imap.heartbeat-timeout=60000 ```
第二阶段:实现核心协议 (由简到难)
邮件协议是基于文本的、一问一答式的。tio-boot
的 AioHandler
就是处理这些问答的核心。
通用准备:
- 编解码器:邮件协议都是基于行(以
\r\n
结尾)的。你需要一个简单的字符串编解码器。 - 会话上下文:使用
ChannelContext
的自定义子类(如SmtpSessionContext
)来跟踪每个客户端连接的状态(例如:是否已认证、MAIL FROM
命令是否已发送等)。
任务1:实现 POP3 服务 (最简单,先做)
POP3 协议简单,状态少,适合入门。
- 创建
Pop3ServerAioHandler and Pop3SessionContext and Pop3ServerAioListener
- 在
handler
方法中,接收客户端发来的一行命令(如USER a@b.com
,PASS 123
,STAT
,LIST
,RETR 1
,DELE 1
,QUIT
)。 - 使用
if-else
或switch
解析命令。 - 响应格式:成功响应以
+OK
开头,失败响应以-ERR
开头。
- 在
- 实现命令逻辑
USER
/PASS
: 调用UserService
进行认证。认证成功后,在会话上下文中标记为“已登录”。STAT
: 调用MailboxService
获取邮箱中的邮件数量和总大小。LIST
: 遍历邮箱中的所有邮件,返回每封邮件的编号和大小。RETR [msg_id]
: 调用MailboxService
读取指定邮件的完整内容(包括头和体),并发送给客户端。DELE [msg_id]
: 在会话中标记邮件为待删除。QUIT
: 退出,关闭连接。
- 测试
- 工具:使用
telnet localhost 110
手动测试命令。 - 客户端:配置 Foxmail 或 Thunderbird 客户端,使用 POP3 协议连接你的服务器。
- 工具:使用
任务2:实现 SMTP 服务 (核心接收功能)
SMTP 协议状态机比 POP3 复杂。
- 创建
SmtpServerAioHandler 等相关类
- 处理的命令包括:
HELO
/EHLO
,AUTH LOGIN
,MAIL FROM:<...>
,RCPT TO:<...>
,DATA
,QUIT
。
- 处理的命令包括:
- 实现命令逻辑 (状态流转)
- 连接建立:服务器发送
220 tio-mail-wing ESMTP Service ready
。 HELO
/EHLO
: 客户端问候。服务器响应250 OK
。AUTH LOGIN
: 进行认证。这是客户端发信时必须的。你需要处理 Base64 编码的用户名和密码。MAIL FROM
: 记录发件人。在会话上下文中保存。RCPT TO
: 记录收件人。检查收件人是否是本服务器的用户。可以有多个RCPT TO
。DATA
: 客户端告知要开始发送邮件内容了。服务器响应354 Start mail input; end with <CRLF>.<CRLF>
。- 邮件内容接收:进入特殊模式,持续接收数据,直到遇到单独一行的
.
为止。 - 邮件解析与存储:将接收到的完整邮件内容(一坨字符串)交给
angus-mail
(Jakarta Mail) 的MimeMessage
解析,然后调用MailboxService
存入收件人的邮箱。 - 存储成功后:响应
250 OK: queued as ...
。 QUIT
: 关闭连接。
- 连接建立:服务器发送
- 测试
- 工具:
telnet localhost 25
。 - 客户端:配置 Foxmail/Outlook,使用你的服务器作为 SMTP 服务器向外发信(或给自己发信)。
- 外部服务器:从你的 QQ 邮箱或 Gmail 向你在
tio-mail-wing
上创建的账户(如user@yourdomain.com
)发送一封邮件,看能否成功接收。
- 工具:
任务3:实现 IMAP 服务 (最复杂,最后做)
IMAP 功能强大,但协议也最复杂,是有状态的,且命令繁多。
- 创建
ImapServerAioHandler
- IMAP 命令有标签(Tag),如
A001 LOGIN user pass
。响应时也需要带上相同的标签,如A001 OK LOGIN completed
。
- IMAP 命令有标签(Tag),如
- 实现核心命令
CAPABILITY
: 告诉客户端服务器支持哪些功能。LOGIN
: 认证。SELECT [mailbox]
: 选择邮箱(如INBOX
)。这是大部分操作的前提。FETCH [msg_id] (FLAGS|BODY[HEADER]|BODY[TEXT])
: 获取邮件信息,可以只获取标志、头、正文等部分内容,非常灵活。STORE [msg_id] +FLAGS (\Seen)
: 修改邮件标志,如标记为已读。LOGOUT
: 退出。
- 挑战
- IMAP 的状态管理非常复杂。
FETCH
命令的参数组合非常多。- 需要支持邮件夹(Mailbox)的概念。
- 建议
- 先实现最核心的
LOGIN
,SELECT INBOX
,FETCH
,LOGOUT
,让客户端能连上并看到邮件列表和内容。 - 仔细阅读 RFC 3501 (IMAP4rev1) 文档。
- 先实现最核心的
第三阶段:数据持久化与业务逻辑
用户管理 (
UserService
)- 初期:可以用一个简单的
Map
或配置文件(如users.properties
)来存储用户名和密码。 - 进阶:使用数据库(如 postgresql)来存储用户信息,方便管理。
- 初期:可以用一个简单的
邮件存储 (
MailboxService
/MailRepository
)- 推荐方案:Maildir 格式
- 这是一种工业标准的、基于文件系统的邮件存储格式。
- 每个用户一个主目录,下面有
cur
,new
,tmp
三个子目录。 - 每封邮件一个文件,文件名保证唯一,避免了文件锁,性能好,结构清晰。
MailboxService
的职责就是根据用户名找到对应的目录,在其中创建、读取、移动(如从未读new
到已读cur
)、删除文件。
- 进阶方案:数据库
- 可以将邮件元数据(发件人、主题、时间等)存入数据库表,邮件原文(MIME 内容)可以存为
BLOB
/TEXT
字段或依然存在文件系统。 - 管理和搜索方便,但大邮件的存取可能有效率问题。
- 可以将邮件元数据(发件人、主题、时间等)存入数据库表,邮件原文(MIME 内容)可以存为
- 推荐方案:Maildir 格式
第四阶段:安全与部署
实现 SSL/TLS (STARTTLS)
- 现代邮件客户端默认都要求加密连接。不加这个,QQ、Gmail 客户端很可能连不上。
- 邮件协议的加密通过
STARTTLS
命令实现。客户端发送此命令后,服务器需要将当前连接升级为 SSL/TLS 连接。 tio-boot
支持 SSL/TLS。你需要在AioHandler
中捕获STARTTLS
命令,然后调用Tio.startSSL(channelContext, ...)
方法来升级连接。- 你需要一个 SSL 证书(开发阶段可以使用自签名证书)。
- 安全端口:除了标准端口,还应支持隐式 SSL 端口(SMTPS: 465, POP3S: 995, IMAPS: 993)。这需要在
tio-boot
中配置单独的、启用 SSL 的TioServer
实例。
部署
- 使用 Maven 打包成一个可执行的
jar
文件。 - 在服务器上通过
java -jar tio-mail-wing.jar
运行。 - 为了让公网能访问,你需要一台有公网 IP 的服务器,并配置好域名和 DNS(MX 记录指向你的服务器 IP)。
- 使用 Maven 打包成一个可执行的
总结与建议
- 分步走,先主后次:POP3 -> SMTP -> IMAP。先让一个协议跑通,再做下一个。
- 测试驱动:每实现一个命令,就用
telnet
测试一下,确保输入输出符合预期。 - 日志是生命线:在
AioHandler
的每个关键步骤打印详细日志,包括收到的命令和发出的响应。当客户端连接失败时,日志是唯一的排错线索。 - 阅读 RFC 文档:虽然枯燥,但邮件协议的细节都在 RFC 文档里。遇到不解的行为时,查阅 RFC 是最权威的方式。
- SMTP: RFC 5321
- POP3: RFC 1939
- IMAP: RFC 3501
- 参考开源实现:可以参考一些优秀的开源邮件服务器项目,如
James
(Java),Dovecot
(C) 的设计思想,但不要陷入代码细节。