使用SMTP协议发送邮件

使用SMTP协议发送邮件,可以不通过SMTP服务器,直接将邮件发送到邮件服务器。很多服务器端程序可能需要向很多用户发送邮件,直接通过SMTP发送可能是最有效的。

关于SMTP协议定义在RFC821,可以在此看中文版

第一步:通过目标email查找邮件服务器。
例如:asklxf@sohu.com,其邮件服务器地址为:sohumx.sohu.com



import java.net.*;
import java.io.*;
import java.util.*;
import javax.naming.*;
import javax.naming.directory.*;


public class Smtp {


    public static void main(String[] args) throws Exception {
        // DNS服务器,看看本机的DNS配置
        String dns = "dns://192.168.1.1";
        // 邮箱后缀:
        String domain = "sohu.com";
        Hashtable env = new Hashtable();
        env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
        env.put("java.naming.provider.url", dns);
        DirContext ctx = new InitialDirContext(env);
        Attributes attr = ctx.getAttributes(domain, new String[]{"MX" });
        NamingEnumeration servers = attr.getAll();
        // 列出所有邮件服务器:
        while(servers.hasMore()) {
            System.out.println(servers.next());
        }
    }
}


第二步:直接连接邮件服务器的25端口,用SMTP协议发送邮件。
这里使用sohu信箱,邮件服务器为sohumx.sohu.com,收信人必须在此服务器上:



import java.net.*;
import java.io.*;
import java.util.*;
import javax.naming.*;
import javax.naming.directory.*;


public class Smtp {


    private static String END_FLAG = "\r\n";


    public static void main(String[] args) throws Exception {
        String mx = "sohumx.sohu.com";
        InetAddress addr = InetAddress.getByName(mx);
        Socket socket = new Socket(addr, 25);


        InputStream in = socket.getInputStream();
        OutputStream out = socket.getOutputStream();


        // 连接成功后服务器会响应:
        response(in);


        // 首先发送HELO命令:
        send("HELO
www.javasprite.com" + END_FLAG, out);
        response(in);


        // 然后发送发件人地址:
        send("MAIL FROM: someone@somewhere.com" + END_FLAG, out);
        response(in);


        // 设置收件人地址:
        send("RCPT TO: asklxf@sohu.com" + END_FLAG, out);
        response(in);


        // 开始发送邮件正文:
        send("DATA" + END_FLAG, out);
        response(in);


        send("From: someone@somewhere.com" + END_FLAG, out);
        send("To: asklxf@sohu.com" + END_FLAG, out);
        send("Subject: Test without smtp server" + END_FLAG, out);
        send("Content-Type: text/plain;" + END_FLAG, out);
        send(END_FLAG + END_FLAG, out);


        // 发送邮件正文,如果用中文,需要BASE64编码:
        send("text message body!" + END_FLAG, out);
        // 每行以\r\n结束,不可过长,可拆成多行。


        // 以"\r\n.\r\n"作为结束标志:
        send(END_FLAG + "." + END_FLAG, out);
        response(in);


        // 结束并确认发送:
        send("QUIT" + END_FLAG, out);
        response(in);
        in.close();
        out.close();
        socket.close();
    }


    public static void response(InputStream in) throws Exception {
        byte[] buffer = new byte[1024];
        int n = in.read(buffer);
        String s = new String(buffer, 0, n);
        // 服务器会返回:### Text
        // 具体含义见RFC821
        System.out.println(s);
    }


    public static void send(String s, OutputStream out) throws Exception {
        byte[] buffer = s.getBytes();
        out.write(buffer);
        // 不要忘了flush(),否则可能在缓冲区:
        out.flush();
    }
}


Ok,打开outlook收信,会发现有一封来自someone@somewhere.com的信件。

第三步:处理服务器返回码,各种异常,包装成Java组件以便重用:



public interface SendMail {
    void send(String from, String to, String subject, String text)
}

public class SendMailImpl extends Thread implements SendMail {
    // TODO: 自己写......
}

About this Entry

This page contains a single entry by Sky published on September 22, 2005 3:46 PM.

Java的ClassLoader与Package机制 was the previous entry in this blog.

Eclipse快速上手指南 (1) is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.