收发邮件中我们至少需要考虑下面几个因素:
1.每个用户有多个邮箱配置信息;
2.当前邮箱的收发状态;
3.收发邮件异常;
4.服务器允许的收发邮件时间间隔。(小于这个时间可能账户会被暂时屏蔽)
题外话:首先谢谢各位无论是喜欢我的文章不喜欢我的文章的朋友,有你们的帮助我才会成长。上面两篇文章可能确实没有什么实际的意义,让大家对我的文章有个误解,但我想那是为后面的文章铺路,文章里面的签名也只是防止其他网站的转载,可能大家的想法不一样。我以后的文章还是希望前辈多多批评指正,谢谢。
邮件的收发,无非就是使用一些组件,找到自己合适的那就用下去。当然如果技术很不错,可以自己动手分析邮件协议,那是另当别论了。
目前用得比较多的,大概就是 jmail 了。我这里也就使用这个组件进行邮件的收取,邮件的发送就使用 .net 自带的组件了。
只是现在网络可能过于发达了,要的东西找不到,不要的东西一大堆,就拿收邮件来说,一搜索到处就是一样的文章,抄来抄去,抄也就算了,站长也不检查检查,感觉一点没劲,时不时会骂一句:站长真会偷牛!!
收发邮件中我们至少需要考虑下面几个因素:
- 每个用户有多个邮箱配置信息;
- 当前邮箱的收发状态;
- 收发邮件异常;
- 服务器允许的收发邮件时间间隔。(小于这个时间可能账户会被暂时屏蔽)
首先我们增加一个继承自 IMailThread,IDisposable 的类 MailThread,既然是使用线程收发,那么必须定义的两个变量:
private Thread _threadReceive = null;// 收取
private Thread _threadSend = null;// 发送
好了,现在我们启动接收邮件线程:

Code
public void ReceiveStart() {
// 判断当前用户和线程状态
if (userId == 0 || _threadReceive != null)
return;
lock (this) {// 锁定当前线程,否则可能出现邮件重复了。
if(_pop3State!=0) {
_pop3State = 0;
}
// 启动线程
_threadReceive = new Thread(new ThreadStart(BeginReceive));
_threadReceive.Start();
}
}
相信大家已经注意到 BeginReceive 这个家伙了,在这个方法里面,我们首先从数据库中取出当前用户的所有邮箱配置信息,然后依次检查每个邮箱的收取状态,如果正在收取并且距离上一次收取的时间间隔不超过5分钟,就不能再收取。

BeginReceive

private void BeginReceive()
{
SqlDataReader configs = (new Config()).GetConfigs(userId);

if(configs != null)
{
_pop3Time = DateTime.Now;
_pop3State = 1;

while(configs.Read())
{
DateTime pTime = DateTime.Parse(configs["pop3Time"].ToString());
TimeSpan m = DateTime.Now - pTime;
// 每5分钟收取邮件一次/超过100分钟也重新收取

if (( m.TotalMinutes >= 5 && configs["pop3Doing"].ToString() == "0") || m.TotalMinutes >= 100)
{
_pop3Mail = string.Format("当前接收邮箱({0}):", configs["ConfigName"]);

dbmail.SetReceiveState(userId, configs["Id"], DateTime.Now, 1);

// 接收单个邮箱的邮件
Receive(
int.Parse(configs["ID"].ToString()),
configs["Pop3Uid"].ToString(),
MailHelper.Decrypt(configs["Pop3Pwd"].ToString(), "123456"),
configs["Pop3Address"].ToString(),
int.Parse(configs["Pop3Port"].ToString()),
byte.Parse(configs["Pop3Backup"].ToString()),
configs["SmtpReply"].ToString()
);
}
}
configs.Close();
_pop3State = 2;
_pop3Message = "接收完成";

} else
{
_pop3State = 3;
_pop3Message = "无配置信息";
}
_threadReceive = null;
}

实践证明,jmail 收取的邮件有两个地方必须自己处理一下,才能得到正确的结果。一个是主题,一个是日期,所以我在上一篇中提到了邮件头解码和日期处理的函数:
string subject = MailHelper.DecodeStr(message.Headers.GetHeader("Subject"));
string date = MailHelper.ParseDate(message.Headers.GetHeader("Date"));

Receive:接收单个邮箱的邮件

接收单个邮箱的邮件#region 接收单个邮箱的邮件

/**//// <summary>
/// 接收单个邮箱的邮件
/// <param name="configId"></param>
/// <param name="pop3Address"></param>
/// <param name="pop3Backup"></param>
/// <param name="pop3Port"></param>
/// <param name="pop3Pwd"></param>
/// <param name="pop3Uid"></param>
/// <param name="smtpReply"></param>
/// </summary>

public void Receive(int configId, string pop3Uid, string pop3Pwd, string pop3Address, int pop3Port, byte pop3Backup, string smtpReply)
{

if(!string.IsNullOrEmpty(pop3Address))
{
MailHelper.SaveLogs(basePath, _pop3Mail);//string.Format("开始收取 {0} 的邮件, 当前用户ID是 {1}", _pop3Mail, userId));

jmail.POP3Class p = new jmail.POP3Class();
p.Timeout = 100;

try
{
// 连接服务器
p.Connect(pop3Uid, pop3Pwd, pop3Address, pop3Port);
jmail.Message message;
jmail.Attachments attachments;
string messageId, subject;

MailAddressCollection mailAddr;
// 总邮件数:p.Messages.Count-1

for(int i = 1; i < p.Messages.Count; i++)
{
_pop3Message = string.Format("正在检查第 {0} 封邮件,共 {1} 封", i, p.Messages.Count - 1);


收取单封邮件#region 收取单封邮件

try
{
// 判断是否收取过
messageId = p.GetMessageUID(i);


if (dbmail.Exists(userId, configId, messageId))
{
continue;
}

// 获取邮件信息
message = p.Messages[i];


if(string.IsNullOrEmpty(message.Charset))
{
message.Charset = "gb2312";
}

if(string.IsNullOrEmpty(message.Encoding))
{
message.Encoding = "base64";
}
message.ISOEncodeHeaders = false;

bool isHtml = true;
string body = message.HTMLBody;

if(body == null)
{
isHtml = false;
body = message.Body;
}
// 重新处理主题
subject = MailHelper.DecodeStr(message.Headers.GetHeader("Subject"));
string date = message.Headers.GetHeader("Date");
mailAddr = MailHelper.GetAddress(message.From);
string mailFrom = mailAddr.Count > 0 ? mailAddr[0].Address : message.From;
// 保存邮件
attachments = message.Attachments;
int mailId = dbmail.SaveMail(userId, 0, configId, messageId, subject, body, mailFrom, smtpReply,
"", isHtml, message.Size, message.Priority, attachments.Count > 0, 4, MailHelper.ParseDate(date), 1, 0);

// 保存附件

if(mailId > 0)
{

for(int j = 0; j < attachments.Count; j++)
{
string attname = attachments[j].Name;
string fileext = "";

if(attname.IndexOf(".") != -1)
{
fileext = attname.Substring(attname.LastIndexOf("."));
}
string fileurl = MailHelper.MakeFileName() + i.ToString().PadLeft(3, '0') + j.ToString().PadLeft(2, '0') + fileext;

attachments[j].SaveToFile(basePath + fileurl);

dbmail.SaveAttachment(attname, fileurl, attachments[j].ContentType, attachments[j].Size, mailId);
}
}
// 删除服务器上的邮件

if(pop3Backup == 0)
{
p.DeleteSingleMessage(i);
}


} catch(Exception ex)
{
MailHelper.SaveLogs(basePath, "接收第" + i.ToString() + "封邮件时出错:" + ex.Message);
}
#endregion
}

message = null;
attachments = null;
p.Disconnect();

} catch(Exception ex0)
{
MailHelper.SaveLogs(basePath, "登录错误:" + ex0.Message);
}

// 保存最后接收时间
dbmail.SetReceiveState(userId, configId, DateTime.Now, 0);

MailHelper.SaveLogs(basePath, "邮件收取完成\r\n");
}
}
#endregion

其中 dbmail 是邮件系统与数据库打交道的类,在本系列讲完,将提供下载。
请关注我的下一篇文章,并给予支持批评教育,谢谢大家。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?