PHP 的 mail() 函数附件支持在 Windows 上是不是有点损坏?

Posted

技术标签:

【中文标题】PHP 的 mail() 函数附件支持在 Windows 上是不是有点损坏?【英文标题】:Is PHP's mail() function attachment support a bit broken on Windows?PHP 的 mail() 函数附件支持在 Windows 上是否有点损坏? 【发布时间】:2011-11-23 07:30:52 【问题描述】:

我今天收到一位客户记录的票证,报告说当他尝试发送附件时,phpmail() 函数在我们的一个 Windows 2003 Server 机器上超时。

经过调查,我能够重现他的问题。 mail() 函数处理包含大约 30-60Kb 大小的小附件的邮件需要 15-20 秒。大约 360-500Kb 的较大附件所用的时间超过了允许的最大脚本执行时间(90 秒)。

我能够在两台不同的 Windows 2003 服务器和一台 Windows 2008R2 服务器上重现该问题。我还尝试了三种不同版本的 PHP(5.2.14、5.2.17 和 5.3.6 - 所有 32 位和所有非线程安全版本,按照微软关于在 Windows 上运行 PHP 的建议)。

在所有情况下,邮件都是通过 SMTP 发送的(即不使用 sendmail 实现)。我尝试了三种不同的 SMTP 方案:

直接传送到我们的 SMTP 智能主机集群(运行 exim) 通过本地 IIS SMTP 服务传递,该服务中继到我们的智能主机 通过本地 IIS SMTP 服务交付,但使用 MX 查找和直接交付

尽管如此,发送附件仍然不是最理想的,这意味着不能将问题固定在慢速中继上。

然后我在没有出现任何这些问题的 CentOS 服务器上运行相同的代码,mail() 函数几乎立即返回。然而,这些服务器上的 PHP 被配置为使用 sendmail

然后我决定探索 PHP 源代码以找出 mail() 函数的实现是什么样的,并在 ext/standard/mail.c 中发现了这段代码:

if (!sendmail_path) 
#if (defined PHP_WIN32 || defined NETWARE)
    /* handle old style win smtp sending */
    if (TSendMail(INI_STR("SMTP"), &tsm_err, &tsm_errmsg, headers, subject, to, message, NULL, NULL, NULL TSRMLS_CC) == FAILURE) 
        if (tsm_errmsg) 
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", tsm_errmsg);
            efree(tsm_errmsg);
         else 
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", GetSMErrorText(tsm_err));
    
        return 0;
    
    return 1;
#else
    return 0;
#endif

TSendMail() 在另一个源文件 (win32/sendmail.c) 中实现。最终,所有发送到 SMTP 服务器的数据似乎都通过 sendmail.c 中名为 Post() 的函数同步传递,如下所示:

static int Post(LPCSTR msg)

    int len = strlen(msg);
    int slen;
    int index = 0;

    while (len > 0) 
        if ((slen = send(sc, msg + index, len, 0)) < 1)
            return (FAILED_TO_SEND);
        len -= slen;
        index += slen;
    
    return (SUCCESS);

send() 函数是 winsock2 函数。

我想知道缓冲区大小(根据下面的知识库文章默认为 8K)或缺乏调整是否会对大量数据产生一些影响。没有调用setsockopt() 来指定缓冲区大小或任何其他选项来优化对send() 的调用。

也许 Windows 上使用 SMTP 传递的 mail() 函数不适合用于发送大型电子邮件?

我很想知道是否有其他人看过这段代码或经历过同样的事情。

Design issues - Sending small data segments over TCP with Winsock

为了清楚起见,我们现在已经为客户 (SwiftMailer) 提供了一个替代解决方案,所以这不是关于获得替代方案的建议。

【问题讨论】:

您通常不会看到来自 a) 管理员和 b) 有很多代表的人提出的问题。必须是NP-Hard...:P 我想知道在这种情况下是否需要赏金。 mail() 功能很弱,千万不要将它用于任何不是超级基本的东西。我认为手册暗示了这一点 我认为这里的区别在于,当 mail() 使用 sendmail 时,PHP 在本地将消息传递给 sendmail,而无需实际等待该消息被传递(sendmail 在 mail() 返回后发送消息)所以它是本地连接速度很快,而如果 mail() 使用套接字,那么它实际上会等待发送此消息,因此 mail() 速度取决于外部连接速度。关于缓冲区大小 - 我认为这并不重要,因为最大 TCP 数据包有效负载大小通常约为 1.5k,因此 8k 缓冲区应该没问题 - 即使没有内部优化,也只有 6 个数据包中的 1 个数据包会过小。 @XzKto - 我猜这也是原因,虽然我没想到通过套接字发送会那么慢。无论如何,为什么不回答这个问题,我会将您标记为已接受。 【参考方案1】:

我的想法是在带有 PEAR Mail 类的 Windows 上使用 http://glob.com.au/sendmail/:http://pear.php.net/package/Mail。这可能是您遇到的延迟的解决方法。我认为这将无法绕过缓冲区,但我认为值得一试。

另外,我从未使用过它,但我听说过关于 SwiftMailer 的好消息:http://swiftmailer.org/

【讨论】:

没关系,我已经推荐了SwiftMailer,非常棒。

以上是关于PHP 的 mail() 函数附件支持在 Windows 上是不是有点损坏?的主要内容,如果未能解决你的问题,请参考以下文章

如何在php用mail发送邮件

PHP mail() 附件问题

如何使用带有附件的 PEAR Mail 包使用 PHP 发送电子邮件

使用 PHP Mail() 发送附件?

PHP通过SMTP实现发送邮件_包括附件

找不到所需的类(javax.activation.DataHandler 和 javax.mail.internet.MimeMultipart)。附件支持已禁用