是的,就是这么简单。因为我使用的是 SendGrid 的免费服务,每天最多只能发送200封邮件,并且没有独立IP地址,因为我发送出去的邮件很可能不能通过垃圾邮件过滤器。
使用W orker 角色接收邮件
在C#下很难找到一个好用且免费的接收邮件的开发库,我使用了 Eric Daugherty 的 C# Email Server (CSES)接收邮件,并添加了 SharpMime 工具来处理解码有附件的MIME邮件,接收邮件的代码最主要要完成两件事:
1、在 OnStart() 中启动一个 TcpListener 监听合适的端口。
2、在 Run() 中启动一个循环,处理每个入站的邮件,即将邮件保存到Blob存储并进行回复。
下面是我们最初的SMTP处理程序(CSES的一部分)和在正确端口上启动 TcpListener 的代码(来自 OnStart() 的调用):
listener = new TcpListener(IPAddress.Any,
RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["SmtpIn"].IPEndpoint.Port);
processor = new SMTPProcessor(RoleEnvironment.GetConfigurationSettingValue("DomainName"),
new RecipientFilter(), new MessageSpool());
listener.Start();
RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["SmtpIn"].IPEndpoint.Port);
processor = new SMTPProcessor(RoleEnvironment.GetConfigurationSettingValue("DomainName"),
new RecipientFilter(), new MessageSpool());
listener.Start();
注意我使用了运行时API确定正确的端口。
下面是简单的入站TCP连接异步处理代码(来自run()的调用):
var mutex = new ManualResetEvent(false);
while (true)
{
mutex.Reset();
listener.BeginAcceptSocket((ar) =>
{
mutex.Set();
processor.ProcessConnection(listener.EndAcceptSocket(ar));
}, null);
mutex.WaitOne();
}
while (true)
{
mutex.Reset();
listener.BeginAcceptSocket((ar) =>
{
mutex.Set();
processor.ProcessConnection(listener.EndAcceptSocket(ar));
}, null);
mutex.WaitOne();
}
下面是处理入站邮件的代码:
// make a container, with public access to blobs
var id = Guid.NewGuid().ToString().Replace("-", null);
var container = account.CreateCloudBlobClient().GetContainerReference(id);
container.Create();
container.SetPermissions(new BlobContainerPermissions() { PublicAccess=BlobContainerPublicAccessType.Blob });
// parse the message
var msg = new SharpMessage(new MemoryStream(Encoding.ASCII.GetBytes(message.Data)),
SharpDecodeOptions.AllowAttachments | SharpDecodeOptions.AllowHtml | SharpDecodeOptions.DecodeTnef);
// create a permalink-style name for the blob
var permalink = Regex.Replace(Regex.Replace(msg.Subject.ToLower(), @"[^a-z0-9]", "-"), "--+", "-").Trim('-');
if (string.IsNullOrEmpty(permalink))
{
// in case there's no subject
permalink = "message";
}
var bodyBlob = container.GetBlobReference(permalink);
// set the CDN to cache the object for 2 hours
bodyBlob.Properties.CacheControl = "max-age=7200";
// replaces references to attachments with the URL of where we'll put them
msg.SetUrlBase(Utility.GetCdnUrlForUri(bodyBlob.Uri) + "/[Name]");
// save each attachment in a blob, setting the appropriate content type
foreach (SharpAttachment attachment in msg.Attachments)
{
var blob = container.GetBlobReference(permalink + "/" + attachment.Name);
blob.Properties.ContentType = attachment.MimeTopLevelMediaType + "/" + attachment.MimeMediaSubType;
blob.Properties.CacheControl = "max-age=7200";
attachment.Stream.Position = 0;
blob.UploadFromStream(attachment.Stream);
}
// add the footer and save the body to the blob
SaveBody(msg, bodyBlob, message, container, permalink);
var id = Guid.NewGuid().ToString().Replace("-", null);
var container = account.CreateCloudBlobClient().GetContainerReference(id);
container.Create();
container.SetPermissions(new BlobContainerPermissions() { PublicAccess=BlobContainerPublicAccessType.Blob });
// parse the message
var msg = new SharpMessage(new MemoryStream(Encoding.ASCII.GetBytes(message.Data)),
SharpDecodeOptions.AllowAttachments | SharpDecodeOptions.AllowHtml | SharpDecodeOptions.DecodeTnef);
// create a permalink-style name for the blob
var permalink = Regex.Replace(Regex.Replace(msg.Subject.ToLower(), @"[^a-z0-9]", "-"), "--+", "-").Trim('-');
if (string.IsNullOrEmpty(permalink))
{
// in case there's no subject
permalink = "message";
}
var bodyBlob = container.GetBlobReference(permalink);
// set the CDN to cache the object for 2 hours
bodyBlob.Properties.CacheControl = "max-age=7200";
// replaces references to attachments with the URL of where we'll put them
msg.SetUrlBase(Utility.GetCdnUrlForUri(bodyBlob.Uri) + "/[Name]");
// save each attachment in a blob, setting the appropriate content type
foreach (SharpAttachment attachment in msg.Attachments)
{
var blob = container.GetBlobReference(permalink + "/" + attachment.Name);
blob.Properties.ContentType = attachment.MimeTopLevelMediaType + "/" + attachment.MimeMediaSubType;
blob.Properties.CacheControl = "max-age=7200";
attachment.Stream.Position = 0;
blob.UploadFromStream(attachment.Stream);
}
// add the footer and save the body to the blob
SaveBody(msg, bodyBlob, message, container, permalink);