月度归档:2014 年九月

javamail通过代理服务器发送邮件

这段时间在写一个邮件系统,因为可能有很多帐号,需要使用不同的代理服务器发送,在网上查了很多资料,很多demo基本上都是没用,真不知道哪些家伙刷那么多垃圾帖干嘛,自己都没测试就贴出来,浪费老子时间,真想扇他们几个耳刮子。

不想浪费在这海量垃圾资料的查阅中,只能自己动手丰衣足食了。通过跟了大半天的源码终于成功了。

javamail只有1.4.5及以上版本才支持代理发送,而且支持socks代理,http代理不支持,网上有很多类似的代码如:


System.getProperties().put("http.proxySet","true");
System.getProperties().put("http.proxyHost","127.0.0.1");
System.getProperties().put("http.proxyPort","8098");

这些我尝试都不成功,JAVAMAIL API FAQ中也有提到:Note that setting these properties directs all TCP sockets to the SOCKS proxy, which may have negative impact on other aspects of your application.这个会代理所有的tcp socket,可能会影响到其他程序,而且不能做得每个帐号不同的代理,也会有并发的问题,显然这不是我想要的。

JAVAMAIL API FAQ提到了通过设置:mail.smtp.socks.host属性来做到session级别的代理,但是经过尝试还是没有成功,代码如下:


properties.put("mail.smtp.socks.host","127.0.0.1");
properties.put("mail.smtp.socks.port","8098");

通过一步一步代码跟踪,发现在 com.sun.mail.util.SocketFetcher#createSocket(InetAddress localaddr, int localport,String host, int port, int cto, int to,Properties props, String prefix,SocketFactory sf, boolean useSSL) 这个方法中开始出现了使用socks的代码:


String socksHost = props.getProperty(prefix + ".socks.host", null);

调用这个方法的代码:


serverSocket = SocketFetcher.getSocket(host, port,
props, "mail." + name, isSSL);

可以知道,prefix为”mail.”+name

name在SMTPSSLTransport的构造方法中定义,值为“smtps”,代码如下:


public SMTPSSLTransport(Session session, URLName urlname) {
 super(session, urlname, "smtps", true);
 }

这里调用SMTPSSLTransport的构造方法是由配置:

 properties.put("mail.transport.protocol", "smtps");

来决定的;

在session初始化的时候:

mailSession = Session.getInstance(properties);

会调用 Session#loadProviders 方法


addProvider(new Provider(Provider.Type.TRANSPORT,
 "smtps", "com.sun.mail.smtp.SMTPSSLTransport",
 "Sun Microsystems, Inc.", Version.version));

把相应的Provider初始化好,在获取Transport的时候会根据配置获取相应的Provider, Session#getTransport:

getTransport(getProperty("mail.transport.protocol"))

因此这里的设置代理的时候name为stmps而不是stmp:

properties.put("mail.smtps.socks.host","127.0.0.1");
properties.put("mail.smtps.socks.port","8098");

这样设置后虽然能正确获取host和端口了,但是最终还是没有使用代理,继续跟踪发现socket在这里创建:


if (sf != null)
 socket = sf.createSocket();
if (socket == null) {
 ......
 // get & invoke the getSocket(host, port) method
 Method mthGetSocket = proxySupport.getMethod("getSocket",
 new Class[] { String.class, int.class });
 socket = (Socket)mthGetSocket.invoke(new Object(),
 ......
 }

这个sf即配置的socketFactory,具体代码在方法:SocketFetcher#getSocket(String host, int port, Properties props,String prefix, boolean useSSL) 中,

因此只有不配置socketFactory就会使用配置的代理去生成socket;

于是乎把socketFactory相关配置,如:

 properties.put("mail.imaps.ssl.socketFactory.class", "javax.net.ssl.SSLSocketFactory");

去掉,再运行,完美运行。

关于测试是否使用了代理发送,在收到邮件后,在foxmail中右键邮件->更多操作->查看邮件源码:


Return-Path: <xxxx@gmail.com>
Received: from xxxx  ([my proxy ip])

这里my proxy ip就是代理的ip了;