java编写SMTP邮件服务器
- 格式:doc
- 大小:1.08 MB
- 文档页数:21
某企业的邮件服务系统设计与实现摘要:电子邮件(E-mail)在如今社会有着举足轻重的地位。
随着人们使用次数的增多,通过发送邮件、收集客户资料的移动商务方式受到众多企业的青睐。
本文叙述了电子邮件的定义,分析了Java Mail API和相关协议,开发设计基于Java mail某企业的邮件服务系统。
关键词:SMTP; POP; Javamail; 邮件收发1引言电子信箱、电子邮政,它主要是指个人利用各种电子邮件方式将个人信息通过邮件进行交换的一种通信方式及其手段,在国际互联网中已经得到广泛的普及和应用。
在Internet网络资源中,电子邮箱系统并不是端对端的服务,其核心是存储和发送的服务。
利用收发存储的功能可以实现无连接通信,称作是一种异步通信[1]。
随着技术人员的能力逐渐提高,进一步开发了基于JavaMail邮件系统。
技术人员采用多线程的设计手段,基于JavaMail邮件系统的通信效率得到了提高,大大降低了电子邮件系统使用成本,邮件系统采用新的MVC设计模式的也大大降低了系统的维护难度。
2国内外研究现状从安全的角度考虑:过去传统的邮件系统保护措施不足,到今天为止,仍然有许多网络用户使用安全版本低的邮件系统,缺少安全保护能力。
全球的木马邮件和垃圾邮件问题逐渐恶化,也使得电子邮件系统发展过程中变得举步维艰。
许多用户和企业希望电子邮件系统能进一步加强加密、防木马、杜绝垃圾邮件的措施,解决电子邮件安全性问题。
针对电子邮件存在的漏洞,国内外的技术人员深入分析电子邮件系统,在系统每个层次面上作大量开发研究。
多进程/多线程的技术能大大提高邮件系统处理任务的效率,高速缓存使得电子邮件在服务器中高速数据交换,快速队列机制能够实现用户之间邮件快速收发。
3邮件协议和JavaMail API3.1邮件协议(1)SMTP:全称“Simple Mail Transfer Protocol”顾名思义叫简单邮件传输协协议。
一般人们统称SMTP。
JavaWeb(⼗三)使⽤javamail进⾏发送邮件,(使⽤QQ,163,新浪邮箱服务器) 加油加油。
--WZY⼀、发送邮件的原理 在了解其原理之前,先要知道两个协议,SMTP和POP3 SMTP:Simple Mail Transfer Protocol,即简单邮件传输协议,发送邮件的协议,默认端⼝,25 POP3:Post Office Protocol 3,即邮局协议,接收邮件的协议,默认端⼝,110 知道了这两个协议,下⾯来说说邮件发送和接收的原理图,分两种,相同邮件(QQ邮箱给QQ邮箱)和不同邮件(QQ邮箱给163邮箱)是不⼀样的。
解释: 为了⽅便起见,将新浪邮箱,list@称为⽤户A,将搜狐邮箱,wangwu@ 称为⽤户B ⽤户A(新浪邮箱,list@)给⽤户B(搜狐邮箱,wangwu@)发送⼀封邮件,那么整个过程就为图中的实线部分,⽤户A通过OutLook(浏览器或者客户端)登录⾃⼰的邮箱帐号,编写邮件,使⽤Smtp协议发送给Sina的邮件服务器中的Smtp服务器(专门⽤来发送的服务器),然后在通过SMTP协议,传输给Sohu的邮件服务器中的Smtp服务器,然后通过Sohu的Smtp服务器将邮件发送给⽤户B的存储设备进⾏存储(每个⽤户都会有⼀个存储空间⽤来存储邮件的),到这⾥,⽤户A就相当于发送成功了,因为已经到达了⽬的地,如果B⽤户需要查看邮件内容,必须通过POP3服务器将从⾃⼰的存储设备中拿到,然后返回到浏览器或者客户端中显⽰。
⽤户B给⽤户A发送⼀封邮件,那么整个过程就为图中的虚线部分,是和A给B发⼀样的步骤 ⽤户A给⼀个同样使⽤新浪邮箱的⽤户C发送⼀封邮件,那么其过程就简单的多了,先通过Smtp服务器,然后smtp服务器会将其发送到⽤户C的存储设备上,A发送邮件就成功了,⽤户C要显⽰⾃⼰邮箱中的邮件,那么就通过POP3服务器从⾃⼰存储设备中拿取所有邮件进⾏查看。
⼆、通过Java代码实现发送邮件 2.1、准备jar包 核⼼:mail.jar 依赖:activation.jar,邮件需要发送附件时使⽤ 2.2、使⽤163邮箱发送邮件 2.2.1、⾸先在163邮箱中注册⼀个帐号。
Java实现发送邮件,图⽚,附件1、JavaMail 介绍JavaMail 是sun公司(现以被甲⾻⽂收购)为⽅便Java开发⼈员在应⽤程序中实现邮件发送和接收功能⽽提供的⼀套标准开发包,它⽀持⼀些常⽤的邮件协议,如前⾯所讲的SMTP,POP3,IMAP,还有MIME等。
我们在使⽤JavaMail API 编写邮件时,⽆须考虑邮件的底层实现细节,只要调⽤JavaMail 开发包中相应的API类就可以了。
JavaMail 下载地址:2、JavaMail APIJavaMail API 按照功能可以划分为如下三⼤类:①、创建和解析邮件的API②、发送邮件的API③、接收邮件的API以上三种类型的API在JavaMail 中由多个类组成,但是主要有四个核⼼类,我们在编写程序时,记住这四个核⼼类,就很容易编写出Java邮件处理程序。
Message 类:javax.mail.Message 类是创建和解析邮件的核⼼ API,这是⼀个抽象类,通常使⽤它的⼦类javax.mail.internet.MimeMessage 类。
它的实例对象表⽰⼀份电⼦邮件。
客户端程序发送邮件时,⾸先使⽤创建邮件的 JavaMail API 创建出封装了邮件数据的 Message 对象,然后把这个对象传递给邮件发送API(Transport 类)发送。
客户端程序接收邮件时,邮件接收API把接收到的邮件数据封装在Message 类的实例中,客户端程序在使⽤邮件解析API从这个对象中解析收到的邮件数据。
Transport 类:javax.mail.Transport 类是发送邮件的核⼼API 类,它的实例对象代表实现了某个邮件发送协议的邮件发送对象,例如 SMTP 协议,客户端程序创建好Message 对象后,只需要使⽤邮件发送API 得到 Transport 对象,然后把 Message 对象传递给 Transport 对象,并调⽤它的发送⽅法,就可以把邮件发送给指定的 SMTP 服务器。
网络邮件系统电子邮件作为Internet 最为广泛的应用之一,在人们的工作和生活中扮演着重要的角色,应用的普及使电子邮件技术有了飞速的发展。
网络电子邮件系统在当今的互联网上比比皆是,不管是windows 上的普及,还linux 上的稳定可靠和低廉;不管是分布式系统,还是基于P2P ,都离不开邮件系统的基本原理。
邮件传输原理在Internet 上将一段文本信息从一台计算机传送到另一台计算机上,可通过两种协议来完成,即 SMTP(Simple Mail Transfer Protocol ,简单邮件传输协议)和POP3(Post Office Protocol ,邮局协议3)。
SMTP 是Internet 协议集中的邮件标准。
在Internet 上能够接收电子邮件的服务器都有SMTP 。
电子邮件在发送前,发件方的SMTP 服务器与接收方的SMTP 服务器联系,确认接收方准备好了,则开始邮件传递;若没有准备好,发送服务器便会等待,并在一段时间后继续与接收方邮件服务器联系。
这种方式在Internet 上称为“存储——转发”方式。
POP3可允许E-mail 客户向某一SMTP 服务器发送电子邮件,另外,也可以接收来自SMTP 服务器的电子邮件。
换句话说,电子邮件在客户PC 机与服务提供商之间的传递是通过P0P3来完成的,而电子邮件在 Internet 上的传递则是通过SMTP 来实现。
如图所示:注:服务器A 是发送邮件服务器(SMTP ),服务器B 是接收邮件服务器(POP3/IMAP )1. 电子邮件的发送和接收电子邮件在Internet 上发送和接收的原理可以很形象地用我们日常生活中邮寄包裹来形容:当我们要寄一个包裹的时候,我们首先要找到任何一个有这项业务的邮局,在填写完收件人姓名、地址等等之后包裹就寄出而到了收件人所在地的邮局,那么对方取包裹的时候就必须去这个邮局才能取出。
同样的,当我们发送电子邮件的时候,这封邮件是由邮件发送服务器(任何一个都可以)发出,并根据收信人的地址判断对方的邮件接收服务器而将这封信发送到该服务器上,收信人要收取邮件也只能访问这个服务器才能够完成。
javamaven项⽬实现发送邮件1,先导⼊⼀个jar包在pom.xml 中写⼊<!-- 邮件start --><dependency><groupId>javax.mail</groupId><artifactId>mail</artifactId><version>1.4.7</version></dependency><!-- 邮件end -->2,写⼀个邮件的类包括,发送内容,端⼝等public class MailSenderInfo {// 发送邮件的服务器的IP和端⼝private String mailServerHost;private String mailServerPort = "25";// 邮件发送者的地址private String fromAddress;// 邮件接收者的地址private String toAddress;// 登陆邮件发送服务器的⽤户名和密码private String userName;private String password;// 是否需要⾝份验证private boolean validate = false;// 邮件主题private String subject;// 邮件的⽂本内容private String content;// 邮件附件的⽂件名private String[] attachFileNames;private String iwallHost ;/*** 获得邮件会话属性*/public Properties getProperties(){Properties p = new Properties();p.put("mail.smtp.host", this.mailServerHost);p.put("mail.smtp.port", this.mailServerPort);p.put("mail.smtp.auth", validate ? "true" : "false");return p;}public String getMailServerHost() {return mailServerHost;}public void setMailServerHost(String mailServerHost) {this.mailServerHost = mailServerHost;}public String getMailServerPort() {return mailServerPort;}public void setMailServerPort(String mailServerPort) {this.mailServerPort = mailServerPort;}public boolean isValidate() {return validate;}public void setValidate(boolean validate) {this.validate = validate;}public String[] getAttachFileNames() {return attachFileNames;}public void setAttachFileNames(String[] fileNames) {this.attachFileNames = fileNames;}public String getFromAddress() {return fromAddress;}public void setFromAddress(String fromAddress) {this.fromAddress = fromAddress;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getToAddress() {return toAddress;}public void setToAddress(String toAddress) {this.toAddress = toAddress;}public String getUserName() {return userName;}public void setUserName(String userName) {erName = userName;}public String getSubject() {return subject;}public void setSubject(String subject) {this.subject = subject;}public String getContent() {return content;}public void setContent(String textContent) {this.content = textContent;}public String getIwallHost() {return iwallHost;}public void setIwallHost(String iwallHost) {this.iwallHost = iwallHost;}}3,为了可配置性,可将上⾯的类的所需信息写⼊配置⽂件⾥<bean id = "mailInfo" class = "xxxxxxx.MailSenderInfo" ><property name="mailServerHost" value = ""></property> <property name="mailServerPort" value = "25"></property><property name="fromAddress" value = "xxxxxxx@xx.xxx"></property><property name="userName" value = "xxxxxxxx"></property><property name="password" value = "xxxxx"></property><property name="iwallHost" value = "http://localhost:8080/xxxxx"></property> </bean>4,写⼀个简单的发送邮件的类,包含发送text或者 html的⽅法public class SimpleMailSender {/*** 以⽂本格式发送邮件* @param mailInfo 待发送的邮件的信息*/public boolean sendTextMail(MailSenderInfo mailInfo) {// 判断是否需要⾝份认证MyMailAuthenticator authenticator = null;Properties pro = mailInfo.getProperties();if (mailInfo.isValidate()) {// 如果需要⾝份认证,则创建⼀个密码验证器authenticator = new MyMailAuthenticator(mailInfo.getUserName(), mailInfo.getPassword()); }// 根据邮件会话属性和密码验证器构造⼀个发送邮件的sessionSession sendMailSession = Session.getDefaultInstance(pro,authenticator);try {// 根据session创建⼀个邮件消息Message mailMessage = new MimeMessage(sendMailSession);// 创建邮件发送者地址Address from = new InternetAddress(mailInfo.getFromAddress());// 设置邮件消息的发送者mailMessage.setFrom(from);// 创建邮件的接收者地址,并设置到邮件消息中Address to = new InternetAddress(mailInfo.getToAddress());mailMessage.setRecipient(Message.RecipientType.TO,to);// 设置邮件消息的主题mailMessage.setSubject(mailInfo.getSubject());// 设置邮件消息发送的时间mailMessage.setSentDate(new Date());// 设置邮件消息的主要内容String mailContent = mailInfo.getContent();mailMessage.setText(mailContent);// 发送邮件Transport.send(mailMessage);return true;} catch (MessagingException ex) {ex.printStackTrace();}return false;}/*** 以HTML格式发送邮件* @param mailInfo 待发送的邮件信息*/public static boolean sendHtmlMail(MailSenderInfo mailInfo){// 判断是否需要⾝份认证MyMailAuthenticator authenticator = null;Properties pro = mailInfo.getProperties();//如果需要⾝份认证,则创建⼀个密码验证器if (mailInfo.isValidate()) {authenticator = new MyMailAuthenticator(mailInfo.getUserName(), mailInfo.getPassword()); }// 根据邮件会话属性和密码验证器构造⼀个发送邮件的sessionSession sendMailSession = Session.getDefaultInstance(pro,authenticator);try {// 根据session创建⼀个邮件消息Message mailMessage = new MimeMessage(sendMailSession);// 创建邮件发送者地址Address from = new InternetAddress(mailInfo.getFromAddress());// 设置邮件消息的发送者mailMessage.setFrom(from);// 创建邮件的接收者地址,并设置到邮件消息中Address to = new InternetAddress(mailInfo.getToAddress());// Message.RecipientType.TO属性表⽰接收者的类型为TOmailMessage.setRecipient(Message.RecipientType.TO,to);// 设置邮件消息的主题mailMessage.setSubject(mailInfo.getSubject());// 设置邮件消息发送的时间mailMessage.setSentDate(new Date());// MiniMultipart类是⼀个容器类,包含MimeBodyPart类型的对象Multipart mainPart = new MimeMultipart();// 创建⼀个包含HTML内容的MimeBodyPartBodyPart html = new MimeBodyPart();// 设置HTML内容html.setContent(mailInfo.getContent(), "text/html; charset=utf-8");mainPart.addBodyPart(html);// 将MiniMultipart对象设置为邮件内容mailMessage.setContent(mainPart);// 发送邮件Transport.send(mailMessage);return true;} catch (MessagingException ex) {ex.printStackTrace();}return false;}}5,在类中,实现发送邮件,建议另起线程发送邮件/*** 发送邮件内部类**/class SendEmailThread implements Runnable {private MailSenderInfo mailInfo;public SendEmailThread(MailSenderInfo mailInfo) {this.mailInfo = mailInfo;}@Overridepublic void run() {try {if (this.mailInfo != null) {SimpleMailSender.sendHtmlMail(mailInfo);}} catch (Exception e) {e.printStackTrace();}}}/*** 发送邮件⽅法** @param account* @param code*/public void sendEmail(String account, String code) {WebApplicationContext ac = ContextLoader.getCurrentWebApplicationContext();// 获取当前服务IPMailSenderInfo mailInfo = (MailSenderInfo) ac.getBean("mailInfo");mailInfo.setValidate(Boolean.TRUE);mailInfo.setToAddress(account);mailInfo.setSubject("验证码邮件");mailInfo.setContent("<p>您好!</p><p>感谢您注册成为xxxxxx⽤户!</p><p>您的验证码是:" + code + "。
如何实现局域网内的邮件服务器搭建在现代社会中,电子邮件已经成为人们日常生活和工作中不可或缺的一部分。
为了在局域网内实现高效的邮件交流和管理,搭建一个局域网内的邮件服务器是非常重要的。
本文将介绍如何实现局域网内的邮件服务器搭建,帮助读者了解相关的基本概念和步骤。
一、选择合适的邮件服务器软件要搭建一个局域网内的邮件服务器,首先需要选择合适的邮件服务器软件。
市面上有许多不同的邮件服务器软件可供选择,如Microsoft Exchange Server、Postfix、Sendmail等。
根据实际需要和个人偏好,选择最适合自己的邮件服务器软件。
二、配置域名和DNS解析在搭建邮件服务器之前,需要为局域网内的邮件服务器配置域名和DNS解析。
域名是邮件服务器所在局域网的唯一标识,可以通过向域名注册商购买一个合适的域名。
DNS解析则是将域名解析为IP地址,使得邮件服务器可以被其他设备访问到。
三、安装和配置邮件服务器软件选择合适的邮件服务器软件后,需要按照软件提供的安装指南进行安装和配置。
不同的邮件服务器软件安装和配置的步骤会有所不同,因此读者需要仔细阅读邮件服务器软件的官方文档或者使用指南,按照指引完成安装和配置。
四、设置邮件服务器的基本参数在安装和配置完成后,需要设置邮件服务器的基本参数。
这些参数包括邮件服务器的名称、IP地址、端口号等。
需要根据实际情况进行设置,确保邮件服务器能够顺利地在局域网内运行。
五、配置用户账户和权限一个完善的邮件服务器需要具备用户账户和权限管理的功能。
在搭建局域网内的邮件服务器时,需要配置合适的用户账户和权限,控制用户对邮件服务器的访问和操作权限。
这有助于提高邮件服务器的安全性和管理效率。
六、设置邮件规则和过滤器邮件服务器还可以设置各种邮件规则和过滤器,以提供更加便捷和个性化的邮件服务。
通过设置邮件规则和过滤器,可以实现自动转发、自动回复、垃圾邮件过滤等功能。
可以根据实际需求,配置适合自己的邮件规则和过滤器。
Java基于JavaMail实现向QQ邮箱发送邮件最近项⽬在做新闻爬⾍,想实现这个功能:爬⾍某个页⾯失败后,把这个页⾯的 url 发到邮箱。
最终实现的效果图如下,后期可以加上过滤标签、失败状态码等,⽅便分类搜索异常。
开发⼈员可以根据邮件⾥的 url 和堆栈信息,分析爬⾍失败的原因。
是不是服务器 down 了?还是爬⾍的 Dom 解析没有解析到内容?还是正则表达式对于这个页⾯不适⽤?开启SMTP服务在 QQ 邮箱⾥的设置->账户⾥开启 SMTP 服务注意开启完之后,QQ 邮箱会⽣成⼀个授权码,在代码⾥连接邮箱使⽤这个授权码⽽不是原始的邮箱密码,这样可以避免使⽤明⽂密码。
⽹上查了⼀下例⼦,根据这篇⽂章 Java Mail(⼆):JavaMail介绍及发送⼀封简单邮件的⽰例代码。
Properties props = new Properties();// 开启debug调试props.setProperty("mail.debug", "true");// 发送服务器需要⾝份验证props.setProperty("mail.smtp.auth", "true");// 设置邮件服务器主机名props.setProperty("mail.host", "");// 发送邮件协议名称props.setProperty("mail.transport.protocol", "smtp");Session session = Session.getInstance(props);//邮件内容部分Message msg = new MimeMessage(session);msg.setSubject("seenews 错误");StringBuilder builder = new StringBuilder();builder.append("url = " + "/never_cxb/article/details/50524571");builder.append("页⾯爬⾍错误");builder.append("\n data " + TimeTool.getCurrentTime());msg.setText(builder.toString());//邮件发送者msg.setFrom(new InternetAddress("**发送⼈的邮箱地址**"));//发送邮件Transport transport = session.getTransport();transport.connect("", "**发送⼈的邮箱地址**", "**你的邮箱密码或者授权码**");transport.sendMessage(msg, new Address[] { new InternetAddress("**接收⼈的邮箱地址**") });transport.close();但是报错了DEBUG SMTP: AUTH LOGIN command trace suppressedDEBUG SMTP: AUTH LOGIN failedException in thread "main" javax.mail.AuthenticationFailedException: 530Error: A secure connection is requiered(such as ssl). More information at /cgi-bin/help?id=28因为⽰例代码是⽤的163邮箱,⽽笔者是 QQ 邮箱,看 Log 分析是 QQ 邮箱需要 SSL 加密。
oracle数据库发送邮件功能Oracle数据库如何来执行java代码实现发邮件功能Oracle数据库可以通过使用Java代码来实现发送邮件功能。
以下是实现此功能的步骤:1. 配置SMTP服务器:首先,在Oracle数据库中配置SMTP服务器的信息。
可以使用以下命令进行配置:````EXECUTE DBMS_NETWORK_ACL_ADMIN.CREATE_ACL('smtp_acl.xml','ACL for SMTP', 'your_email_address', TRUE, 'connect');EXECUTE DBMS_NETWORK_ACL_ADMIN.ADD_PRIVILEGE('smtp_acl.xml', 'your_email_address', 'connect', TRUE);EXECUTE DBMS_NETWORK_ACL_ADMIN.ASSIGN_ACL('smtp_acl.xml','your_database_user');COMMIT;```这些命令将创建一个ACL(访问控制列表)并将其分配给数据库用户,以允许其连接到SMTP服务器。
2. 创建Java存储过程:接下来,创建一个Java存储过程,用于发送电子邮件。
可以使用以下代码作为示例:````javaCREATE OR REPLACE AND RESOLVE JAVA SOURCE NAMED "EmailSender" ASimport java.util.*;import javax.mail.*;import javax.mail.internet.*;public class EmailSenderpublic static void sendEmail(String recipient, String subject, String body)final String senderEmail = "your_sender_email_address";final String senderPassword = "your_sender_email_password";Properties props = new Properties(;props.put("mail.smtp.auth", "true");props.put("mail.smtp.starttls.enable", "true");props.put("mail.smtp.host", "your_smtp_host");props.put("mail.smtp.port", "your_smtp_port");Session session = Session.getInstance(props, newjavax.mail.Authenticatoprotected PasswordAuthentication getPasswordAuthenticatio return new PasswordAuthentication(senderEmail, senderPassword);}});tryMessage message = new MimeMessage(session);message.setFrom(new InternetAddress(senderEmail));message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipient));message.setSubject(subject);message.setText(body);Transport.send(message);System.out.println("Email sent successfully.");} catch (MessagingException e)}}}/```在上述代码中,需要将`your_sender_email_address`、`your_sender_email_password`、`your_smtp_host`和`your_smtp_port`替换为实际的SMTP服务器和发件人的信息。
Javamail代码+tomcat配合ccproxy实现邮件自动发送1.利用javamail可以实现邮件发送功能,若局域网中web服务器不能访问外网(如不能访问 :25 ),需要一台能上外网的服务器做代理服务器,安装ccproxy软件,设置如下:端口映射目标地址端口为需要访问的外网服务地址,本地端口可以随意设置,不要提示有冲突即可。
账号建立:IP地址对应web服务器IP地址2.部署代码的服务器设置如下:(1)public class MyTimerTask extends TimerTask {public void run() {---等等网上提供很多邮件发送的代码props.setProperty("mail.smtp.port", "119");transport.connect("172.20.109.xx", "xxxxx", "xxxxxxx");以上两句是修改后的,指向ccproxy代理服务器IP和端口映射规则的端口。
(2)public class TestAutoRun extends HttpServlet{private static final long serialVersionUID = 1L;//当servlet类被加载时,执行本函数public void init(ServletConfig config) throws ServletException{super.init(config);System.out.println("** ServletConfig 初始化 ...") ;//此处放置需要自动执行的代码Timer timer = new Timer();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date date;try {String sql="select date_sub(curdate(),interval -1 day) a;";ResultSet rs = new JDBConnection().executeQuery(sql);rs.absolute(1);String dd=rs.getString("a");date = sdf.parse(dd+" 09:10:10");timer.schedule( new MyTimerTask(),date, 86400000 );rs.close();new JDBConnection().close(rs);} catch (Exception e) {e.printStackTrace();}}public void destroy(){}(3) web.xml配置<servlet><servlet-name>TestAutoRun</servlet-name><servlet-class>com.wy.TestAutoRun</servlet-class><load-on-startup>1</load-on-startup></servlet>-<servlet-mapping><servlet-name>TestAutoRun</servlet-name><url-pattern>/servlet/TestAutoRun</url-pattern></servlet-mapping>关于tomcat自动任务执行两次的情况,可以修改tomcat6011\conf\server.xml <Host name="localhost" appBase="" unpackWARs="true" autoDeploy="true"xmlValidation="false" xmlNamespaceAware="false">。
11.1 电子邮件系统工作原理电子邮件是因特网上最为流行的应用之一,它主要包括如下组成部分:用户代理、邮件服务器、简单邮件传输协议(simple Mail Transfer Protocol,简称SMTP)、邮件消息格式、邮件访问协议。
图11.1展示了因特网电子邮件系统的概貌。
在下文对电子邮件系统各组成部分的说明中,以发信人A1ice给收信人Bob发送电子邮件消息作为例子。
图11.1 因特网电子邮件系统概貌1.用户代理用户代理提供用户阅读、保存、回复、转发、编写、发送和删除邮件消息等功能。
Alice写完电子邮件消息后,其用户代理把这个消息发送给Alice邮箱所在的邮件服务器,再由该邮件服务器把这个消息放入外出消息队列中并负责发送到Bob邮箱所在的邮件服务器。
当Bob想阅读电子邮件消息时,其用户代理将从其邮件服务器上的邮箱中取得邮件。
20世纪90年代后期,图形用户界面(GUI)的电子邮件用户代理变得流行起来,允许用户阅读和编写多媒体消息。
当前流行的用户代理包括Outlook、Foxmail等。
2.邮件服务器邮件服务器构成了电子邮件系统的核心。
每个收信人都有一个位于某个邮件服务器上的邮箱(mailbox)。
Bob的邮箱用于管理和维护已经发送给他的邮件消息。
一个邮件消息的典型流程是从发信人的用户代理开始,经发信人邮箱所在的邮件服务器,中转到收信人邮箱所在的邮件服务器,然后投递到收信人的邮箱中。
当Bob想查看自己的邮箱中的邮件消息时,其邮箱所在的邮件服务器将以他提供的用户名和口令认证他。
Alice邮箱所在邮件服务器还必须处理Bob邮箱所在邮件服务器出故障的情况。
如果Alice方的邮件服务器无法把邮件消息立即递送到Bob方的邮件服务器,则A1ice方的服务器就把它们存放在消息队列(message queue)中,以后再尝试递送。
这种尝试通常每30分钟左右执行一次:要是过了若干天仍未尝试成功,该服务器就把这个消息从消息队列中删除,同时以另一个邮件消息通知发信人(即Alice)。
3.简单邮件传输协议SMTPSMTP是因特网电子邮件系统重要的应用层协议。
它使用由TCP提供的可靠的数据传输服务把邮件消息从发信人邮箱所在邮件服务器传送到收信人邮箱所在邮件服务器。
SMTP是客户-服务器应用模式,由发信人的邮件服务器执行的客户端和收信人的邮件服务器执行的服务器端组成。
SMTP的客户端和服务器端同时运行在每个邮件服务器上。
当一个邮件服务器向其它邮件服务器发送邮件消息时,它是作为SMTP客户端。
当一个邮件服务器从其它邮件服务器接收邮件消息时,它是作为SMTP服务器端。
SMTP规范定义在RFC 821中,它的作用是把邮件消息从发信人的邮件服务器传送到收信人的邮件服务器。
SMTP限制所有邮件消息的信体必须是简单的7位ASCII字符格式。
这个限制使得二进制多媒体数据在由SMTP传送之前必须编码成7位ASCII文本;SMTP传送完毕之后,再把相应的7位ASCII文本邮件消息解码成二进制数据。
下面通过假设Alice给Bob发送一个简单的ASCII文本邮件消息的情形来说明SMTP的基本操作:(1) Alice调用自己的电子邮件用户代理,给出Bob的电子邮件地址(例如bob@),写好邮件内容,然后让用户代理发送本邮件消息。
(2) Alice的用户代理把该邮件消息发送到其邮件服务器中,由邮件服务器把该消息放入某个消息队列中。
(3) 运行在A1ice的邮件服务器上的SMTP客户端看到消息队列中的这个邮件消息后,打开一个到运行在Bob的邮件服务器主机上的SMTP服务器端的TCP连接。
(4) 经过最初的一些SMTP握手之后,SMTP客户把A1ice的邮件消息发送到TCP连接上。
(5) 在Bob的邮件服务器主机上,SMTP服务器收到这个邮件消息后,把这个消息投递到Bob 的邮箱中。
(6) Bob在方便的时候调用自己的电子邮件用户代理阅读该邮件消息。
图11.2展示了上述情形。
图11.2 A1ice的邮件服务器把邮件消息传送到Bob的邮件服务器SMTP通常不使用中间的邮件服务器主机中转邮件。
如果Bob的邮件服务器不工作了,那么A1ice发给Bob的邮件消息将存留在Alice的邮件服务器中等待新的尝试,而不会存放到某个中间的邮件服务器中。
SMTP协议与现实社会人们面对面交互的礼仪之间有许多相似之处。
首先,运行在发送端邮件服务器主机上的SMTP客户,发起建立一个到运行在接收端邮件服务器主机上的SMTP服务器端口号25之间的TCP连接。
如果接收邮件服务器当前不工作,SMTP客户就等待一段时间后再尝试建立该连接。
这个连接建立之后,SMTP客户和服务器先执行一些应用层握手操作。
就像人们在转手东西之前往往先自我介绍那样,SMTP客户和服务器也在传送信息之前先自我介绍一下。
在这个SMTP握手阶段,SMTP客户向服务器分别指出发信人和收信人的电子邮件地址。
彼此自我介绍完毕之后,客户发出邮件消息。
SMTP使用传输层提供的可靠数据传输服务(TCP服务)把该消息无差错地传送到服务器。
如果客户还有其它邮件消息需发送到同一个服务器,它就在同一个TCP连接上重复上述过程;否则,它就指示TCP关闭该连接。
假设客户所在主机名为,服务器所在主机名为。
前面标以“C:”的ASCII文本行是客户发送到它的TCP套接字中的完整文本行,前面标以“S:”的ASCII文本行是服务器发送到它的TCP套接字中的完整文本行。
一个客户和服务器交互的例子如下(以下传输脚本在TCP连接建立之后发生):S: 220 C: HELO S: 250 Hello , pleased to meet youC: MAIL FROM: <alice@>S: 250 Sender OKC: RCPT TO: bob@S: 250 Recipient OKC: DATAS: 354 Enter mail, end with "." on a line by its selfC: Do you like ketchup?C: How about pickles?C: .S: 250 Message accepted for deliveryC: QUITS: 221 closing connection在这个例子中,客户发送了一个从邮件服务器主机到的邮件消息,信体内容为:“Do you like ketchup? How about pickles?”。
客户总共发出了5个命令。
其中HELO命令标识发信人自己的身份;MAIL FROM命令表示请求发送邮件,初始化邮件传输;RCPT TO命令标识某电子邮件的计划接收人;DATA命令表示所有的邮件接收人已标识,并初始化数据传输,以.结束;QUIT命令表示退出邮件发送过程,结束会话。
服务器给每个命令发回应答,其中每个应答都由应答码和一些英语解释(可选)构成。
SMTP使用持久连接,如果发送邮件服务器有多个邮件消息需发送到同一个接收邮件服务器,那么所有这些消息可以在同一个TCP连接中发送。
对于其中的每一个消息,客户以一个新的“HELO ”命令开始整个消息发送过程,但是QUIT命令要等到所有消息都发送完之后才发出。
一旦SMTP 把Alice发给Bob的邮件消息从Alice的邮件服务器传送到Bob的邮件服务器,该邮件消息就存放在Bob的邮箱中。
4.邮件消息格式当Alice给Bob发一封普通的邮政信件时,她把这封信装入一个信封里,在信封上写明Bob 的地址和自己的回信地址,然后投入邮箱;邮政业务在递送这封信的过程中,也会把表明时间和地点的邮戳盖在信封上。
类似地,当电子邮件消息从一个人传送到另一个人时,在信体之前会有一个含有这些外围信息的信头。
这些信息由一系列在RFC 822中定义的邮件消息头部及其值构成。
邮件消息中构成信头的各个头部和信体之间以一个空行(即CRLF)分割。
RFC 822详细说明了各个邮件消息头部的格式和含义。
邮件消息的每个头部都是直观可读的文本,由一个后跟冒号的关键字和相应的值构成。
这些头部有些是必须的,有些则是可选的。
每个信头必须有一个From:头部和一个To:头部。
还可以有一个Subject:头部和其他头部。
这些头部和前面讨论的SMTP命令不是一回事:SMTP命令是SMTP握手协议的一部分,邮件消息头部则属于邮件消息的一部分。
下面是一个典型的电子邮件信头:From: alice@To: bob@Subject: this is a letter信头之后空一行就是信体。
SMTP RFC822要求每个邮件消息的信体以单个点号构成的一行做结束标记,换用ASCII字符形式,就是每个邮件消息的信体必须以“CRLF.CRLF”结尾,其中CR和LF分别代表回车符和换行符。
这种方式下,当从同一个SMTP客户接收一系列邮件消息时,SMTP服务器可以通过在字节流中搜索“CRLF.CRLF”来分割每个消息。
RFC 822中说明的邮件消息头部尽管足以满足发送普通ASCII文本邮件的要求,但是在多媒体消息(例如,包含图像、音频或视频数据的消息)的描述和非ASCII文本格式(例如,非英语国家使用的文字)的承载上,却显然不够,例如,若信体是JPEG图像的二进制数据,那么这些二进制数据字节流中可能出现“CRLF.CRLF”模式。
这将导致SMTP服务器误认为当前邮件消息已结束。
为避免这样的问题,二进制数据应以某种方式编码成ASCII文本,保证其中不存在特定的ASCII 字符(包括点号)。
要发送非ASCII文本的邮件消息,必须由发送者的用户代理在其中增添额外的头部。
RFC 2045和RFC 2046定义了这些额外的头部,它们是针对RFC 822的多用途因特网邮件扩展(Multipurpose Internet Mail Extensions, 简称MIME)。
支持多媒体的两个关键MIME头部是Content-Type:和Content-Tansfer-Encoding:。
Content-Type:头部允许接收用户代理对邮件消息采取合适的行动。
例如,通过指出信体内容为一个JPEG图像,接收用户代理可以把信件定向到某个JPEG解压缩例程。
为确保SMTP正常工作,非ASCII文本消息必须预先编码成ASCII文本格式。
Content-Tansfer-Encoding:头部用于告知接收用户代理信体已被编码成ASCII格式,并指出具体编码方式。
这样,当某个用户代理收到一个包含这两个头部的邮件消息时,它首先使用Content-Tansfer-Encoding:头部的值把信体转换成原始的非ASCII文本形式,再使用Content-Type:头部的值确定自己应该对信体采取什么行动。