$email = Encode::encode('utf-8',$email->as_string)
在将邮件发送给之前
sendmail(...)
要真正理解问题和解决方法,必须深入研究Perl中套接字中字符与八位字节的处理:
-
Email::Sender::Transport::SMTP
使用
Net::SMTP
它本身使用
syswrite
IO::Socket::SSL
或
IO::Socket::IP
(或
IO::Socket::INET
)套接字,具体取决于是否使用了SSL。
-
需要八位字节,它需要写入套接字的八位字节数。
-
但是,您使用的邮件
Email::Simple
ÑекÑÑ
当用UTF-8转换时,被视为5个字符,而它是10个八位字节。
-
电子邮件::发件人::传输::SMTP
只需将电子邮件的UTF8字符串转发到
在一个
系统写入
length
它给出的字符数与本例中的八位字节数不同。但在套接字站点上,它将从字符串中提取八位字节而不是字符,并将给定的长度视为八位字节数。
-
由于它将给定的长度视为八位字节,而不是字符,因此最终会像程序上层所期望的那样,向服务器发送更少的数据。
-
这样,邮件结束标记(带单点的行)就不会发送,因此服务器正在等待客户端发送更多数据,而客户端不知道要发送更多数据。
以一封仅由两个俄语字符组成的邮件为例。带有行尾和邮件尾标记,由7个字符组成:
ий\r\n.\r\n
и й \r \n . \r \n
d0 b8 d0 b9 0d 0a 2e 0d 0a
syswrite($fd,"ий\r\n.\r\n",7)
只写7个字符的前7个八位字节,但写9个八位字节长的字符串:
и й \r \n .
d0 b8 d0 b9 0d 0a 2e
这意味着邮件结束标记不完整。这意味着邮件服务器将等待更多数据,而邮件客户端不知道需要发送更多数据。这实际上会导致应用程序挂起。
有人可能会认为IO::Socket::SSL::syswrite应该以一种合理的方式处理UTF8数据,这是请求的,但在
RT#98732
. 但是
系统写入
Wide character in syswrite ...
相反
下一层是预期的
网络::SMTP
documentation of Net::SMTP::data
:
数据可以是对列表或列表的引用,并且
现在有人可能会说
Email::Transport
应该正确处理UTF8字符串
Email::Simple::as_string
首先不应返回UTF8字符串。
Email::MIME
并设置适当的内容传输编码。