- 浏览: 42109 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
huoyi:
您好,问您一下,我按F5,运行后总是说错误 ...\Deb ...
GBA模拟器VisualBoyAdvance源码 -
weipt:
麻烦问一下,这个怎么在android下应用?
用Java Socket实现SMTP邮件发送 -
weipt:
非常好。十分感谢!
用Java Socket实现SMTP邮件发送 -
taihe:
在发送附件的时候,我尝试将54修改微128或者1024,收到的 ...
用Java Socket实现SMTP邮件发送 -
viperasi:
多谢提供,希望在遇到问题的时候能向你寻求帮助。先谢谢了。
GBA模拟器VisualBoyAdvance源码
什么是SMTP?
SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一种提供可靠且有效电子邮件传输的协议。SMTP是建模在FTP文件传输服务上的一种邮件服务,主要用于传输系统之间的邮件信息并提供来信有关的通知。
协议结构
SMTP命令是发送于SMTP主机之间的ASCII信息,下面列举了5个常用SMTP指令
HELO <服务商><域名><换行> 与SMTP服务器握手
MAIL <服务商> FROM:<发件人地址><换行> 传送发件人的邮箱地址
RCPT <服务商> TO:<收件人地址><换行> 传送收件人的邮箱地址
DATA <换行> 发送邮件数据,以新行.结束(包括信头与信体)
QUIT <换行> 与SMTP服务器断开连接
信头与信体
在DATA指令所传送的数据中,信头和信体以一个空行分隔,下面列举了部分常用信头
From: 发件人地址
To: 收件人地址
Subject: 邮件主题
Date: 发信时间
MIME-Version: MIME版本
Content-Type: 内容类型
Content-Transfer-Encoding: 编码方式
X-Priority: 优先级
X-Mailer: 代理发信的客户端
通过Socket发送SMTP邮件
1.Base64
Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式,它要求把每三个8位的字节转换为四个6位的字节,然后在6位字节的高位补两个0,最后组成四个8位的字节。在发送电子邮件时,服务器认证的用户名和密码需要用Base64编码,附件也需要用Base64编码。这种加密方式并非绝对安全,只能做到让人不能直接看出原本内容而已。
一个用Java实现的Base64的加/解密类
2.MIME类型
MIME意为多目Internet邮件扩展,它设计的最初目的是为了在发送电子邮件时附加多媒体数据,让邮件客户程序能根据其类型进行处理。每个MIME类型由两部分组成,前面是数据的大类别,后面为具体的种类。例如:image/bmp、image/jpeg、audio/mpeg
一个用于获取各种后缀名所对应MIME类型的工具类(MIME类型列表本来是存储在XML文件中的,此类的作用是读取XML中的数据,由于本文并不打算介绍XML解析,所以直接将数据写到了代码里)
3.SMTP邮件发送实例
日志管理器
用Socket实现的SMTP邮件发送类
测试类
测试日志
状态: 创建邮件接收服务器列表
连接: 主机:"mx0.qq.com." 端口:"25"
响应: 220 mx7.qq.com ESMTP QQ Mail Server
发送: HELO gameeden.org
响应: 250 mx7.qq.com
发送: MAIL FROM: <blackfox@gameeden.org>
响应: 250 Ok
发送: RCPT TO: <114412367@qq.com>
响应: 250 Ok
发送: DATA
响应: 354 End data with .
发送: From: =?GBK?B?QmxhY2sgRm94?= <blackfox@gameeden.org>
发送: To: =?GBK?B?U29s?= <114412367@qq.com>
发送: Subject: =?GBK?B?U1RNUNPKvP6y4srU?=
发送: Date: Thu, 25 Oct 2007 10:42:13 +0800 (CST)
发送: MIME-Version: 1.0
发送: Content-Type: multipart/mixed; BOUNDARY="Boundary-=_hMbeqwnGNoWeLsRMeKTIPeofyStu"
发送: Content-Transfer-Encoding: base64
发送: X-Priority: 3
发送: X-Mailer: BlackFox Mail[Copyright(C) 2007 Sol]
发送:
发送: --Boundary-=_hMbeqwnGNoWeLsRMeKTIPeofyStu
发送: Content-Type: text/plain; charset="GBK"
发送: Content-Transfer-Encoding: base64
发送:
发送: 1eLKx9K7t+Ky4srU08q8/qGj
发送: --Boundary-=_hMbeqwnGNoWeLsRMeKTIPeofyStu
发送: Content-Type: image/gif; name="=?GBK?B?amF2YS5naWY=?="
发送: Content-Transfer-Encoding: base64
发送: Content-Disposition: attachment; filename="=?GBK?B?amF2YS5naWY=?="
发送:
发送: R0lGODlhOQA9AEQAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQAyAAAACwAAAAAOQA9AKT///+Z
发送: mcz/zMzu7u5mZpn/mZm7u7vMzMzd3d3/mczMZpnMZmbMM2aZZpkzM5nMmcz/zP+IiIj/ZpnM
发送: MzNmM5nMzP+qqqrMADPMmZmZZmYAAAAAAAAAAAAAAAAAAAAAAAAF/yAgjmRpnmiqrmzrvnAs
发送: z3Rtk0JRCMPtiwKJQgABQBISwa9WkPRMuSVroOMBhiqBUnoSLCZbwI6V4JoKjAUJUgCEUW+z
发送: pKAoSYDF1CBv3imeIlQiBYAne2ZuO20kCVuLKXFLdXUlj5Qpj1JjlyOLAmVWUJmSiSZlYj0J
发送: fCRqbpE0n64lA1utdCYLWwyvMwmpq7KxBRc8jwutCqM2i40lVrkLaAIMWNFAYFKnsYw9DLsD
发送: F62yAwrEXJaVYhMiC3U5nkPiS49jnQITRblU9G1JZkT2RiigBmBBoza0APgriEjQCAHJEjCY
发送: MFFBMgADdi1UiEhMl11eBrTaVW9QoSVRnP8xAMCAXA+DEO6kQ1RyRBKDQ7woPAmgGc1RIies
发送: u9eDE4kCwFAm01LgGEWMKwecOqGMi5YtEC5YbDXVRMKOJ6hBYFCFlxGwJxTkukPFJ9oWEhEY
发送: wECLh68TFQLoRcBFroEDgA0w4NtIy6ASevUScOCAgAEbAwzsHTEAAQYMD/4meJRyQIAGBCgQ
发送: AM3YQQAaARwfMPAXMAIErXgYyHBALgYLklMTCCDZc+jUp2UEeBCAgt4IBJKPTvy5gW7RAQZE
发送: CC4iNQACCA5Q4NniAPDafy3gZm3AgvPhu0RUOEBigAP2BAAgwD6jwgDvAfimDNQqI4/siT1Q
发送: 3WmeVVfDasQtxxv/YAdkYAAC0yiB3wN8jRAfANFhRF0NuWVWYQKn3JVCfgB4Z6ANedHnDAoD
发送: FELiAA2IEEGFNvAWwHSsMRgYcYmxN0IFfMHYIgE+3jCAcgdU9lptCLToZGWrFelZZSr60JsB
发送: uzHHXGtNmsCXXtyhdsADD4Q5AgLEPUbCjUVKYUBjjWmpZZKU5UajGQEkCZpyop2n1555vnUm
发送: aMbduBtriSmnZkee6WXAE1gmpyWgvN3ppqTcyYVcaZLq1aZXn7qwGwIb9lWqC8HllmdtLbL4
发送: 5BOAVJZbrC2Q+l10gPFoaAMRTDddA3su5gAFw0r6YAkVLMqCAcgJq5xyfjKnHKcETJdZMayr
发送: /UVDZJ2SqWUEwGap5bPiHvuDd1lymV22umr5l5k/QMlaZn81Ca+g+Oar774jhAAAIfkEABQA
发送: AAAsAAAAADkAKwCk////mZnMzMzM7u7u/8zMZmaZ3d3du7u7/5mZzJnM/8z/zDNmmWaZMzOZ
发送: ZjOZiIiIzGZmzMz//5nMzGaZmWZmzJmZ/2aZqqqqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
发送: Bf8gII5kaZ5oqq5s675wLM90bd94rsfE7gOS364nzBGLtyPSplzOms6YYhCtQasuKnam3cIG
发送: Xa/rKi4pw+XmdKQom98jMnagJrXdoiY4jueTgnl9IncjCCMShHhHBIYiEIKBIghEBI+QjQAL
发送: IwtyWwhtCI0QmH0KmgOaABOkfZwAEEEIE5CShgYLBqgrEQG9Bj4GBwcCAhQCBhBEFim9vQUN
发送: DQUHNgMHviMDFcTGAgBXAwEMBQ4F4tANATQB0gLCxMcJAAqGA+0HwcQJzgUB1uDj69LJCKDP
发送: Qa8HBRKWC9csAMJx/AY8EChiHYACBgQ4QONCQMBjw9olOHBBX7NjIyJbdMPWoFsBAAYwzohQ
发送: 75oJVgAGZGwWD0CvnAIpyhC5jkGvkO9+jfBIUKmIlz6pgMNhjeC9gb88VrzBS2aWMAF+DWAg
发送: 4oHTGv0c9kNK7IDJXitFRBDLAEyBuCZCAAAh+QQAFAAAACwAAAAAOQArAKT///+Zmczu7u7/
发送: zMzMzMz/mZlmZpnd3d27u7vMM2b/mczMZmbMZpn/zP/MmczMMzP/ZpkzM5mZZplmM5nMzP/M
发送: ADOIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF/yAgjmRpnmiqrmzrvnAsz4BQMMsC
发送: KXR/NouEoiEaFBaMgc83qFQS0BxjaCsQl7LFoCHodgcKRhJQUGJh15SCBz67RYKr+Y29Cuhn
发送: gXmOp9lEBX0+CmYMgj0JIwuHMzeAgYwmYAWUhGYCD3cNiZEkCg8LBWuUEDhBiwAJfJEDCXco
发送: rQMMD4adIwOrJVoCCWUQr7YKwCWyAw+QZMORlCdTDzwkuYzKIhUPuVy2KQWuJ4TaJwUV1CLd
发送: 4J5QKL3n0aoQJrzI7Km48gCt9ueOuJ7X8yQL7vADZO6fiHf3zEBAZXAEJD0AFHBqOALaPQBp
发送: KIpooESAgwPkSlAIQPIAnQMIEMAQWPlRwIGMJ0iSNBAhggEEPgQgKDnCJQGVP1MICCDBwAQD
发送: RWtGCEAjwM2fQAmATAjgwEqpQB3MNBBg51CjTpnKCKB1AkkLBtJyJSpz69EAAiyIFeEUgAGr
发送: E0KuIBAW68qUKdsGkDqCAgESAiIcNlD17gwKAvgGMNnCp0wHdJkOpdvjp1akJKOupCxC8kcS
发送: jAHArTG3x06yCEi/mAyAL2cfIx276EKCtgAJIizIbrpTbsqrf7XKPFzY5O8uBpifCAEAIfkE
发送: ABQAAAAsAAAAADkAKwCk////mZnM/8zM/5mZ7u7uzMzMzDNm/5nMZmaZzGZm3d3dzGaZu7u7
发送: /2aZ/8z/zJnMmWaZMzOZzDMzzMz/ZjOZiIiIqqqqzAAzzJmZAAAAAAAAAAAAAAAAAAAAAAAA
发送: AAAABf8gII5kaZKHYBJn675wrMZ0bQPHre/lwP+6AQtIhDWGxaQpoYQJBgmDNBFNLA5IErN5
发送: OkgHs9KTmhsJGtwSwZAIOw6DuCB7SJTb6dHaJxJIGwdwUAthAE9+eSMGfIYSKlkiKSULjGkO
发送: WyJtCxILJ3MkBgIOiZIjcZ0ujANohU2lfYAuDqgABiwCha08uD1QugADZQe0ZX1Ko2qGugaK
发送: SJW8RZ+Qhj1lArQElWBKBw7LJGsjCUgNhcxNc9sAa5jAfAKYAhJaleiU1FojhH0XM2u0iZ5A
发送: wsSMwIJQAGZJKJbIzKRIcRDCW9TwRKVsmSTwOciwIigSbxxIGWIAjccTCS7/brrn7qRFgAn7
发送: qavn0oUVYEOQ1XwBzwfMnS/K5TAJNEYnUUSLvniXVGmLMg8wKJhmYkKAqwrSKGDAoEABCwUU
发送: KPhp4upVBBEiIGBAhAADrHoeeC0AoYALAgEgIKCAQG/aCAF+BFhbgOtcBQ9EEGig4HDXAg/O
发送: IgjwFu/ewYF3BIhM4WoFBKD7XoVgdvDeyQQqZBYxGACCxhSoxiiAOexjBpEtWCgddsQEu3oi
发送: 2EUAQMFrHhMI0A6QFUW5FY3NJgZwdV3m1TsKRxZN2evj5iKWPwDvmjULvEnebmZAvgZzALRZ
发送: F7F6vAaBLO8JQBBRoT0PygGoZthc2vFGwgRZ6XcfBQLAnRACACH5BAAUAAAALAAAAAA5ACsA
发送: pP/////MzJmZzO7u7v+ZmczMzMwzZmZmmf+ZzN3d3cxmmbu7u8xmZv/M/8wzM8yZzJlmmf9m
发送: mTMzmWYzmcwAM8zM/5lmZoiIiMyZmaqqqgAAAAAAAAAAAAAAAAAAAAAAAAX/ICCOZGmeQBAg
发送: Kuq+MBwwjsEoBIEHce+XAYOBdwoQfkgYwkF8NZNQEcEw6A2qUZ/R4KBQbIRG7JlFzRyEwIAq
发送: MiqwKET5RUCPAhFT2nWcn6ZwKWRtgyIMfiYIBiQNK4UpYiU5iCWLAFNeaI8pJWuUjDk1X0Sb
发送: DZGGcp9tNUF9bU4jBIeqhocBCiWnf1KWtAgMPGEmjw24gLQib5d5JoEkDAgKvci4AAPAJ7oi
发送: ijfIJNUACDppziRBV94kzIIpOGZs6SSunEVDiuXpqSIN5QFM/lVmPFGgj5K2ALrWDPAnxt+T
发送: IN7IiMNCQM4QERe/bSrTwpwCYLZm3TIxK2LBbwCw/yGIMGhdRGWSAOACOGheOm3hLgG4iPNk
发送: PCApJolwhvCnCxXTOsayafSVS3bmIuCLxwMB0XJTqQ7wubGpiAcDHiQIpMZEBQFoE9BKsGBB
发送: gbcFMFx5suYJWrQHJEg4sGDOgAVpRwxga+Fthkj+XA0QAOHAhAON9UoQkEUA3wJt4SZIwASj
发送: WwwG3BZ4gPeAAMCLHVumDEUA6QloLxyY/RgtBAiMZzs2PeACaxGWARxIUGBC1hcFVhdgO7rw
发送: ggwW0EZfPqJCARIDJFw/ACDB8CgVBiQXoPYSFnDgBo93DZzyYuBlMJOGfPcthrcJ9I0XS4I7
发送: AAFVvOcHYK4tUJ5O9LhAHikAycE3x1nf+YDOCAsOAIEIFxxYxmkC+JYZXPLdJcB11all4RUH
发送: kHhCCAA7
发送: --Boundary-=_hMbeqwnGNoWeLsRMeKTIPeofyStu--
发送: .
响应: 250 Ok: queued as
发送: QUIT
响应: 221 Bye
邮件发送成功。
备注
使用动态IP或伪造域名发送的本地邮件可能会被邮件服务商列为垃圾邮件拒收。
参考资料
RFC2821
RFC2045
SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一种提供可靠且有效电子邮件传输的协议。SMTP是建模在FTP文件传输服务上的一种邮件服务,主要用于传输系统之间的邮件信息并提供来信有关的通知。
协议结构
SMTP命令是发送于SMTP主机之间的ASCII信息,下面列举了5个常用SMTP指令
HELO <服务商><域名><换行> 与SMTP服务器握手
MAIL <服务商> FROM:<发件人地址><换行> 传送发件人的邮箱地址
RCPT <服务商> TO:<收件人地址><换行> 传送收件人的邮箱地址
DATA <换行> 发送邮件数据,以新行.结束(包括信头与信体)
QUIT <换行> 与SMTP服务器断开连接
信头与信体
在DATA指令所传送的数据中,信头和信体以一个空行分隔,下面列举了部分常用信头
From: 发件人地址
To: 收件人地址
Subject: 邮件主题
Date: 发信时间
MIME-Version: MIME版本
Content-Type: 内容类型
Content-Transfer-Encoding: 编码方式
X-Priority: 优先级
X-Mailer: 代理发信的客户端
通过Socket发送SMTP邮件
1.Base64
Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式,它要求把每三个8位的字节转换为四个6位的字节,然后在6位字节的高位补两个0,最后组成四个8位的字节。在发送电子邮件时,服务器认证的用户名和密码需要用Base64编码,附件也需要用Base64编码。这种加密方式并非绝对安全,只能做到让人不能直接看出原本内容而已。
一个用Java实现的Base64的加/解密类
package org.gameeden.security; import java.io.ByteArrayOutputStream; /** * Base64编码/解码器。 * @author Sol */ public class Base64 { private final static char[] BASE64_ENCODING_TABLE; private final static byte[] BASE64_DECODING_TABLE; static { BASE64_ENCODING_TABLE="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray(); BASE64_DECODING_TABLE=new byte[] { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1 }; } private Base64() { } /** * 将数据进行Base64编码。 * @param data 数据 * @param offset 数据中的初始偏移量 * @param length 写入的字节数 * @return 编码后的字符串 */ public final static String encode(byte[] data,int offset,int length) { if(data==null) { return null; } StringBuffer buffer=new StringBuffer(); int[] temp=new int[3]; int end=offset+length; while(offset<end) { temp[0]=data[offset++]&255; if(offset==data.length) { buffer.append(BASE64_ENCODING_TABLE[(temp[0]>>>2)&63]); buffer.append(BASE64_ENCODING_TABLE[(temp[0]<<4)&63]); buffer.append('='); buffer.append('='); break; } temp[1]=data[offset++]&255; if(offset==data.length) { buffer.append(BASE64_ENCODING_TABLE[(temp[0]>>>2)&63]); buffer.append(BASE64_ENCODING_TABLE[((temp[0]<<4)|(temp[1]>>>4))&63]); buffer.append(BASE64_ENCODING_TABLE[(temp[1]<<2)&63]); buffer.append('='); break; } temp[2]=data[offset++]&255; buffer.append(BASE64_ENCODING_TABLE[(temp[0]>>>2)&63]); buffer.append(BASE64_ENCODING_TABLE[((temp[0]<<4)|(temp[1]>>>4))&63]); buffer.append(BASE64_ENCODING_TABLE[((temp[1]<<2)|(temp[2]>>>6))&63]); buffer.append(BASE64_ENCODING_TABLE[temp[2]&63]); } return buffer.toString(); } /** * 将数据进行Base64编码。 * @param data 数据 * @return 编码后的字符串 */ public final static String encode(byte[] data) { return encode(data,0,data.length); } /** * 将字符串进行Base64编码。 * @param str 字符串 * @return 编码后的字符串 */ public final static String encode(String str) { return encode(str.getBytes()); } /** * 对使用Base64编码的字符串进行解码。 * @param str 经过编码的字符串 * @return 解码后的数据 */ public final static byte[] decode(String str) { if(str==null) { return null; } ByteArrayOutputStream buffer=new ByteArrayOutputStream(); byte[] data=str.getBytes(); int[] temp=new int[4]; int index=0; while(index<data.length) { do { temp[0]=BASE64_DECODING_TABLE[data[index++]]; }while(index<data.length&&temp[0]==-1); if(temp[0]==-1) { break; } do { temp[1]=BASE64_DECODING_TABLE[data[index++]]; }while(index<data.length&&temp[1]==-1); if(temp[1]==-1) { break; } buffer.write(((temp[0]<<2)&255)|((temp[1]>>>4)&255)); do { temp[2]=data[index++]; if(temp[2]==61) { return buffer.toByteArray(); } temp[2]=BASE64_DECODING_TABLE[temp[2]]; }while(index<data.length&&temp[2]==-1); if(temp[2]==-1) { break; } buffer.write(((temp[1]<<4)&255)|((temp[2]>>>2)&255)); do { temp[3]=data[index++]; if(temp[3]==61) { return buffer.toByteArray(); } temp[3]=BASE64_DECODING_TABLE[temp[3]]; }while(index<data.length&&temp[3]==-1); if(temp[3]==-1) { break; } buffer.write(((temp[2]<<6)&255)|temp[3]); } return buffer.toByteArray(); } }
2.MIME类型
MIME意为多目Internet邮件扩展,它设计的最初目的是为了在发送电子邮件时附加多媒体数据,让邮件客户程序能根据其类型进行处理。每个MIME类型由两部分组成,前面是数据的大类别,后面为具体的种类。例如:image/bmp、image/jpeg、audio/mpeg
一个用于获取各种后缀名所对应MIME类型的工具类(MIME类型列表本来是存储在XML文件中的,此类的作用是读取XML中的数据,由于本文并不打算介绍XML解析,所以直接将数据写到了代码里)
package org.gameeden.mail; import java.util.Hashtable; /** * 用于获得MIME类型的工具类。 * @author Sol * @since 1.5 */ public final class MimeTypeFactory { private final static Hashtable<String,String> mimeTypes; static { mimeTypes=new Hashtable<String,String>(); mimeTypes.put("*","application/octet-stream"); mimeTypes.put("323","text/h323"); mimeTypes.put("acx","application/internet-property-stream"); mimeTypes.put("ai","application/postscript"); mimeTypes.put("aif","audio/x-aiff"); mimeTypes.put("aifc","audio/x-aiff"); mimeTypes.put("aiff","audio/x-aiff"); mimeTypes.put("asf","video/x-ms-asf"); mimeTypes.put("asr","video/x-ms-asf"); mimeTypes.put("asx","video/x-ms-asf"); mimeTypes.put("au","audio/basic"); mimeTypes.put("avi","video/x-msvideo"); mimeTypes.put("axs","application/olescript"); mimeTypes.put("bas","text/plain"); mimeTypes.put("bcpio","application/x-bcpio"); mimeTypes.put("bin","application/octet-stream"); mimeTypes.put("bmp","image/bmp"); mimeTypes.put("c","text/plain"); mimeTypes.put("cat","application/vnd.ms-pkiseccat"); mimeTypes.put("cdf","application/x-cdf"); mimeTypes.put("cer","application/x-x509-ca-cert"); mimeTypes.put("class","application/octet-stream"); mimeTypes.put("clp","application/x-msclip"); mimeTypes.put("cmx","image/x-cmx"); mimeTypes.put("cod","image/cis-cod"); mimeTypes.put("cpio","application/x-cpio"); mimeTypes.put("crd","application/x-mscardfile"); mimeTypes.put("crl","application/pkix-crl"); mimeTypes.put("crt","application/x-x509-ca-cert"); mimeTypes.put("csh","application/x-csh"); mimeTypes.put("css","text/css"); mimeTypes.put("dcr","application/x-director"); mimeTypes.put("der","application/x-x509-ca-cert"); mimeTypes.put("dir","application/x-director"); mimeTypes.put("dll","application/x-msdownload"); mimeTypes.put("dms","application/octet-stream"); mimeTypes.put("doc","application/msword"); mimeTypes.put("dot","application/msword"); mimeTypes.put("dvi","application/x-dvi"); mimeTypes.put("dxr","application/x-director"); mimeTypes.put("eps","application/postscript"); mimeTypes.put("etx","text/x-setext"); mimeTypes.put("evy","application/envoy"); mimeTypes.put("exe","application/octet-stream"); mimeTypes.put("fif","application/fractals"); mimeTypes.put("flr","x-world/x-vrml"); mimeTypes.put("gif","image/gif"); mimeTypes.put("gtar","application/x-gtar"); mimeTypes.put("gz","application/x-gzip"); mimeTypes.put("h","text/plain"); mimeTypes.put("hdf","application/x-hdf"); mimeTypes.put("hlp","application/winhlp"); mimeTypes.put("hqx","application/mac-binhex40"); mimeTypes.put("hta","application/hta"); mimeTypes.put("htc","text/x-component"); mimeTypes.put("htm","text/html"); mimeTypes.put("html","text/html"); mimeTypes.put("htt","text/webviewhtml"); mimeTypes.put("ico","image/x-icon"); mimeTypes.put("ief","image/ief"); mimeTypes.put("iii","application/x-iphone"); mimeTypes.put("ins","application/x-internet-signup"); mimeTypes.put("isp","application/x-internet-signup"); mimeTypes.put("jfif","image/pipeg"); mimeTypes.put("jpe","image/jpeg"); mimeTypes.put("jpeg","image/jpeg"); mimeTypes.put("jpg","image/jpeg"); mimeTypes.put("js","application/x-javascript"); mimeTypes.put("latex","application/x-latex"); mimeTypes.put("lha","application/octet-stream"); mimeTypes.put("lsf","video/x-la-asf"); mimeTypes.put("lsx","video/x-la-asf"); mimeTypes.put("lzh","application/octet-stream"); mimeTypes.put("m13","application/x-msmediaview"); mimeTypes.put("m14","application/x-msmediaview"); mimeTypes.put("m3u","audio/x-mpegurl"); mimeTypes.put("man","application/x-troff-man"); mimeTypes.put("mdb","application/x-msaccess"); mimeTypes.put("me","application/x-troff-me"); mimeTypes.put("mht","message/rfc822"); mimeTypes.put("mhtml","message/rfc822"); mimeTypes.put("mid","audio/mid"); mimeTypes.put("mny","application/x-msmoney"); mimeTypes.put("mov","video/quicktime"); mimeTypes.put("movie","video/x-sgi-movie"); mimeTypes.put("mp2","video/mpeg"); mimeTypes.put("mp3","audio/mpeg"); mimeTypes.put("mpa","video/mpeg"); mimeTypes.put("mpe","video/mpeg"); mimeTypes.put("mpeg","video/mpeg"); mimeTypes.put("mpg","video/mpeg"); mimeTypes.put("mpp","application/vnd.ms-project"); mimeTypes.put("mpv2","video/mpeg"); mimeTypes.put("ms","application/x-troff-ms"); mimeTypes.put("mvb","application/x-msmediaview"); mimeTypes.put("nws","message/rfc822"); mimeTypes.put("oda","application/oda"); mimeTypes.put("p10","application/pkcs10"); mimeTypes.put("p12","application/x-pkcs12"); mimeTypes.put("p7b","application/x-pkcs7-certificates"); mimeTypes.put("p7c","application/x-pkcs7-mime"); mimeTypes.put("p7m","application/x-pkcs7-mime"); mimeTypes.put("p7r","application/x-pkcs7-certreqresp"); mimeTypes.put("p7s","application/x-pkcs7-signature"); mimeTypes.put("pbm","image/x-portable-bitmap"); mimeTypes.put("pdf","application/pdf"); mimeTypes.put("pfx","application/x-pkcs12"); mimeTypes.put("pgm","image/x-portable-graymap"); mimeTypes.put("pko","application/ynd.ms-pkipko"); mimeTypes.put("pma","application/x-perfmon"); mimeTypes.put("pmc","application/x-perfmon"); mimeTypes.put("pml","application/x-perfmon"); mimeTypes.put("pmr","application/x-perfmon"); mimeTypes.put("pmw","application/x-perfmon"); mimeTypes.put("pnm","image/x-portable-anymap"); mimeTypes.put("pot,","application/vnd.ms-powerpoint"); mimeTypes.put("ppm","image/x-portable-pixmap"); mimeTypes.put("pps","application/vnd.ms-powerpoint"); mimeTypes.put("ppt","application/vnd.ms-powerpoint"); mimeTypes.put("prf","application/pics-rules"); mimeTypes.put("ps","application/postscript"); mimeTypes.put("pub","application/x-mspublisher"); mimeTypes.put("qt","video/quicktime"); mimeTypes.put("ra","audio/x-pn-realaudio"); mimeTypes.put("ram","audio/x-pn-realaudio"); mimeTypes.put("ras","image/x-cmu-raster"); mimeTypes.put("rgb","image/x-rgb"); mimeTypes.put("rmi","audio/mid"); mimeTypes.put("roff","application/x-troff"); mimeTypes.put("rtf","application/rtf"); mimeTypes.put("rtx","text/richtext"); mimeTypes.put("scd","application/x-msschedule"); mimeTypes.put("sct","text/scriptlet"); mimeTypes.put("setpay","application/set-payment-initiation"); mimeTypes.put("setreg","application/set-registration-initiation"); mimeTypes.put("sh","application/x-sh"); mimeTypes.put("shar","application/x-shar"); mimeTypes.put("sit","application/x-stuffit"); mimeTypes.put("snd","audio/basic"); mimeTypes.put("spc","application/x-pkcs7-certificates"); mimeTypes.put("spl","application/futuresplash"); mimeTypes.put("src","application/x-wais-source"); mimeTypes.put("sst","application/vnd.ms-pkicertstore"); mimeTypes.put("stl","application/vnd.ms-pkistl"); mimeTypes.put("stm","text/html"); mimeTypes.put("sv4cpio","application/x-sv4cpio"); mimeTypes.put("sv4crc","application/x-sv4crc"); mimeTypes.put("t","application/x-troff"); mimeTypes.put("tar","application/x-tar"); mimeTypes.put("tcl","application/x-tcl"); mimeTypes.put("tex","application/x-tex"); mimeTypes.put("texi","application/x-texinfo"); mimeTypes.put("texinfo","application/x-texinfo"); mimeTypes.put("tgz","application/x-compressed"); mimeTypes.put("tif","image/tiff"); mimeTypes.put("tiff","image/tiff"); mimeTypes.put("tr","application/x-troff"); mimeTypes.put("trm","application/x-msterminal"); mimeTypes.put("tsv","text/tab-separated-values"); mimeTypes.put("txt","text/plain"); mimeTypes.put("uls","text/iuls"); mimeTypes.put("ustar","application/x-ustar"); mimeTypes.put("vcf","text/x-vcard"); mimeTypes.put("vrml","x-world/x-vrml"); mimeTypes.put("wav","audio/x-wav"); mimeTypes.put("wcm","application/vnd.ms-works"); mimeTypes.put("wdb","application/vnd.ms-works"); mimeTypes.put("wks","application/vnd.ms-works"); mimeTypes.put("wmf","application/x-msmetafile"); mimeTypes.put("wps","application/vnd.ms-works"); mimeTypes.put("wri","application/x-mswrite"); mimeTypes.put("wrl","x-world/x-vrml"); mimeTypes.put("wrz","x-world/x-vrml"); mimeTypes.put("xaf","x-world/x-vrml"); mimeTypes.put("xbm","image/x-xbitmap"); mimeTypes.put("xla","application/vnd.ms-excel"); mimeTypes.put("xlc","application/vnd.ms-excel"); mimeTypes.put("xlm","application/vnd.ms-excel"); mimeTypes.put("xls","application/vnd.ms-excel"); mimeTypes.put("xlt","application/vnd.ms-excel"); mimeTypes.put("xlw","application/vnd.ms-excel"); mimeTypes.put("xof","x-world/x-vrml"); mimeTypes.put("xpm","image/x-xpixmap"); mimeTypes.put("xwd","image/x-xwindowdump"); mimeTypes.put("z","application/x-compress"); mimeTypes.put("zip","application/zip"); } private MimeTypeFactory() { } /** * 设置MIME类型。 * @param postfix 文件后缀名 * @param mimeType MIME类型 * @return 以前的MIME类型 */ public static String setMimeType(String postfix,String mimeType) { Object result=mimeTypes.put(postfix.toLowerCase(),mimeType); return result==null?(String)getMimeType("*"):(String)result; } /** * 获得MIME类型。 * @param postfix 文件后缀名 * @return MIME类型 */ public static String getMimeType(String postfix) { Object result=mimeTypes.get(postfix.toLowerCase()); return result==null?(String)getMimeType("*"):(String)result; } }
3.SMTP邮件发送实例
日志管理器
package org.gameeden.util; /** * 日志管理器。 * @author Sol */ public interface LogManager { /** * 输出。 * @param info 信息 */ public void output(String info); }
用Socket实现的SMTP邮件发送类
package org.gameeden.mail; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; import java.net.Socket; import java.net.SocketTimeoutException; import java.nio.charset.Charset; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Hashtable; import java.util.Locale; import java.util.regex.Pattern; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.InitialDirContext; import org.gameeden.security.Base64; import org.gameeden.util.LogManager; /** * SMTP邮件发送系统。 * * * 发件人和收件人的正确格式如下: * * 例1: "Sol"<sol@gameeden.org> * 例2: Sol<sol@gameeden.org> * 例3: <sol@gameeden.org> * 例4: sol@gameeden.org * * @author Sol * @since 1.5 */ public final class SmtpMailSender { /** * 发送成功的常量。 */ public final static boolean SUCCESSFUL=true; /** * 发送失败的常量。 */ public final static boolean FAILED=false; private final static int PORT=25;//服务器端口(SMTP服务器和邮件接收服务器的端口均为25) private final static int RETRY=3;//当连接SMTP服务器失败后尝试重新连接的次数(仅用于发送ESMTP邮件) private final static int INTERVAL=1000;//当连接SMTP服务器失败后重新连接的时间间隔(仅用于发送ESMTP邮件) private final static int TIMEOUT=10000;//网络连接的超时时间 private final static String BOUNDARY;//MIME分格符 private final static String CHARSET;//虚拟机的默认编码 private final static Pattern PATTERN;//用于效验邮箱地址的正确性 private static InitialDirContext dirContext;//用于查询DNS记录 private final ArrayList<LogManager> logManager;//日志管理器 private boolean isEsmtp;//发送类型 private String smtp;//SMTP服务器地址(仅用于发送ESMTP邮件) private String user;//用户名(仅用于发送ESMTP邮件) private String password;//密码(仅用于发送ESMTP邮件) private String sender;//发件人名字 private String senderAddress;//发件人的E-Mail地址 static { BOUNDARY="Boundary-=_hMbeqwnGNoWeLsRMeKTIPeofyStu"; CHARSET=Charset.defaultCharset().displayName(); PATTERN=Pattern.compile(".+@[^.@]+(\\.[^.@]+)+$");//此处放弃了传统匹配方式,这是为了兼容非英文域名的电子邮箱 Hashtable<String,String> hashtable=new Hashtable<String,String>(); hashtable.put("java.naming.factory.initial","com.sun.jndi.dns.DnsContextFactory"); try { dirContext=new InitialDirContext(hashtable); } catch(NamingException e) { } } private SmtpMailSender(String from) { if(from==null) { throw new IllegalArgumentException("参数from不能为null。"); } int leftSign=(from=from.trim()).charAt(from.length()-1)=='>'?from.lastIndexOf('<'):-1; senderAddress=leftSign>-1?from.substring(leftSign+1,from.length()-1).trim():from; if(!PATTERN.matcher(senderAddress).find()) { throw new IllegalArgumentException("参数from不正确。"); } sender=leftSign>-1?from.substring(0,leftSign).trim():null; logManager=new ArrayList<LogManager>(); isEsmtp=false; if(sender!=null) { if(sender.length()==0) { sender=null; } else if(sender.charAt(0)=='"'&&sender.charAt(sender.length()-1)=='"') { sender=sender.substring(1,sender.length()-1).trim(); } } } private SmtpMailSender(String address,String from,String user,String password) { this(from); isEsmtp=true; this.smtp=address; this.user=Base64.encode(user.getBytes()); this.password=Base64.encode(password.getBytes()); } /** * 创建SMTP邮件发送系统实例。 * @param from 发件人 * @return SMTP邮件发送系统的实例 * @throws IllegalArgumentException 如果参数from为null或格式不正确 */ public static SmtpMailSender createSmtpMailSender(String from) throws IllegalArgumentException { return new SmtpMailSender(from); } /** * 创建ESMTP邮件发送系统实例。 * @param smtp SMTP服务器地址 * @param from 发件人 * @param user 用户名 * @param password 密码 * @return SMTP邮件发送系统的实例 * @throws IllegalArgumentException 如果参数from为null或格式不正确 */ public static SmtpMailSender createESmtpMailSender(String smtp,String from,String user,String password) throws IllegalArgumentException { return new SmtpMailSender(smtp,from,user,password); } /** * 发送邮件。 * @param to 收件人 * @param subject 主题 * @param content 正文 * @param attachments 附件 * @param isHtml 使用网页形式发送 * @param isUrgent 紧急邮件 * @return 是否发送成功 * @throws IllegalArgumentException 如果参数to为null或格式不正确 */ public boolean sendMail(String to,String subject,String content,File[] attachments,boolean isHtml,boolean isUrgent) throws IllegalArgumentException { if(to==null) { throw new IllegalArgumentException("参数to不能为null。"); } int leftSign=(to=to.trim()).charAt(to.length()-1)=='>'?to.lastIndexOf('<'):-1; String addresseeAddress=leftSign>-1?to.substring(leftSign+1,to.length()-1).trim():to;//收件人的E-Mail地址 if(!PATTERN.matcher(addresseeAddress).find()) { throw new IllegalArgumentException("参数to不正确。"); } String addressee=leftSign>-1?to.substring(0,leftSign).trim():null;//收件人名字 boolean needBoundary=attachments!=null&&attachments.length>0; Socket socket=null; InputStream in=null; OutputStream out=null; byte[] data; try { if(addressee!=null) { if(addressee.length()==0) { addressee=null; } else if(addressee.charAt(0)=='"'&&addressee.charAt(addressee.length()-1)=='"') { addressee=addressee.substring(1,addressee.length()-1).trim(); } } if(isEsmtp) { for(int k=1;;k++) { try { log("连接: 主机:\""+smtp+"\" 端口:\""+PORT+"\""); socket=new Socket(smtp,PORT); break; } catch(IOException e) { log("错误: 连接失败"+k+"次"); if(k==RETRY) { return FAILED; } try { Thread.sleep(INTERVAL); } catch(InterruptedException ie) { } } } in=socket.getInputStream(); out=socket.getOutputStream(); if(response(in)!=220) { return FAILED; } } else { log("状态: 创建邮件接收服务器列表"); String[] address=parseDomain(parseUrl(addresseeAddress)); if(address==null) { return FAILED; } for(int k=0;k<address.length;k++) { try { log("连接: 主机:\""+address[k]+"\" 端口:\""+PORT+"\""); socket=new Socket(address[k],PORT); in=socket.getInputStream(); out=socket.getOutputStream(); if(response(in)!=220) { return FAILED; } break; } catch(IOException e) { log("错误: 连接失败"); } } } if(in==null||out==null) { return FAILED; } socket.setSoTimeout(TIMEOUT); sendString("HELO "+parseUrl(senderAddress),out); sendNewline(out); if(response(in)!=250) { return FAILED; } if(isEsmtp) { sendString("AUTH LOGIN",out); sendNewline(out); if(response(in)!=334) { return FAILED; } sendString(user,out); sendNewline(out); if(response(in)!=334) { return FAILED; } sendString(password,out); sendNewline(out); if(response(in)!=235) { return FAILED; } } sendString("MAIL FROM: <"+senderAddress+">",out); sendNewline(out); if(response(in)!=250) { return FAILED; } sendString("RCPT TO: <"+addresseeAddress+">",out); sendNewline(out); if(response(in)!=250) { return FAILED; } sendString("DATA",out); sendNewline(out); if(response(in)!=354) { return FAILED; } sendString("From: "+(sender==null?senderAddress:getBase64String(sender)+" <"+senderAddress+">"),out); sendNewline(out); sendString("To: "+(addressee==null?addresseeAddress:getBase64String(addressee)+" <"+addresseeAddress+">"),out); sendNewline(out); sendString("Subject: "+getBase64String(subject),out); sendNewline(out); sendString("Date: "+new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z (z)",Locale.US).format(new Date()),out); sendNewline(out); sendString("MIME-Version: 1.0",out); sendNewline(out); if(needBoundary) { sendString("Content-Type: multipart/mixed; BOUNDARY=\""+BOUNDARY+"\"",out); sendNewline(out); } else { if(isHtml) { sendString("Content-Type: text/html; charset=\""+CHARSET+"\"",out); sendNewline(out); } else { sendString("Content-Type: text/plain; charset=\""+CHARSET+"\"",out); sendNewline(out); } } sendString("Content-Transfer-Encoding: base64",out); sendNewline(out); if(isUrgent) { sendString("X-Priority: 1",out); sendNewline(out); } else { sendString("X-Priority: 3",out); sendNewline(out); } sendString("X-Mailer: BlackFox Mail[Copyright(C) 2007 Sol]",out); sendNewline(out); log("发送: "); sendNewline(out); if(needBoundary) { sendString("--"+BOUNDARY,out); sendNewline(out); if(isHtml) { sendString("Content-Type: text/html; charset=\""+CHARSET+"\"",out); sendNewline(out); } else { sendString("Content-Type: text/plain; charset=\""+CHARSET+"\"",out); sendNewline(out); } sendString("Content-Transfer-Encoding: base64",out); sendNewline(out); log("发送: "); sendNewline(out); } data=(content!=null?content:"").getBytes(); for(int k=0;k<data.length;k+=54) { sendString(Base64.encode(data,k,Math.min(data.length-k,54)),out); sendNewline(out); } if(needBoundary) { RandomAccessFile attachment=null; int fileIndex=0; String fileName; int k; data=new byte[54]; try { for(;fileIndex<attachments.length;fileIndex++) { fileName=attachments[fileIndex].getName(); attachment=new RandomAccessFile(attachments[fileIndex],"r"); sendString("--"+BOUNDARY,out); sendNewline(out); sendString("Content-Type: "+MimeTypeFactory.getMimeType(fileName.indexOf(".")==-1?"*":fileName.substring(fileName.lastIndexOf(".")+1))+"; name=\""+(fileName=getBase64String(fileName))+"\"",out); sendNewline(out); sendString("Content-Transfer-Encoding: base64",out); sendNewline(out); sendString("Content-Disposition: attachment; filename=\""+fileName+"\"",out); sendNewline(out); log("发送: "); sendNewline(out); do { k=attachment.read(data,0,54); if(k==-1) { break; } sendString(Base64.encode(data,0,k),out); sendNewline(out); }while(k==54); } } catch(FileNotFoundException e) { log("错误: 附件\""+attachments[fileIndex].getAbsolutePath()+"\"不存在"); return FAILED; } catch(IOException e) { log("错误: 无法读取附件\""+attachments[fileIndex].getAbsolutePath()+"\""); return FAILED; } finally { if(attachment!=null) { try { attachment.close(); } catch(IOException e) { } } } sendString("--"+BOUNDARY+"--",out); sendNewline(out); } sendString(".",out); sendNewline(out); if(response(in)!=250) { return FAILED; } sendString("QUIT",out); sendNewline(out); if(response(in)!=221) { return FAILED; } return SUCCESSFUL; } catch(SocketTimeoutException e) { log("错误: 连接超时"); return FAILED; } catch(IOException e) { log("错误: 连接出错"); return FAILED; } catch(Exception e) { log("错误: "+e.toString()); return FAILED; } finally { if(in!=null) { try { in.close(); } catch(IOException e) { } } if(out!=null) { try { out.close(); } catch(IOException e) { } } if(socket!=null) { try { socket.close(); } catch(IOException e) { } } } } /** * 给多个发件人发送邮件。 * @param to 收件人 * @param subject 主题 * @param content 正文 * @param attachments 附件 * @param isHtml 使用网页形式发送 * @param isUrgent 紧急邮件 * @return 任务状况 * @throws IllegalArgumentException 如果参数to为null或格式不正确 */ public boolean[] sendMail(String[] to,String subject,String content,File[] attachments,boolean isHtml,boolean isUrgent) throws IllegalArgumentException { boolean[] task=new boolean[to.length]; for(int k=0;k<task.length;k++) { task[k]=sendMail(to[k],subject,content,attachments,isHtml,isUrgent); } return task; } /** * 发送纯文本邮件。 * @param to 收件人 * @param subject 主题 * @param content 正文 * @return 是否发送成功 * @throws IllegalArgumentException 如果参数to为null或格式不正确 */ public boolean sendTextMail(String to,String subject,String content) throws IllegalArgumentException { return sendMail(to,subject,content,null,false,false); } /** * 发送HTML邮件。 * @param to 收件人 * @param subject 主题 * @param content 正文 * @return 是否发送成功 * @throws IllegalArgumentException 如果参数to为null或格式不正确 */ public boolean sendHtmlMail(String to,String subject,String content) throws IllegalArgumentException { return sendMail(to,subject,content,null,true,false); } /** * 给多个发件人发送纯文本邮件。 * @param to 收件人 * @param subject 主题 * @param content 正文 * @return 任务状况 * @throws IllegalArgumentException 如果参数to为null或格式不正确 */ public boolean[] sendTextMail(String[] to,String subject,String content) throws IllegalArgumentException { return sendMail(to,subject,content,null,false,false); } /** * 给多个发件人发送HTML邮件。 * @param to 收件人 * @param subject 主题 * @param content 正文 * @return 任务状况 * @throws IllegalArgumentException 如果参数to为null或格式不正确 */ public boolean[] sendHtmlMail(String[] to,String subject,String content) throws IllegalArgumentException { return sendMail(to,subject,content,null,true,false); } /** * 发送带附件的纯文本邮件。 * @param to 收件人 * @param subject 主题 * @param content 正文 * @param attachments 附件 * @return 是否发送成功 * @throws IllegalArgumentException 如果参数to为null或格式不正确 */ public boolean sendTextMail(String to,String subject,String content,File[] attachments) throws IllegalArgumentException { return sendMail(to,subject,content,attachments,false,false); } /** * 发送带附件的HTML邮件。 * @param to 收件人 * @param subject 主题 * @param content 正文 * @param attachments 附件 * @return 是否发送成功 * @throws IllegalArgumentException 如果参数to为null或格式不正确 */ public boolean sendHtmlMail(String to,String subject,String content,File[] attachments) throws IllegalArgumentException { return sendMail(to,subject,content,attachments,true,false); } /** * 给多个发件人发送带附件的纯文本邮件。 * @param to 收件人 * @param subject 主题 * @param content 正文 * @param attachments 附件 * @return 任务状况 * @throws IllegalArgumentException 如果参数to为null或格式不正确 */ public boolean[] sendTextMail(String[] to,String subject,String content,File[] attachments) throws IllegalArgumentException { return sendMail(to,subject,content,attachments,false,false); } /** * 给多个发件人发送带附件的HTML邮件。 * @param to 收件人 * @param subject 主题 * @param content 正文 * @param attachments 附件 * @return 任务状况 * @throws IllegalArgumentException 如果参数to为null或格式不正确 */ public boolean[] sendHtmlMail(String[] to,String subject,String content,File[] attachments) throws IllegalArgumentException { return sendMail(to,subject,content,attachments,true,false); } /** * 添加一个日志管理器。 * @param manager 日志管理器 */ public void addLogManager(LogManager manager) { logManager.add(manager); } /** * 移除日志管理器。 * @param manager 要移除的日志管理器 */ public void removeLogManager(LogManager manager) { logManager.remove(manager); } /** * 通过分析收件人邮箱域名的DNS记录获取邮件接收服务器地址。 * @param url 收件人邮箱域名 * @return 主机地址列表 */ private String[] parseDomain(String url) { try { NamingEnumeration records=dirContext.getAttributes(url,new String[]{"mx"}).getAll(); String[] address; String[] tmpMx; MX[] tmpMxArray; MX tmp; if(records.hasMore()) { url=records.next().toString(); url=url.substring(url.indexOf(": ")+2); address=url.split(","); tmpMxArray=new MX[address.length]; for(int k=0;k<address.length;k++) { tmpMx=address[k].trim().split(" "); tmpMxArray[k]=new MX(Integer.parseInt(tmpMx[0]),tmpMx[1]); } for(int n=1;n<tmpMxArray.length;n++) { for(int m=n;m>0;m--) { if(tmpMxArray[m-1].pri>tmpMxArray[m].pri) { tmp=tmpMxArray[m-1]; tmpMxArray[m-1]=tmpMxArray[m]; tmpMxArray[m]=tmp; } } } for(int k=0;k<tmpMxArray.length;k++) { address[k]=tmpMxArray[k].address; } return address; }//分析mx记录 records=dirContext.getAttributes(url,new String[]{"a"}).getAll(); if(records.hasMore()) { url=records.next().toString(); url=url.substring(url.indexOf(": ")+2).replace(" ",""); address=url.split(","); return address; }//分析a记录 return new String[]{url}; } catch(NamingException e) { log("错误: 域名\""+url+"\"无法解析"); return null; } } /** * 获得响应码。 * @param in 输入流 * @return 响应码 * @throws IOException 如果发生 I/O 错误。 */ private int response(InputStream in) throws IOException { byte[] buffer=new byte[1024]; int k=in.read(buffer); if(k==-1) { return -1; } String response=new String(buffer,0,k).trim(); log("响应: "+response); return Integer.parseInt(response.substring(0,3)); } /** * 输出字符串。 * @param str 字符串 * @param out 输出流 * @throws IOException 如果发生 I/O 错误。 */ private void sendString(String str,OutputStream out) throws IOException { log("发送: "+str); if(str==null) { str=""; } out.write(str.getBytes()); out.flush(); } /** * 写日志。 * @param info 信息 */ private void log(String info) { for(int n=0,m=logManager.size();n<m;n++) { logManager.get(n).output(info); } } /** * 输出一个换行符。 * @param out 输出流 * @throws IOException 如果发生 I/O 错误。 */ private static void sendNewline(OutputStream out) throws IOException { out.write('\r'); out.write('\n'); out.flush(); } /** * 获得字符串的Base64加密形式。 * @param str 字符串 * @return 加密后的字符串 */ private static String getBase64String(String str) { if(str==null||str.length()==0) { return ""; } StringBuffer tmpStr=new StringBuffer(); byte[] bytes=str.getBytes(); for(int k=0;k<bytes.length;) { if(k!=0) { tmpStr.append(' '); } tmpStr.append("=?"); tmpStr.append(CHARSET); tmpStr.append("?B?"); tmpStr.append(Base64.encode(bytes,k,Math.min(bytes.length-k,30))); tmpStr.append("?="); k+=30; if(k<bytes.length) { tmpStr.append('\r'); tmpStr.append('\n'); } } return tmpStr.toString(); } /** * 分析邮箱域名。 * @param address E-Mail地址 * @return 邮箱域名 */ private static String parseUrl(String address) { return address.substring(address.lastIndexOf('@')+1); } /** * MX记录。 */ private class MX { final int pri; final String address; MX(int pri,String host) { this.pri=pri; this.address=host; } } }
测试类
import java.io.File; import org.gameeden.mail.SmtpMailSender; import org.gameeden.util.LogManager; /** * 测试类。 */ public class TestSmtpMail { public static void main(String[] args) { SmtpMailSender sms=SmtpMailSender.createSmtpMailSender("\"Black Fox\"<blackfox@gameeden.org>"); // SmtpMailSender sms=SmtpMailSender.createESmtpMailSender("smtp.163.com","\"Object\"<java.lang.object@163.com>","java.lang.object","******"); sms.addLogManager(new LogPrinter());//添加日志管理器 if(sms.sendTextMail("\"Sol\"<114412367@qq.com>","STMP邮件测试","这是一封测试邮件。",new File[]{new File("java.gif")})==SmtpMailSender.SUCCESSFUL) { System.out.println("邮件发送成功。"); } else { System.out.println("邮件发送失败。"); } } } /** * 一个简单的日志管理器。 */ class LogPrinter implements LogManager { public void output(String info) { System.out.println(info);//将日志打印到控制台 } }
测试日志
状态: 创建邮件接收服务器列表
连接: 主机:"mx0.qq.com." 端口:"25"
响应: 220 mx7.qq.com ESMTP QQ Mail Server
发送: HELO gameeden.org
响应: 250 mx7.qq.com
发送: MAIL FROM: <blackfox@gameeden.org>
响应: 250 Ok
发送: RCPT TO: <114412367@qq.com>
响应: 250 Ok
发送: DATA
响应: 354 End data with .
发送: From: =?GBK?B?QmxhY2sgRm94?= <blackfox@gameeden.org>
发送: To: =?GBK?B?U29s?= <114412367@qq.com>
发送: Subject: =?GBK?B?U1RNUNPKvP6y4srU?=
发送: Date: Thu, 25 Oct 2007 10:42:13 +0800 (CST)
发送: MIME-Version: 1.0
发送: Content-Type: multipart/mixed; BOUNDARY="Boundary-=_hMbeqwnGNoWeLsRMeKTIPeofyStu"
发送: Content-Transfer-Encoding: base64
发送: X-Priority: 3
发送: X-Mailer: BlackFox Mail[Copyright(C) 2007 Sol]
发送:
发送: --Boundary-=_hMbeqwnGNoWeLsRMeKTIPeofyStu
发送: Content-Type: text/plain; charset="GBK"
发送: Content-Transfer-Encoding: base64
发送:
发送: 1eLKx9K7t+Ky4srU08q8/qGj
发送: --Boundary-=_hMbeqwnGNoWeLsRMeKTIPeofyStu
发送: Content-Type: image/gif; name="=?GBK?B?amF2YS5naWY=?="
发送: Content-Transfer-Encoding: base64
发送: Content-Disposition: attachment; filename="=?GBK?B?amF2YS5naWY=?="
发送:
发送: R0lGODlhOQA9AEQAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQAyAAAACwAAAAAOQA9AKT///+Z
发送: mcz/zMzu7u5mZpn/mZm7u7vMzMzd3d3/mczMZpnMZmbMM2aZZpkzM5nMmcz/zP+IiIj/ZpnM
发送: MzNmM5nMzP+qqqrMADPMmZmZZmYAAAAAAAAAAAAAAAAAAAAAAAAF/yAgjmRpnmiqrmzrvnAs
发送: z3Rtk0JRCMPtiwKJQgABQBISwa9WkPRMuSVroOMBhiqBUnoSLCZbwI6V4JoKjAUJUgCEUW+z
发送: pKAoSYDF1CBv3imeIlQiBYAne2ZuO20kCVuLKXFLdXUlj5Qpj1JjlyOLAmVWUJmSiSZlYj0J
发送: fCRqbpE0n64lA1utdCYLWwyvMwmpq7KxBRc8jwutCqM2i40lVrkLaAIMWNFAYFKnsYw9DLsD
发送: F62yAwrEXJaVYhMiC3U5nkPiS49jnQITRblU9G1JZkT2RiigBmBBoza0APgriEjQCAHJEjCY
发送: MFFBMgADdi1UiEhMl11eBrTaVW9QoSVRnP8xAMCAXA+DEO6kQ1RyRBKDQ7woPAmgGc1RIies
发送: u9eDE4kCwFAm01LgGEWMKwecOqGMi5YtEC5YbDXVRMKOJ6hBYFCFlxGwJxTkukPFJ9oWEhEY
发送: wECLh68TFQLoRcBFroEDgA0w4NtIy6ASevUScOCAgAEbAwzsHTEAAQYMD/4meJRyQIAGBCgQ
发送: AM3YQQAaARwfMPAXMAIErXgYyHBALgYLklMTCCDZc+jUp2UEeBCAgt4IBJKPTvy5gW7RAQZE
发送: CC4iNQACCA5Q4NniAPDafy3gZm3AgvPhu0RUOEBigAP2BAAgwD6jwgDvAfimDNQqI4/siT1Q
发送: 3WmeVVfDasQtxxv/YAdkYAAC0yiB3wN8jRAfANFhRF0NuWVWYQKn3JVCfgB4Z6ANedHnDAoD
发送: FELiAA2IEEGFNvAWwHSsMRgYcYmxN0IFfMHYIgE+3jCAcgdU9lptCLToZGWrFelZZSr60JsB
发送: uzHHXGtNmsCXXtyhdsADD4Q5AgLEPUbCjUVKYUBjjWmpZZKU5UajGQEkCZpyop2n1555vnUm
发送: aMbduBtriSmnZkee6WXAE1gmpyWgvN3ppqTcyYVcaZLq1aZXn7qwGwIb9lWqC8HllmdtLbL4
发送: 5BOAVJZbrC2Q+l10gPFoaAMRTDddA3su5gAFw0r6YAkVLMqCAcgJq5xyfjKnHKcETJdZMayr
发送: /UVDZJ2SqWUEwGap5bPiHvuDd1lymV22umr5l5k/QMlaZn81Ca+g+Oar774jhAAAIfkEABQA
发送: AAAsAAAAADkAKwCk////mZnMzMzM7u7u/8zMZmaZ3d3du7u7/5mZzJnM/8z/zDNmmWaZMzOZ
发送: ZjOZiIiIzGZmzMz//5nMzGaZmWZmzJmZ/2aZqqqqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
发送: Bf8gII5kaZ5oqq5s675wLM90bd94rsfE7gOS364nzBGLtyPSplzOms6YYhCtQasuKnam3cIG
发送: Xa/rKi4pw+XmdKQom98jMnagJrXdoiY4jueTgnl9IncjCCMShHhHBIYiEIKBIghEBI+QjQAL
发送: IwtyWwhtCI0QmH0KmgOaABOkfZwAEEEIE5CShgYLBqgrEQG9Bj4GBwcCAhQCBhBEFim9vQUN
发送: DQUHNgMHviMDFcTGAgBXAwEMBQ4F4tANATQB0gLCxMcJAAqGA+0HwcQJzgUB1uDj69LJCKDP
发送: Qa8HBRKWC9csAMJx/AY8EChiHYACBgQ4QONCQMBjw9olOHBBX7NjIyJbdMPWoFsBAAYwzohQ
发送: 75oJVgAGZGwWD0CvnAIpyhC5jkGvkO9+jfBIUKmIlz6pgMNhjeC9gb88VrzBS2aWMAF+DWAg
发送: 4oHTGv0c9kNK7IDJXitFRBDLAEyBuCZCAAAh+QQAFAAAACwAAAAAOQArAKT///+Zmczu7u7/
发送: zMzMzMz/mZlmZpnd3d27u7vMM2b/mczMZmbMZpn/zP/MmczMMzP/ZpkzM5mZZplmM5nMzP/M
发送: ADOIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF/yAgjmRpnmiqrmzrvnAsz4BQMMsC
发送: KXR/NouEoiEaFBaMgc83qFQS0BxjaCsQl7LFoCHodgcKRhJQUGJh15SCBz67RYKr+Y29Cuhn
发送: gXmOp9lEBX0+CmYMgj0JIwuHMzeAgYwmYAWUhGYCD3cNiZEkCg8LBWuUEDhBiwAJfJEDCXco
发送: rQMMD4adIwOrJVoCCWUQr7YKwCWyAw+QZMORlCdTDzwkuYzKIhUPuVy2KQWuJ4TaJwUV1CLd
发送: 4J5QKL3n0aoQJrzI7Km48gCt9ueOuJ7X8yQL7vADZO6fiHf3zEBAZXAEJD0AFHBqOALaPQBp
发送: KIpooESAgwPkSlAIQPIAnQMIEMAQWPlRwIGMJ0iSNBAhggEEPgQgKDnCJQGVP1MICCDBwAQD
发送: RWtGCEAjwM2fQAmATAjgwEqpQB3MNBBg51CjTpnKCKB1AkkLBtJyJSpz69EAAiyIFeEUgAGr
发送: E0KuIBAW68qUKdsGkDqCAgESAiIcNlD17gwKAvgGMNnCp0wHdJkOpdvjp1akJKOupCxC8kcS
发送: jAHArTG3x06yCEi/mAyAL2cfIx276EKCtgAJIizIbrpTbsqrf7XKPFzY5O8uBpifCAEAIfkE
发送: ABQAAAAsAAAAADkAKwCk////mZnM/8zM/5mZ7u7uzMzMzDNm/5nMZmaZzGZm3d3dzGaZu7u7
发送: /2aZ/8z/zJnMmWaZMzOZzDMzzMz/ZjOZiIiIqqqqzAAzzJmZAAAAAAAAAAAAAAAAAAAAAAAA
发送: AAAABf8gII5kaZKHYBJn675wrMZ0bQPHre/lwP+6AQtIhDWGxaQpoYQJBgmDNBFNLA5IErN5
发送: OkgHs9KTmhsJGtwSwZAIOw6DuCB7SJTb6dHaJxJIGwdwUAthAE9+eSMGfIYSKlkiKSULjGkO
发送: WyJtCxILJ3MkBgIOiZIjcZ0ujANohU2lfYAuDqgABiwCha08uD1QugADZQe0ZX1Ko2qGugaK
发送: SJW8RZ+Qhj1lArQElWBKBw7LJGsjCUgNhcxNc9sAa5jAfAKYAhJaleiU1FojhH0XM2u0iZ5A
发送: wsSMwIJQAGZJKJbIzKRIcRDCW9TwRKVsmSTwOciwIigSbxxIGWIAjccTCS7/brrn7qRFgAn7
发送: qavn0oUVYEOQ1XwBzwfMnS/K5TAJNEYnUUSLvniXVGmLMg8wKJhmYkKAqwrSKGDAoEABCwUU
发送: KPhp4upVBBEiIGBAhAADrHoeeC0AoYALAgEgIKCAQG/aCAF+BFhbgOtcBQ9EEGig4HDXAg/O
发送: IgjwFu/ewYF3BIhM4WoFBKD7XoVgdvDeyQQqZBYxGACCxhSoxiiAOexjBpEtWCgddsQEu3oi
发送: 2EUAQMFrHhMI0A6QFUW5FY3NJgZwdV3m1TsKRxZN2evj5iKWPwDvmjULvEnebmZAvgZzALRZ
发送: F7F6vAaBLO8JQBBRoT0PygGoZthc2vFGwgRZ6XcfBQLAnRACACH5BAAUAAAALAAAAAA5ACsA
发送: pP/////MzJmZzO7u7v+ZmczMzMwzZmZmmf+ZzN3d3cxmmbu7u8xmZv/M/8wzM8yZzJlmmf9m
发送: mTMzmWYzmcwAM8zM/5lmZoiIiMyZmaqqqgAAAAAAAAAAAAAAAAAAAAAAAAX/ICCOZGmeQBAg
发送: Kuq+MBwwjsEoBIEHce+XAYOBdwoQfkgYwkF8NZNQEcEw6A2qUZ/R4KBQbIRG7JlFzRyEwIAq
发送: MiqwKET5RUCPAhFT2nWcn6ZwKWRtgyIMfiYIBiQNK4UpYiU5iCWLAFNeaI8pJWuUjDk1X0Sb
发送: DZGGcp9tNUF9bU4jBIeqhocBCiWnf1KWtAgMPGEmjw24gLQib5d5JoEkDAgKvci4AAPAJ7oi
发送: ijfIJNUACDppziRBV94kzIIpOGZs6SSunEVDiuXpqSIN5QFM/lVmPFGgj5K2ALrWDPAnxt+T
发送: IN7IiMNCQM4QERe/bSrTwpwCYLZm3TIxK2LBbwCw/yGIMGhdRGWSAOACOGheOm3hLgG4iPNk
发送: PCApJolwhvCnCxXTOsayafSVS3bmIuCLxwMB0XJTqQ7wubGpiAcDHiQIpMZEBQFoE9BKsGBB
发送: gbcFMFx5suYJWrQHJEg4sGDOgAVpRwxga+Fthkj+XA0QAOHAhAON9UoQkEUA3wJt4SZIwASj
发送: WwwG3BZ4gPeAAMCLHVumDEUA6QloLxyY/RgtBAiMZzs2PeACaxGWARxIUGBC1hcFVhdgO7rw
发送: ggwW0EZfPqJCARIDJFw/ACDB8CgVBiQXoPYSFnDgBo93DZzyYuBlMJOGfPcthrcJ9I0XS4I7
发送: AAFVvOcHYK4tUJ5O9LhAHikAycE3x1nf+YDOCAsOAIEIFxxYxmkC+JYZXPLdJcB11all4RUH
发送: kHhCCAA7
发送: --Boundary-=_hMbeqwnGNoWeLsRMeKTIPeofyStu--
发送: .
响应: 250 Ok: queued as
发送: QUIT
响应: 221 Bye
邮件发送成功。
备注
使用动态IP或伪造域名发送的本地邮件可能会被邮件服务商列为垃圾邮件拒收。
参考资料
RFC2821
RFC2045
评论
7 楼
weipt
2012-05-08
麻烦问一下,这个怎么在android下应用?
6 楼
weipt
2012-05-08
非常好。十分感谢!
5 楼
taihe
2011-07-05
在发送附件的时候,我尝试将54修改微128或者1024,收到的附件都无法读取。
4 楼
skyfine
2009-03-15
非常感谢,学习中。。。。
3 楼
mengyang
2008-12-05
好帖子啊。。。
我做项目时用到javamail,但是发现有些地方根本无法实现(比如发送进度),最后还是要用到最基本的socket发邮件,还是基础重要啊。。。
我做项目时用到javamail,但是发现有些地方根本无法实现(比如发送进度),最后还是要用到最基本的socket发邮件,还是基础重要啊。。。
2 楼
hxsmile
2008-10-09
谢谢共享。
1 楼
folie2006
2008-10-07
不错,收藏起来
相关推荐
Java Socket编程是Java...通过阅读和理解这些代码,你可以深入学习如何在Java中使用Socket和SSL/TLS实现SMTP邮件发送。实践是掌握这些知识的最佳方式,建议动手尝试编写一个简单的SMTP邮件发送程序,以巩固理论知识。
在提供的"SocketForSmtp"压缩包文件中,可能包含了一个简单的Java示例代码,演示了如何用Socket实现SMTP邮件发送并支持SSL/TLS。这个示例代码可能包括了以上提到的所有步骤,包括错误处理和异常捕获,以便在遇到问题...
下面将详细介绍如何使用Java Socket实现SMTP发送邮件,并涵盖与QQ和Gmail邮箱的兼容性。 首先,为了通过SMTP发送邮件,我们需要理解SMTP的工作流程。SMTP服务器通常使用25号端口进行通信,但在启用SSL或TLS安全连接...
在这里,我们将使用 Java Socket 实现 SMTP 协议来发送邮件。 SMTP 协议简介 SMTP(Simple Mail Transfer Protocol)是一种用于发送电子邮件的协议。SMTP 协议工作在应用层,使用 TCP 协议作为传输层协议。SMTP ...
java socket发送SMTP邮件,支持SSL,支持发附件,抄送且发送的邮件标题内容都无乱码!
在"java_socket_smtp.rar"这个压缩包中,我们关注的是如何利用Java Socket来实现SMTP邮件的发送,而不依赖于JavaMail API。Base64编码是一种在网络上传输二进制数据时常用的编码方式,可以将任意的二进制数据转换为...
### 使用Socket编程实现电子邮件发送的关键知识点 #### 一、Socket编程基础 Socket编程是一种网络通信方式,它允许不同计算机上的程序进行数据交换。在互联网上,每个设备都有一个唯一的IP地址,而Socket则是通过...
在这个"基于Java的简单socket邮件发送"实验中,我们将深入探讨如何利用Java的Socket类来构建一个基本的邮件发送系统。 首先,我们需要了解Socket的工作原理。Socket是TCP/IP协议的一部分,它为两台计算机提供了一个...
使用Java的Socket编程实现SMTP邮件发送是一种直接且有效的方法,适用于对性能和控制有较高要求的应用场景。虽然现代的邮件发送库如JavaMail提供了更高级的功能和错误处理,但对于学习网络编程原理和SMTP协议来说,...
总之,Java编程实现邮件发送含身份验证是一个涉及网络编程、SMTP协议、JavaMail API和身份验证等多个知识点的综合任务。通过理解并应用这些概念,你可以创建一个可靠的邮件发送系统,满足各种业务需求。
总的来说,Java Socket结合SMTP协议可以实现基本的邮件发送功能,但实际应用中,使用专门的邮件库通常更为便捷和安全。然而,理解SMTP协议和Socket编程的基本原理对于开发者来说仍然非常重要,因为它有助于理解邮件...
- 一个简单的SMTP邮件发送示例可能包括打开Socket、创建输入输出流、发送SMTP命令、关闭连接等步骤。具体代码需要查看`Client.java`的内容。 请注意,由于没有实际的`Client.java`源码,以上解释是基于一般Java ...
在Java中,可以使用`java.net.Socket`创建到SMTP服务器的连接,并使用`PrintWriter`向服务器发送命令。`javax.mail`和`javax.mail.internet`包提供了`Session`、`Transport`、`MimeMessage`等类,用于构建和发送符合...
对于简单的邮件发送,使用Socket编程可能更容易理解,但对于复杂的场景,JavaMail API无疑更合适。 总的来说,通过Java的Socket实现EMAIL发送,虽然相对复杂,但有助于深入理解网络通信和SMTP协议的工作原理。如果...
Java Socket电子邮件发送与接收是基于Java网络编程技术实现的一种通信方式,主要涉及到Java的Socket类以及Email相关的API。本项目是一个课程项目,开发者通过几天的努力完成了一个具备图形用户界面(GUI)的邮件系统...
通过使用Java,开发人员可以有效地实现一个SMTP服务器,从而在本地网络或更广泛的互联网环境中促进电子邮件的传输。理解SMTP协议的基本原理和掌握Java网络编程技巧是这一过程中的关键。无论是用于教育目的,还是构建...
总之,通过JavaMail API和Socket通信,我们可以实现从Java应用程序中发送邮件的功能。这个项目提供了一个基础的示例,开发者可以根据实际需求进行扩展,比如支持HTML格式的邮件、添加多个收件人、处理抄送和密送等。...