我开发了一个基于 Beancount 的账本托管服务 HostedBeans,欢迎大家来了解纯文本复式记账并试用我的服务。
标签 #笔记

笔记:互联网电子邮件架构

果然写笔记是最好的读书方式,这里的笔记绝不是对知识点的堆砌,而是要让没读过这书的人有一个大概的了解,或者让读过书的人有新的发现。

最高效的阅读便是从书中为自己寻找论据。

在我的日志中,“笔记”前缀表示我这是现学现卖,对内容没太多把握。

电子邮件的出现要比Web早得多,一直以来都可以算是互联网的核心服务之一。

应该说由于历史遗留问题,目前电子邮件的架构并不完美,但一直都没有合格的替代品出现。

电子邮件使用SMTP协议被发送,SMTP是一个“简单”的,基于纯文本的交互式协议,位于TCP之上。当A向B发送邮件时,A向B在25端口上发起TCP连接,使用SMTP协议向B传输一份邮件,同时指明发信人地址和收信人地址。

这时,A和B就可以称为邮件服务器,通常一个邮件服务器是既可以收信也可以发信的,即同时是SMTP服务器和SMTP客户端。作为一台服务器,需要保持时刻在线,以便随时接收新邮件。显然,用户是不会直接使用服务器的。

在经典的电子邮件架构中,在每个用户的个人计算机上,都有一个本地SMTP代理,它负责在本地收集邮件,并定时发往服务器。引入本地SMTP代理后,A向B发送邮件时,用户A将写好的邮件交给本地SMTP代理(通常是个人计算机上的一个用户程序),本地SMTP代理通过SMTP协议将邮件发送到A的邮件服务器,A的邮件服务器再将邮件通过SMTP发送到B的邮件服务器。

本地SMTP代理和邮件服务器都会维护一个邮件队列,逐个发送邮件,当邮件无法送达时会进行几次重试,仍无法发送时会退回发信人。

邮件已经到达了B的邮件服务器,再来考虑B如何收信。B的个人计算机并不能保证时刻在线,也可能没有固定的IP地址,所以邮件服务器很难将新邮件发送到他的个人计算机上。

可以想象,SMTP是一个“推送”协议,当有新信息时,由发信人向收信人推送信息。而在从邮件服务器检查新邮件时,我们需要的可能是一种“拉取”协议,由收信人定时拉取新信息。

于是有了POP3, 它同样是在TCP之上的,基于纯文本的“简单”交互式协议。个人计算机上的POP3代理,定时使用POP3协议从邮件服务器检查新邮件,同时将新邮件下载到个人计算机。

除POP3以外还有功能更加复杂的IMAP协议,IMAP协议在POP3的基础上还支持同步阅读状态,搜索,建立文件夹对邮件进行分类等等功能。

SMTP代理和POP3(IMAP)代理分别代表了收信和发信,它们通常是一体的,称为邮件客户端,如Outlook, Thunderbird.

一封邮件除了正文之外,还可能有很多元信息,如收信人,发信人,日期,标题,收信来源等。这些键/值对信息以邮件报头的形式存在于一封邮件中,非常类似于HTTP中的报头。在早期的SMTP协议中,仅在邮件中支持ASCII字符,无法使用非ASCII字符(如中文),也无法使用格式文本和嵌入附件。于是出现了MIME标准,通过将邮件正文(base64)编码,并添加特殊的报头信息,即可实现使用非ASCII字符,使用HTML, 嵌入图片和附件等功能。

形如 m@jybox.net 的电子邮件地址指明了该邮箱位于 jybox.net 这台服务器上,对应的用户名是 m, 显然我们可以通过DNS查询到jybox.net所对应的IP, 但电子邮件服务器并非由通常的A记录指定,而是有专门的MX记录。发信人通过向DNS查询邮件地址中域名的MX记录来找到收信人的邮件服务器。

我们再来考虑安全性问题,SMTP是可选验证的。在本地SMTP代理向邮件服务器提交邮件时,通常是需要验证用户名和密码的,因为邮件服务器只希望向它的用户而不是所有人提供邮件转发服务。而在邮件服务器间发送的邮件则无需验证,因为你需要接收来自所有人的邮件。

至于邮件拉取协议(POP3, IMAP)显然也需要验证,因为一个人只应当能够查看自己邮箱里的邮件。

至此,还没有任何方式来鉴定一封邮件的真伪,任何人都可以在向收信人邮件服务器发送邮件时伪造发信人地址。为此,你可以通过DNS在一个域名的TXT记录中查询到一份被称为SPF的记录,该记录指明了经过认证的,用于该域名的发信服务器的地址,若发信人不是这个地址,则可以认为邮件是伪造的。除此之外,还有一些手段可以对SMTP进行加密,签名,工作量证明,但它们不是业界标准的一部分或应用较少,这里不再介绍。

Hotmail是当年最为成功的,基于Web的免费邮件服务,现在几乎所有邮件服务提供商均支持通过Web发送和接收邮件,甚至不再提供SMTP和POP3/IMAP访问服务。

不过随着智能手机的普及,SMTP和POP3/IMAP又回来了。

笔记: 循环, 递归, 迭代, 遍历

本文首发于SegmentFault, http://segmentfault.com/q/1010000000199577#a-1020000000199630

表示”重复”这个含义的词有很多, 比如循环(loop), 递归(recursion), 遍历(traversal), 迭代(iterate).

循环

循环算是最基础的概念, 凡是重复执行一段代码, 都可以称之为循环. 大部分的递归, 遍历, 迭代, 都是循环.

递归

递归的定义是, 根据一种(几种)基本情况定义的算法, 其他复杂情况都可以被逐步还原为基本情况.

在编程中的特征就是, 在函数定义内重复调用该函数.

例如斐波那契数列, 定义F(0)=1, F(1)=1, 所有其他情况: F(x)=F(x-1)+F(x-2).

所有大于1的整数经过有限次的反推之后都可以转换到两种基本情况. 而在编程中, 算法则是这样的:

int F(x)
{
    if(x==0 || x==1)
        return 1;    //这里是退出递归的条件, 以保证在有限次递归后能够得到结果
    return F(x-1)+F(x-2);    //转化为更为基本的情况, 重复调用自身进行计算
}

迭代(数学)

迭代在数学和编程中有不同的含义.

数学方面是指在循环的基础上, 每一次循环, 都比上一次更为接近结果.

例如下面是一个迭代的例子.

int result = 0;
for(int i=0; i>10; i++)
    result += i;    //每一次循环之后, result都更加接近结果45

有很多数学问题, 都是迭代算法, 如牛顿迭代法(求平方根).

迭代(编程)

按顺序访问一个列表中的每一项, 这在很多编程语言中表现为foreach语句:

$arr = [1, 2, 3, 4];
foreach($arr as $i)
    echo $i;

遍历

按一定规则访问一个非线性的结构中的每一项, 强调非线性结构(树, 图). 而迭代一般适用于线性结构(数组, 队列).

结论

  • 循环(loop) – 最基础的概念, 所有重复的行为
  • 递归(recursion) – 在函数内调用自身, 将复杂情况逐步转化成基本情况
  • (数学)迭代(iterate) – 在多次循环中逐步接近结果
  • (编程)迭代(iterate) – 按顺序访问线性结构中的每一项
  • 遍历(traversal) – 按规则访问非线性结构中的每一项

这些概念都表示“重复”的含义, 彼此互相交叉, 在上下文清晰的情况下, 不必做过于细致的区分.

参考

笔记:PHP和PEAR, PHAR, PECL

这三个东西可以算是PHP中的高新技术了, 既”高科技”, 又是新东西.

有PHP开发经验的可能见过这几个词, 但真心估计没几个详细了解过的.

它们有一个共同点, 都是用来管理PHP的扩展的.

PECL

即PHP Extension Community Library, PHP扩展库.

这里的扩展指的是使用C语言编写的动态链接库扩展, 通过php.ini配置, 并随PHP进程被装入内存.

常见的PECL扩展比如:

  • apc – 字节码缓存器
  • xdebug – 调试工具
  • PDO_xxoo – 数据库驱动
  • memcache, mongo – NoSQL数据库驱动
  • markdown – 文本处理器
  • zip – 压缩算法

这种动态链接库又分两种, 一种是用户空间扩展, 如mongo, markdown, 旨在通过C代码提升性能, 为PHP代码提供更好的抽象.

另一种是Zend扩展, 这是对Zend内核(PHP引擎内核)的扩展, 如apc, xdebug.

PECL在Windows上并不好用, 通常的做法是直接下载形式如php_xxoo.dll的已编译好的二进制动态链接库.

下载时还应该选择对应PHP版本号, 处理器指令集, 线程安全性的版本.

据说PECL的基础设施还在建设中, 目前有个简陋的网页(http://downloads.php.net/pierre/)可以下载到一部分编译好的Windows PECL扩展.

而在Linux则方便不少, 比如要安装apc, 只需:

pecl install apc

即可, pecl是PHCL的命令行工具, 可以自动完成扩展的安装(通常是下载源代码后自动编译)工作.

PEAR

即PHP Extension and Application Repository, PHP扩展和应用库.

这里的扩展和应用, 指的是用PHP编写的软件包, 一系列类库性质的PEAR包会被安装到PHP的根目录, 然后你就可以在你的代码中直接包含(require)这些类库.

常见的PEAR包如:

  • phpDocumentor – 文档提取工具
  • PHPUnit – 单元测试框架
  • DB – 数据库封装

PEAR希望创建一个规范化的PHP源代码包仓库, PEAR本身也对源代码的格式提出了一些要求, 以便于让源码包更加通用和规范.

同时PEAR也是PECL的上级项目, PECL是PEAR的一部分, pecl的命令行工具也包含在PEAR中.

#安装标配的源码包:
pear install db
#PEAR也采用了`软件源`的设计, 安装非标配的软件包如phpunit, 需要先添加频道(软件源)
pear channel-discover pear.phpunit.de
pear install phpunit

PHAR

PHAR即PHP Archive, 起初只是PEAR中的一个库而已, 后来在PHP5.3后被重新编写成C扩展并内置到PHP中.

PEAR用来将多个.php脚本打包(也可以打包其他文件)成一个.phar的压缩文件(通常是ZIP格式).

目的在于模仿Java的.jar, 不对, 目的是为了让发布PHP应用程序更加方便. 同时还提供了数字签名验证等功能.

.phar文件可以像.php文件一样, 被PHP引擎解释执行, 同时你还可以写出这样的代码来包含(require) .phar 中的代码.

require("xxoo.phar");
require("phar://xxoo.phar/xo/ox.php");

PEAR中的很多源码包都通过PHAR打包.

bcompiler

提到了PHAR, 我还不得不提一下bcompiler, PHP bytecode Compiler, PHP字节码编译器.

虽然它目前只是个实验性项目. 可以通过pecl安装.

很多PHP编写的商业软件, 有闭源的需要, 通常它们会选择Zend Guard等商业代码加密软件, 它们成熟稳定, 但也价格昂贵.

其实bcompiler就能满足这个需求, 它可以将PHP代码编译成字节码, 虽然保密性不及商业加密软件, 但也很难被轻易修改, 而且因为省去了生成字节码的过程, 会有不少的性能提升, 官方表示编译后体积会减少到原来的20%, 性能会提高30%.

使用bcompiler编译出的PHP字节码文件可以直接使用(就像PEAR那样):

// xxoo.exe 是 xxoo.php 编译后的字节码.
require("xxoo.exe");

题外

有空我写个在线的PHAR和bcompiler编译工具.

精子生于 1995 年,英文 ID jysperm.

订阅推送

通过 Telegram Channel 订阅我的博客日志、产品和项目的动态:

王子亭的博客 @ Telegram


通过邮件订阅订阅我的博客日志、产品和项目的动态(历史邮件):

该博客使用基于  Hexo  的  simpleblock  主题。博客内容使用  CC BY-NC-ND  授权发布。最后生成于 2023-12-20.