摘要:本文在阐述电子邮件原理、相关网络协议和软件的基础上,提出一种按照流量计费的电子邮件服务器解决方案,并给出该方案的实现模型。
|
1 前言 电子邮件是internet服务的重要组成部分。随着internet技术日新月异的发展,电子邮件以其方便、快速、廉价和可靠的特点越来越赢得人们的喜爱。现在,电子邮件已经成为学术界、商业界最为流行的一种通信方式,和国外进行学术交流更少不了电子邮件。
在国外,电子邮件服务一般是一次性收取服务费,或者是商业广告性质的免费服务。但是,在cernet按照网络流量计费的大前提下,为公众提供电子邮件服务时,将不得不考虑流量计费的问题。
本文将从internet电子邮件原理入手,在阐明相关网络协议和软件的基础上,提供一种按照流量计费的电子邮件服务器解决方案,并给出该方案的实现模型。
2 internet邮件原理 2.1 internet电子邮件简介
为了保证电子邮件系统的正常运行,tcp/ip定义了一组协议,smtp(简单邮件传输协议)、pop3(邮局协议)和imap(internet消息通道协议)是主要的几个协议。它们的关系如图1所示:
|
| 图一 | 图1 internet邮件传送示意图
图1显示了电子邮件从发送到接收的一个单向的典型过程。smtp和pop3服务器是服务器软件,它们运行在邮件服务器上。smtp服务器负责接收待发送的邮件,并发送至目标邮件服务器的smtp服务器,由该smtp服务器写入用户邮箱。实际上,由于smtp服务器具有中转(relay)功能,它并不区分邮件是来自用户机(如普通pc)还是其他smtp服务器。如果用户想在普通客户机(没有smtp服务器的普通主机)上接收邮件的话,他需要通过pop3协议或imap协议从邮件服务器上获取。不同的是,pop3服务器要求用户将邮件取回本地的普通客户机进行维护,而imap则可以在服务器上直接维护,例如建立不同的邮件夹等。到目前为止,pop3的使用比imap要广泛的多,下面我们主要介绍smtp协议和pop3协议。
2.2 smtp协议
smtp(simple mail transfer protocol, rfc 821)是一个用7-bit基本ascii字符传送简单信文的邮件协议。它是一个独立的用户级协议,它要求一个可靠的数据通道。在tcp/ip协议中,这个通道是8-bit的tcp数据流,因此smtp的7-bit字节一律按照最高位为零的8-bit字节进行传输。如果要传送8-bit数据,需要用特殊的调制算法(例如mime)将其转为7-bit数据,在接收端再用相反的算法将其复原。
smtp的一个重要特点是“中转”(relay)。一般地,用户可以选择任意一台smtp服务器(如a)来发送邮件(只要能与该服务器建立传输层连接),若该服务器与目标smtp服务器b可以建立直接连接,则邮件将被直接送至目标服务器b;若不能建立直接连接,该smtp服务器将向其他所知的smtp服务器询问路由,假如有一台smtp服务器c可以与目标建立直接连接,或知道通向目标的路由,则邮件被转至服务器c,由服务器c向目标b转发。不管是从客户机到服务器的发送还是服务器间的中转,smtp使用同一套指令来进行连接和数据的接收发送,从而使得整个过程清晰简捷。
sendmail 是由美国加州大学开发的一个基于unix的共享smtp服务器软件,它支持多种unix的clone平台,如solaris、linux等,并且兼容很多其他类型的电子邮件系统,如uucp等。sendmail模型如图2所示。
|
| 图二 | 图2 sendmail模型
图2中,客户方sender与smtp服务器sendmail建立连接,将邮件送交sendmail服务器;sendmail按照邮件的不同类型,送交不同的邮件发送程序(称之为mailer)进行发送,例如smtp mailer,uucp mailer等等。sendmail提供一种local mailer程序mail.local,对邮件目标地址进行认证,若合法则将邮件写入用户的邮箱,否则将邮件退回。
2.3 pop3协议
pop3(post office protocol version 3, rfc 1939)定义了客户机从邮件服务器上获取邮件的一个简单的方法,它通过一组简单指令和应答实现与用户的交互操作。例如,用户通过user指令和pass指令实现身份认证,认证成功后可以通过retr指令收取邮件等。
加州大学针对smtp服务器sendmail,开发了一个共享的pop3服务器软件popper,该软件具有与sendmail相同的支持多平台和多种类型邮件的优点,并且在设计上采用结构清晰的状态机模型,其模型如图3所示。
|
| 图三 | 图3 popper状态机模型
如图3,系统初始状态为auth,身份认证通过后进入transact状态,系统邮箱被拷贝至一个临时文件。这一状态中,用户可以通过list命令列出邮件头的信息,通过retr指令将指定邮件取回本地机,通过dele命令将指定邮件标识为删除,等等。接到quit指令后,进入update状态,系统将没有被标识为删除的邮件反拷贝回系统邮箱,然后进入hault状态退出。
3 计费邮件服务器要解决的问题 3.1 计费信息获取
1. 流量。流量即邮件的长度,它是我们计费的基准。因为每一个来件都交由mail.local递送,所以在mail.local里可以容易地得到每个邮件的长度;另外,用户取信时与popper连接,popper要对信箱中每个邮件制作一个消息头(包括邮件的长度,在邮箱中的位置,是否被检索过等信息),同时也会获得邮箱的总长度,因此在popper中也可以获得流量,但程序上的开销会大一些(例如为了避免重复计费,在每次收信时需要判断邮件是否被检索过)。
2. 邮件的来源。目前在我国四大网(教育网cernet,科学院网casnet,电子部金桥网chinagbn和chinanet)内,网络流量是不计费的,因此应当根据邮件源地址是免费区域和非免费区域按照不同的标准计费。这需要获取邮件的源地址,源地址在mail.local和popper中都可以获得。此外,对于internet上一些反动或黄色站点的来信,应当禁止写到用户信箱里去。由于这个原因,我们应当在mail.local中就获得来信地址。
3. 用户取信的时间和客户机ip地址。这两种信息主要用于管理,例如,我们可以通过ip地址为用户限制可取信的客户机范围,从而可保护用户的邮件不被他人获取。另外,将这两条信息记入帐表后可为用户的查询提供极大的方便。这两种信息只能在popper中获得。
从上面的分析可以看到,我们需要一个基于mail.local和popper的综合解决方案,才能充分利用已有资源,在开销尽可能小的前提下获得以上信息。
|
| 图四 | 3.2 帐户管理问题
1. 为非unix用户提供邮件服务。每一个unix的用户都有一个邮件帐户,sendmail的缺省的帐户管理也是面向这些用户的,它需要对unix用户进行身份认证。例如,mail.local获得了一封给jdx的来信,要使用unix系统调用对“jdx”进行认证,如果系统没有一个叫做jdx的unix帐号,将返回空值,来信将被退回。在popper中除了以上步骤外,还要检查用户输入的密码与系统中密码是否相同。
这样的机制不适合对大量邮件用户的管理。首先,每增加一个邮件用户就需要建立一个unix帐号,用户所享受的将不仅是电子邮件服务,给用户权限管理带来不便;其次,对于大量帐号的快速检索也成问题。另外,所有的邮箱文件存放在一个目录下,例如/var/mail或/var/spool/mail下,在文件过多时,系统效率降低,还可能给管理带来麻烦。
因此,我们需要建立自己的帐户管理机制。这个机制应当能够实现:
1) 支持没有unix帐号的普通邮件帐户;
2) 用户的邮箱文件应能分类写入不同的目录;
3) 能够对用户信箱按照用户名进行快速检索。
2. 用户表的管理。用户表包括的信息有帐号,加密的口令,真实姓名,状态(正常,欠费,不受欢迎等等)。为了提供快速检索,用户表应当是一个排序的表。
用户表是一个动态的表,每天都可能有新用户加入,也可能有老用户注销。而且,计费邮件服务器要能根据用户的费用情况自动修改用户的权限。例如,用户申请帐号时需要预付一定数量的使用费,此时用户为正常用户;一旦使用超过限度(收取了大量邮件),系统将其置为欠费用户,暂时禁止取信,在用户补交足够的费用时,系统恢复其取信权利。因此,用户表应当及时更新。
为了减少管理难度,提供自我服务,应当向用户提供随时修改口令的机制。另外,用户表应当有一个安全可靠的后备,在用户表出现故障时能够及时地修复。
3.3 记帐的问题
用户每次取信的相关信息,例如时间、地点(ip)、取了多少封信、长度是多少等,都应当记录下来。作为专用邮件服务器,一般都要为大量的用户提供服务,假设有20000用户,每人每天取信一次。这样,一天大约有两万条记录,这些记录基本集中在从早上8点到晚上10点的14个小时(3600*14=50,400秒)内,即平均每2.5秒一条记录。在这样的情况下,同一时刻处理好几条记录的可能性非常大。如果将记录实时地交给数据库处理,将加重数据库负荷,延长响应时间,并增加丢失数据的可能性,严重时甚至可能导致系统崩溃。
一个解决办法是,每次的记录写入一个日志文件,每天在数据库负荷最低的时候,对这个文件进行批处理。
4 计费邮件服务器的实现 4.1 解决方案
针对上面提出的问题,我们可以设计如下解决方案:
1. 将计费邮件系统分为两个模块,邮件处理模块和数据库计费模块。邮件处理模块按照用户表进行常规的邮件收发和计费日志记录;数据库计费模块读计费日志、处理帐目、并更新用户表。两个模块通过共享用户表mail-usr-tab,邮件信息表mailmsgtab,和计费日志实现相互通信。
2. 修改mail.local,插入计费模块,以获取来信的源地址,识别邮件的长度及是否国外邮件,并将这些信息写到一个中间表mailmsgtab里;修改popper,插入计费模块,在用户使用popper取邮件时,将该邮件的信息从中间表mailmsgtab中取出来,连同用户的ip及取信时间一同写入日志文件,并将mailmsgtab中相应条目清空,以避免对同一封邮件的重复计费。这样,既获得了邮件的源地址,又获得了用户的本地ip。对于不受欢迎站点的来信,可以在mail.local中将其源地址识别出来之后,退回发送者或者丢弃。对于欠费用户或不受欢迎的用户,可以在用户通过popper获取邮件时拒绝用户的请求。
3. 如果直接由popper写计费日志的话,可能会出现几个进程同时写一个文件的情况。虽然用互锁机制可以避免写丢失,但在一个进程写的时候其它几个进程必须等待,延长了响应时间,降低了系统性能。因此采用一个专门的进程写计费日志,称为通信守护进程(cmpd,communication monitor process daemon)。所有的计费信息由popper使用udp包发送给它,由它写入日志。
4. 提供自我服务,开发一个自我服务程序,用户可以通过远程登录启动该程序的一个进程来修改在用户表中的密码,象常规的密码修改程序一样,普通用户首先需要使用旧密码进行身份认证,管理员可以修改任何用户的密码。
4.2 计费邮件服务器实现模型
根据上述解决方案,设计出计费邮件服务器实现模型如下:
图4 计费邮件服务器实现模型
整个计费邮件系统由sendmail、mail.local、popper、通信守护进程cmpd、计帐程序、密码修改程序及相关数据构成,其相互关系及工作流程在4.3节中说明,下面介绍主要数据结构。
1) 用户表mailusrtab
char name[17]; /* 用户名 */
char student_no[15];/* 学号/证号 */
char passwd[17]; /* 加密的密码 */
char realname[21]; /* 真实姓名 */
int usertype; /* 用户类型 */
char annotation[31];/* 备注 */
2) 信件信息表mailmsgtab
char name[15]; /* 用户名 */
int msgnum_f; /* 国外信件数 */
long size_f; /* 国外信件长 */
int msgnum_n; /* 国内信件数 */
long size_n; /* 国内信件长 */
3) 计费日志mailfee.log
char pktype; /* 国内/国外 */
char reserved[6]; /* 保留 */
char user[17]; /* 用户名 */
char student_no[15];/* 学号 */
struct tm recvdate; /* 取信时间 */
short msgnum; /* 信件数 */
long droplength; /* 信件长 */
char ipaddr[16]; /* 取信的ip */
char checksum; /* 校验和 */
4.3 系统的典型工作过程
下面我们用一个实际的例子来看系统的工作过程:
1. 一封寄给studmail@mail.cic. tsinghua.edu.cn的邮件到达邮件服务器mail.cic.tsinghua.edu.cn,smtp服务器程序sendmail接收该邮件并转交mail.local程序;mail.local首先判断该邮件是否从不受欢迎的站点寄来的信,若是则退回;若不是则查询用户表,确认本机有此用户后将邮件写入名为studmail的系统邮箱,同时将邮件信息写入邮件信息表mailmsgtab。
2. 在166.111.5.1的用户studmail在pop3客户程序中输入自己的帐号和口令,并与mail.cic.tsinghua.edu.cn的pop3服务器程序popper连接。popper查询用户表,如果是正常用户,由pop_dropcopy将系统邮箱拷贝至临时邮箱,同时将该用户在mailmsgtab表中的记录连同邮件总长度及用户ip作为udp包发送给守护进程cmpd,并清空mailmsgtab表中相应记录。用户可以通过其他pop3指令从临时邮箱中收取邮件,删除邮件等等,结束后由popper将没有删除的邮件反拷贝至系统邮箱;如果popper发现该用户是欠费用户,则将保存有催款信息的公告邮箱,而不是含有用户邮件的系统邮箱拷贝至临时邮箱,其他过程相同,这样用户只能看到催款信息,直到其标志恢复为正常用户。
3. cmpd将接收到的udp包进行校验,确认是正常的计费信息后写入日志文件。
4. 计帐程序在每天的指定时刻(一般为上线较少的深夜) 将日志文件写入数据库,同时把用户在用户表中修改过的密码写入数据库,最后根据数据库更新用户表mailusrtab(包括修改用户标志,增加新用户,删除旧用户等等)和邮件信息表mailmsgtab(为新用户增加表项)。
5. 密码修改程序作为tcp/ip超级服务器inetd的子服务程序,在用户使用远程登录时自动启动,为用户提供密码修改服务,并根据用户操作更新其在用户表mailusrtab中的相应表项。
4.4 系统性能分析
影响系统性能的主要问题包括以下几个方面:
1. cmpd是通过无连接的udp数据报来接收计费信息的,目的是避免用于连接的时间过长和消耗网络资源过多。但是,在上线用户很多的情况下,udp包的丢包概率有多大?从理论上讲,udp包的丢失最可能是传输信道的不可靠性,由于我们的发送方和接收方在同一台主机上,所以信道是相当可靠的。对此,我们在sun-ultra1这种不算高档的工作站上,用一个进程尽可能快地向cmpd发送udp包3,000个,结果无一遗漏地被接收。在实际应用中,也没有发现丢包现象。
2. 每封来信都要向邮件信息表mailmsgtab写信息,每次取信都要从mailmsgtab中读信息,在多大程度上会影响系统性能?在设计中,mailmsgtab同用户表mailusrtab一样采用用户名为关键字排序,并且其记录具有一一对应关系,因此,用户信息在mailusrtab和mailmsgtab中的相对位置是相同的。因为mail.local和popper都要检索mailusrtab表,所以对于mailmsgtab的检索可以省去,而直接找到正确的记录位置。在1000个用户的试运行期间,用户普遍反映系统较快,和没有计费的邮件系统没有差别。
5 结束语 对电子邮件按流量计费是一个符合中国国情的做法,它的实现有助于电子邮件在我国的普及,必将产生良好的社会效益和经济效益。按照以上的解决方案,我们成功开发了一套计费邮件系统。该系统在清华大学计算机与管理信息中心开放实验室的sun ultra1 170(solaris2.5)平台上对1000个用户进行了四个月的试运行,效果良好。同时在微机linux环境做了较小规模的测试,也是较为成功的。
参考文献:
1. j.postel. simply mail transport protocol (frc821). 1982.8
2. j.myers. post office protocol –version 3 (rfc1939). 1996.3
3. a.s.tanenbaum. computer network (version 3). 1996
4. eric allman. sendmail – installation and operation guide. 1996
|

文章整理:西部数码--专业提供
域名注册、
虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!