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 【问题描述】:我今天收到一位客户记录的票证,报告说当他尝试发送附件时,php 的 mail()
函数在我们的一个 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 上是不是有点损坏?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用带有附件的 PEAR Mail 包使用 PHP 发送电子邮件
找不到所需的类(javax.activation.DataHandler 和 javax.mail.internet.MimeMultipart)。附件支持已禁用