JAVA模拟邮箱服务器与主流邮箱传输邮件,使⽤
(netty,javax.mail,dnsjava)
实现效果
1、接收QQ邮箱发过来的邮件
2、发送邮件到QQ邮箱
基础概念
邮件从客户端发送到服务器这⼀过程中的⾓⾊
MUA(Mail User Agent):邮件⽤户代理,接收邮件的客户端,使⽤⽅是⽤户,负责把编辑好的邮件发送到MTA
MTA(Mail Transfer Agent):邮件传输代理,邮箱服务器,负责邮件的接收,传输,接收之后检查收件⼈是不是属于⾃⼰邮箱,如果是,则交给MDA,不是则继续传输到下⼀个MTA
MDA(Mail Delivery Agent):邮件投递代理,保存邮件,等待收件⼈领取,可以做邮件的分析,删除
等功能
邮件使⽤过程中常⽤协议
SMTP(Simple Mail Transfer Protocol)简单邮件传输协议,端⼝号常⽤(25,465;其中25是邮件服务器默认端⼝,465是加密端⼝) 主要负责发送邮件,MUA往MTA发邮件使⽤smtp这⼀协议,MTA对MTA也⽤smtp协议发邮件
POP3(Post Office Protocol3)邮局通讯协议 端⼝号常⽤(110)主要负责从邮箱服务器获取邮件
IMAP(Internet Mail Access Protocol)交互式邮件存取协议,端⼝号常⽤(143)主要负责对本地和服务器上的邮件操作进⾏同步    发送⼀封邮件指令如下,其中\r\n表⽰换⾏,如果在命令⾏⾥操作可以去掉\r\n,在Java中拼接字符串要带上,
HELO mail.yuming \r\n
MAIL FROM:<zhangsan@yuming>\r\n
RCPT TO:<mahuateng@qq>\r\n
DATA\r\n
From: 张三 <zhangsan@yuming>\r\n
Sender: 张三 <zhangsan@yuming>\r\n
Reply-To: 张三 <zhangsan@yuming>\r\n
To: mahuateng@qq\r\n
Message-ID: <728800004.0.1411122805115@my-PC>\r\n
Subject: 主题.\r\n
MIME-Version: 1.0\r\n
Content-Type: text/plain; charset=us-ascii\r\n
Content-Transfer-Encoding: 7bit\r\n
公子乔一内容 . \r\n
.\r\n
陈乔恩
QUIT\r\n
实现过程
1、域名配置
⾸先有个域名,我是从阿⾥云买的,
然后要有公⽹IP,服务器不要⽤云服务器,因为⼤多数会把25端⼝封掉,所以要⽤⾃⼰的服务器,可以是linux也可以是windows,服务器开放25、80端⼝,做解析⽤
进⼊域名解析配置页⾯,新增A解析和MX解析
2、模拟邮箱服务器接⼝功能描述
主要模拟接收邮件和发送邮件这两个操作
接收邮件功能:通过netty 监听25端⼝,接收客户端指令,分析指令,最终接收邮件并返回状态
发送邮件功能:如果收件⽅是第三⽅(⽐如QQ)邮箱,那先通过dnsjava⼯具获取qq的邮箱服务器地址,然后通过javax.mail⼯具发送邮件到QQ邮箱,
当然也可以不⽤javax.mail,⽤netty或者是⾃⼰写socket也能实现发送邮件,原理都是访问第三⽅邮件服务器的25端⼝,然后发送邮件指令,这时候QQ邮箱是会校验发件⼈是否合法的,所以要先把域名解析做好
3、Java代码实现
maven项⽬ pom⽂件引⼊依赖
<!-- 接收邮件⽤ -->
<dependency>
<groupId>ioty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.34.Final</version>
</dependency>
<!-- 服务器间发送邮件⽤ -->
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.6.2</version>
</dependency>
<!-- 解析dns⽤ -->
<dependency>
<groupId>dnsjava</groupId>
<artifactId>dnsjava</artifactId>
<version>3.5.0</version>
</dependency>
netty接收邮件代码
import ioty.bootstrap.ServerBootstrap;
import ioty.channel.ChannelFuture;
import ioty.channel.ChannelFutureListener;
import ioty.channel.ChannelInitializer;
import ioty.channel.ChannelOption;
import ioty.channel.ChannelPipeline;
import ioty.channel.EventLoopGroup;
import ioty.channel.nio.NioEventLoopGroup;
import ioty.channel.socket.SocketChannel;
import ioty.channel.socket.nio.NioServerSocketChannel;
import dec.DelimiterBasedFrameDecoder;
import dec.Delimiters;
import dec.string.StringDecoder;
import dec.string.StringEncoder;
import ioty.util.CharsetUtil;
/**
* 邮件服务器,netty
*/
public class SmtpServer {
/** 接收线程 */
private static EventLoopGroup bossGroup = new NioEventLoopGroup();
/
** ⼯作线程 */
private static EventLoopGroup workerGroup = new NioEventLoopGroup();
/** channel处理结果 */
private static ChannelFuture channelFuture;
/**
* 启动服务
*/
public static void start() throws Exception {
try {
ServerBootstrap b = new ServerBootstrap();
.
channel(NioServerSocketChannel.class)// 设置管道
.option(ChannelOption.SO_BACKLOG, Integer.valueOf(1024))// 最⼤连接数
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
// 配置解析⽅式、处理handler
pipeline.addLast(new DelimiterBasedFrameDecoder(8 * 1024, Delimiters.lineDelimiter()));      pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
pipeline.addLast(new SmtpServerHandler());
}
});// 初始化器,绑定I/O事件的处理类
channelFuture = b.bind(25).sync();
channelFuture.addListener((ChannelFutureListener) channelFuture -> {
System.out.println("邮件服务器已启动");
});
channelFuture.channel().closeFuture().sync();
} finally {
shutdown();
System.out.println("邮件服务器已关闭");
}
}
/
**
* 关闭服务
*/
public static void shutdown() {
if (channelFuture != null) {
channelFuture.channel().close().syncUninterruptibly();
}
if ((bossGroup != null) && (!bossGroup.isShutdown())) {
bossGroup.shutdownGracefully();
}
if ((workerGroup != null) && (!workerGroup.isShutdown())) {
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
try {
start();
} catch (Exception e) {朝鲜和韩国
e.printStackTrace();
}
}
}
import ioty.channel.Channel;
import ioty.channel.ChannelHandlerContext;
import ioty.channel.SimpleChannelInboundHandler;
public class SmtpServerHandler extends SimpleChannelInboundHandler<String> {
private boolean finishData = false;
private boolean userAcc = false;
private boolean userPwd = false;
private String user = null;
private String pwd = null;
private StringBuffer emailData = new StringBuffer();
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
// 与客户端成功建⽴连接,并回复状态
channel.writeAndFlush("220\r\n");
}
@Override
public void channelRead0(ChannelHandlerContext ctx, String msg) {
Channel channel = ctx.channel();
String line = msg;
System.out.println(String.format("接收到指令:【%s】", line));
if (finishData == false) {
onCommand(channel, line);
} else {
onData(channel, line);
}
}
private void onCommand(Channel channel, String line) {
String command;
int i = line.indexOf(" ");
if (i > 0) {
command = line.substring(0, i).toUpperCase();
} else {
command = UpperCase();
}
if (command.length() == 0) {
channel.writeAndFlush("500\r\n");
return;
}
switch (command) {
case "HELO":
case "EHLO":
// TODO ⼀般客户端需要传账号密码才可以发邮件,那这⾥需要返回服务器⽀持的账号校验⽅式如:250-AUTH=LOGIN    channel.writeAndFlush("250-AUTH LOGIN PLAIN XOAUTH XOAUTH2\r\n");
channel.writeAndFlush("250-AUTH=LOGIN\r\n");
channel.writeAndFlush("250\r\n");
break;
case "AUTH":
// 接收到⽤户登录指令⽤户需要输⼊账号
userAcc = true;
channel.writeAndFlush("334 VXNlcm5hbWU6\r\n");
break;
case "MAIL":
// 邮件发件⼈
emailData.setLength(0);
emailData.setLength(0);
channel.writeAndFlush("250\r\n");
break;
case "RCPT":
// 邮件接收⼈
channel.writeAndFlush("250\r\n");
break;
case "DATA":
// 准备接收⽂件
刘涛老公个人资料finishData = true;
channel.writeAndFlush("354\r\n");
break;
case "RSET":
channel.writeAndFlush("250\r\n");
break;
case "QUIT":
手机怎么连接wifi// 退出
channel.writeAndFlush("221\r\n");
break;
default:
if (userAcc) {
userPwd = true;
userAcc = false;
user = line;
channel.writeAndFlush("334 UGFzc3dvcmQ6\r\n");
} else if (userPwd) {
userPwd = false;
pwd = line;
System.out.println(String.format("⽤户账号【%s】,密码【%s】", user, pwd));
if ("admin".equals(user) && "123456".equals(pwd)) {
// 登录成功
明星娱乐八卦channel.writeAndFlush("235\r\n");
} else {
// 登录失败
channel.writeAndFlush("530\r\n");
}
} else {
channel.writeAndFlush("500 \r\n");
channel.close();
}
break;
}
}
private void onData(Channel channel, String line) {
if (im().equals(".")) {
/
/ 默认邮件结束符是⼀个.
finishData = false;
channel.writeAndFlush("250\r\n");
System.out.println(String.format("新来了⼀封邮件【%s】", String()));  // TODO 保存邮件到本地,数据库,
// TODO 检查收件⼈,是第三⽅邮箱需要把这封邮件转到第三⽅邮箱
} else {