题目: 基于STMP与POP3协议的邮件发送与接收
姓 名:
学 院:
专 业:
班 级:
学 号:
指导教师:
完成日期: 年 月 日
原创性声明
本人郑重声明:上交的设计报告,是本人在导师的指导下,进行研究工作所取得的成果。除文中已经注明引用的内容外,本文不包含任何其他个人或集体已经发表或撰写过的作品成果。对本文的研究作出重要贡献的个人和集体,均已在文中以明确方式标明。本人完全意识到本声明的法律结果由本人承担。
作者签名:
日期: 年 月 日
前言
电子邮件指用电子手段传送信件、单据、资料等信息的通信方法。电子邮件综合了电话通信和邮政信件的特点,它传送信息的速度和电话一样快,又能象信件一样使收信者在接收端收到文字记录。电子邮件系统又称基于计算机的邮件报文系统。它承担从邮件进入系统到邮件到达目的地为止的全部处理过程。电子邮件不仅可利用电话网络,而且可利用任何通信网传送。在利用电话网络时,还可利用其非高峰期间传送信息,这对于商业邮件具有特殊价值。由计算机和小型计算机控制的面向有限用户的电子系统可以看作是一种计算机会议系统。
SMTP 是一种提供可靠且有效电子邮件传输的协议。 SMTP 是建模在 FTP 文件传输服务上的一种邮件服务,主要用于传输系统之间的邮件信息并提供来信有关的通知。
POP 协议允许工作站动态访问服务器上的邮件,目前已发展到第三版,称为 POP3。POP3 允许工作站检索邮件服务器上的邮件。POP3 传输的是数据消息,这些消息可以是指令,也可以是应答。
本实践是在SMTP和POP协议下设计的一个电子邮箱,能够实现发送,接收邮件等功能。
第一章 需求分析
1.1 发送邮件
发送端使用SMTP 发送邮件到邮件服务器
发送邮件过程:
输入发件人邮箱、用户名、密码,当与邮件服务器建立连接后,编写邮件主题和内容,若需要附件的话,就点击添加附件按钮进行添加附件。最后点击发送按钮发送邮件
1.2 接收邮件
接收端使用POP3从邮件服务器上下载邮件,并在接收端上阅读。
接收邮件过程:
输入用户名和密码与邮件服务器连接,在文本框中显示出服务器上所接收的邮件,对需要的阅读的邮件点击阅读下载到接收端进行阅读。在状态栏上显示于邮件服务器的连接状态,以及显示连接过程。若对于不需要的邮件,可在文本框选中,点击删除按钮删除。
第二章 总体设计
2.1 总体设计原理
电子邮件的工作过程遵循客户-服务器模式。每份电子邮件的发送都要涉及到发送方与接收方,发送方式构成客户端,而接收方构成服务器,服务器含有众多用户的电子信箱。发送方通过邮件客户程序,将编辑好的电子邮件向邮局服务器(SMTP服务器)发送。邮局服务器识别接收者的地址,并向管理该地址的邮件服务器(POP3服务器)发送消息。
一个邮件系统的传输包含用户代理User Agent传输代理TransferAgent 及接受代理DeliveryAgent三大部分。
用户代理是一个用户发信和收信的程序, 负责将电子邮件按照一定的标准包装, 然后送至邮件服务器, 或由邮件服务器收回。传输代理负责信件的交换和传输。将信件传送至适当的邮件主机, 再由接受代理将信件分发至不同的邮件信箱。传输代理必须要能够接受用户邮件程序送来的信件, 解读收信人的地址, 根据SMTP 协议将它正确无误地传递到目的地。现在一般的传输代理已采用Sendmail程序完成工作。电子邮件到达邮件主机后, 在经接收代理POP协议被用户读取至自己的主机。
电子邮件在发送与接收过程中都要遵循SMTP、POP3等协议,这些协议确保了电子邮件在各种不同系统之间的传输。其中,SMTP负责电子邮件的发送,而POP3则用于接收Internet上的电子邮件。
SMTP(Simple Mail Transfer Protocol, SMTP)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。SMTP协议属于TCP/IP协议族,它帮助每台计算机在发送或中转信件时找到下一个目的地。通过SMTP协议所指定的服务器,我们就可以把E-mail寄到收信人的服务器上
POP3(Post Office Protocol 3)即邮局协议,目前已发展到第三版,称POP3。它规定怎样将个人计算机连接到Internet的邮件服务器和下载电子邮件的电子协议。它是因特网电子邮件的第一个离线协议标准,POP3允许用户从服务器上把邮件存储到本地主机(即自己的计算机)上,同时删除保存在邮件服务器上的邮件,而POP3服务器则是遵循POP3协议的接收邮件服务器,用来接收电子邮件的。总的来说POP3协议是让用户把服务器上的信收到本地来所需要的一种协议。
本程序为一个基于SMTP和POP3协议的小型EMAIL收发程序,简单的实现了邮件的收发功能。
第三章 详细设计与实现
3.1设计思路
本次设计主要基于POP3协议的基础上实现的,首先详细了解一下POP3协议
POP3协议
1. POP适用于C/S结构的脱机模型的电子邮件协议,目前已发展到第三版,称POP3。脱机模型即不能在线操作。
2.当客户机与服务器连接并查询新电子邮件时,被该客户机指定的所有将被下载的邮件都将被程序下载到客户机,下载后,电子邮件客户机就可以删除或修改任意邮件,而无需与电子邮件服务器进一步交互。
3. POP3客户向POP3服务器发送命令并等待响应,POP3命令采用命令行形式,用ASCII码表示。服务器响应是由一个单独的命令行组成,或多个命令行组成,响应第一行以ASCII文本+OK或-ERR指出相应的操作状态是成功还是失败。
4. 在POP3协议中有三种状态,认可状态,处理状态和更新状态。当客户机与服务器建立联系时,一旦客户机提供了自己身份并成功确认,即由认可状态转入处理状态,在完成相应的操作后客户机发出quit命令,则进入更新状态,更新之后最后重返认可状态。如下图
等待连接身份确认quit命令
——|认可|—————|处理|——————|更新|
重返认可状态
5.认可状态的命令语句。一般情况下,大多数现有的POP3客户与服务器执行采用ASCII明文发送用户名和口令,在认可状态等待客户连接的情况下,客户发出连接,并由命令user/pass对在网络上发送明文用户名和口令给服务器
进行身份确认。一旦确认成功,便转入处理状态。为了避免发送明文口令的问题,有一种新的认证方法,命令为APOP,使用APOP,口令在传输之前被加密。当第一次与服务器连接时,POP3服务器向客户机发送一个ASCII码问候,这个问候由一串字符组成对每个客户机是唯一的,与当时的时间有关,然后,客户机把它的纯文本口令附加到从服务器接收到的字符串之后,然后计算出结果字符串的MD5单出函数消息摘要,客户机把用户名与MD5消息摘要作为APOP命令的参数一起发送出去。目前,大多数windows上的邮件客户软件不支持APOP命令,qpopper支持。
6.POP3命令码如下:
命令参数状态描述
假设用C表示客户端(Client),S表示服务器端(Server)。
1) 授权状态
客户端首先与POP3服务器建立TCP连接,服务器接收后发送一个单行的确认信息。此时POP3会话就进入了授权状态。在授权状态,客户需要向服务器发送用户名和密码进行确认,具体命令如下:
1>发送用户名:
语法形式:USER <用户名>
功能:将客户的用户名发送到服务器。
服务器返回:+OK正确的用户名;-ERR错误的用户名。
示例:C:USER **************
S:+OK welcome on this server.
2>用户名确认成功后,需要输入密码:
语法形式:PASS <密码>
功能:将客户的密码发送给服务器。
服务器返回:+OK正确的用户名;-OK错误的用户名。
2) 操作状态
授权成功后, POP3会话将进入操作状态,客户就可以执行POP3命令进行相应的操作。对于每个命令,服务器都会返回应答信息。下面是在操作状态中使用的命令:
1>STAT命令
语法形式:STAT
功能:从服务器中获邮件总数和总字节数。
服务器返回:邮件总数和总字节数。
示例:C:STAT
S:+OK 2 320
2>LIST命令
语法形式:LIST
功能:从服务中获得邮件列表和大小。
服务器返回:列出邮件列表和大小。
示例:C:LIST
S:+OK 2 messages (320 octets)
S:1 120
S:2 200
3>RETR命令
语法形式:RETR <邮件的序号>
功能:从服务器中获得一个邮件。
服务器返回:+OK成功;-ERR错误。
示例:C: RETR 1
S:+OK 120 octets
S:<服务器发送信件1内容>
S: .
注意,这里的“.”是单独发送的。
4>DELE命令
语法形式:DELE <邮件的序号>
功能:服务器将邮件标记为删除,当执行QUIT命令时才真正删除。
服务器返回:+OK成功;-ERR错误。
示例:C:DELE 1
S:+OK 1 Deleted
示例:C:PASS *****
S:+OK myname logged in at 19:04响应
3.2 源代码
发送端:
private void textBoxUserName_TextChanged(object sender, EventArgs e)
{ labelUserName.Visible = !Regex.IsMatch(textBoxUserName.Text,
@"^\\w+([-+.']\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$");
}
private void textBoxPassword_TextChanged(object sender, EventArgs e)
{ labelPassword.Visible = !Regex.IsMatch(textBoxPassword.Text, @"^\\w{5,20}$");
}
private void textBoxReceive_TextChanged(object sender, EventArgs e)
{ labelReceive.Visible = !Regex.IsMatch(textBoxReceive.Text,
@"^\\w+([-+.']\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$");
}
private void textBoxSubject_TextChanged(object sender, EventArgs e)
{ labelSubject.Visible = !Regex.IsMatch(textBoxSubject.Text, @"^.{1,}$");
}
private void textBoxBody_TextChanged(object sender, EventArgs e)
{ labelBody.Visible = !Regex.IsMatch(textBoxBody.Text, @"^.{1,}$");
}
private void buttonSend_Click(object sender, EventArgs e)
{ string invalidString = "";
if (labelUserName.Visible == true) invalidString += "用户名、";
if (labelPassword.Visible == true) invalidString += "口令、";
if (labelReceive.Visible == true) invalidString += "收件人、";
if (labelSubject.Visible == true) invalidString += "主题、";
if (labelBody.Visible == true) invalidString += "邮件内容、";
//if (invalidString.Length > 0)
//{
// MessageBox.Show(invalidString.TrimEnd(' ') + "不合规定");
//}
else
{ MailAddress from = new MailAddress(textBoxUserName.Text);
MailAddress to = new MailAddress(textBoxReceive.Text);
MailMessage message = new MailMessage(from, to);
message.Subject = textBoxSubject.Text;
message.SubjectEncoding = System.Text.Encoding.UTF8;
message.Body = textBoxBody.Text;
message.BodyEncoding = System.Text.Encoding.UTF8;
if (listBox1.Items.Count > 0)
{
for (int i = 0; i < listBox1.Items.Count; i++)
{
Attachment attachFile = new Attachment(listBox1.Items[i].ToString());
message.Attachments.Add(attachFile);
}
}
try
{ SmtpClient client = new SmtpClient("smtp." + from.Host);
SendMail(client, from, textBoxPassword.Text, to, message);
MessageBox.Show("邮件发送出去");
}
catch (SmtpException err)
{ if (err.StatusCode == SmtpStatusCode.GeneralFailure)
{
try
{ SmtpClient client = new SmtpClient(from.Host);
SendMail(client, from, textBoxPassword.Text, to, message);
MessageBox.Show("邮件发送出去");
}
catch (SmtpException err1)
{ MessageBox.Show(err1.Message, "出错");
}
}
else
{ MessageBox.Show(err.Message, "出错");
}
}
}
}
private void SendMail( SmtpClient client, MailAddress from, string password, MailAddress to, MailMessage message)
{
client.UseDefaultCredentials = false;
client.Credentials = new NetworkCredential(from.Address, password);
client.DeliveryMethod = SmtpDeliveryMethod.Network;
try {client.Send(message);}
catch
{ throw;
}
finally
{ message.Dispose();
}
}
private void buttonAddAttachment_Click(object sender, EventArgs e)
{
OpenFileDialog myOpenFileDialog = new OpenFileDialog();
myOpenFileDialog.CheckFileExists = true;
myOpenFileDialog.ValidateNames = true;
myOpenFileDialog.Multiselect = true;
myOpenFileDialog.ShowDialog();
if (myOpenFileDialog.FileNames.Length > 0)
{
listBox1.Items.AddRange(myOpenFileDialog.FileNames);
}
}
private void button1_Click(object sender, EventArgs e)
{
this.Hide();
FormReceiveMail re = new FormReceiveMail();
re.Show();
}
}
}
接收端:
private void connect_Click(object sender, EventArgs e)
{
//将光标置为等待状态
Cursor cr = Cursor.Current;
Cursor.Current = Cursors.WaitCursor;
//用110端口新建POP3服务器连接
Server = new TcpClient(textBoxpop3.Text, 110);
listBoxstatus.Items.Clear();
try
{
//初始化
NetStrm = Server.GetStream();
RdStrm = new StreamReader(Server.GetStream());
listBoxstatus.Items.Add(RdStrm.ReadLine());
//登录服务器过程
Data = "USER " + textBoxuser.Text + CRLF;
szData = System.Text.Encoding.UTF8.GetBytes(Data.ToCharArray());
NetStrm.Write(szData, 0, szData.Length);
listBoxstatus.Items.Add(RdStrm.ReadLine());
Data = "PASS " + textBoxpass.Text + CRLF;
szData = System.Text.Encoding.UTF8.GetBytes(Data.ToCharArray());
NetStrm.Write(szData, 0, szData.Length);
listBoxstatus.Items.Add(RdStrm.ReadLine());
//向服务器发送STAT命令,从而取得邮箱的相关信息:邮件数量和大小
Data = "STAT" + CRLF;
szData = System.Text.Encoding.UTF8.GetBytes(Data.ToCharArray());
NetStrm.Write(szData, 0, szData.Length);
listBoxstatus.Items.Add(RdStrm.ReadLine());
//改变按钮的状态
connect.Enabled = false;
disconnect.Enabled = true;
receive.Enabled = true;
//将光标置回原来状态
Cursor.Current = cr;
}
catch (InvalidOperationException err)
{
listBoxstatus.Items.Add("Error: " + err.ToString());
}
}
private void disconnect_Click_1(object sender, EventArgs e)
{
//将光标置为等待状态
Cursor cr = Cursor.Current;
Cursor.Current = Cursors.WaitCursor;
//向服务器发送QUIT命令从而结束和POP3服务器的会话
Data = "QUIT" + CRLF;
szData = System.Text.Encoding.ASCII.GetBytes(Data.ToCharArray());
NetStrm.Write(szData, 0, szData.Length);
listBoxstatus.Items.Add(RdStrm.ReadLine());
//断开连接
NetStrm.Close();
RdStrm.Close();
//改变按钮的状态
connect.Enabled = true;
disconnect.Enabled = false;
receive.Enabled = false;
//将光标置回原来状态
Cursor.Current = cr;
}
private void receive_Click(object sender, EventArgs e)
{
//将光标置为等待状态
Cursor cr = Cursor.Current;
Cursor.Current = Cursors.WaitCursor;
string szTemp;
listBoxcontent.Items.Clear();
try
{
//根据邮件编号从服务器获得相应邮件
Data = "RETR " + textBoxmailnum.Text + CRLF;
szData = System.Text.Encoding.ASCII.GetBytes(Data.ToCharArray());
NetStrm.Write(szData, 0, szData.Length);
szTemp = RdStrm.ReadLine();
if (szTemp[0] != '-')
{
//不断地读取邮件内容,只到结束标志:英文句号
while (szTemp != ".")
{
listBoxcontent.Items.Add(szTemp);
szTemp = RdStrm.ReadLine();
}
//若BackupChBox未选中,则收取邮件后,删除保留在服务器上的邮件
/*if(BackupChBox.Checked == false)
{
Data = "DELE" + MailNum.Text + CRLF;
szData = System.Text.Encoding.ASCII.GetBytes(Data.ToCharArray());
NetStrm.Write(szData,0,szData.Length);
Status.Items.Add(RdStrm.ReadLine());
}*/
}
else
{
listBoxstatus.Items.Add(szTemp);
}
//将光标置回原来状态
Cursor.Current = cr;
}
catch (InvalidOperationException err)
{
listBoxstatus.Items.Add("Error: " + err.ToString());
}
}
private void button1_Click(object sender, EventArgs e)
{
this.Hide();
FormSendMail sm = new FormSendMail();
sm.Show();
}
}
}
第四章 测试
发送邮件主界面:
发送邮件成功后实例:
接收邮件主界面:
接收邮件成功后实例:
第五章 实训设计体会
通过这次C#网络编程实训,对本学期的与网络有关的课程进行了一次总结和吸收,使知识更加牢固,对课程有了进一步的理解,也从中找到今后学习和研究的方向。
这次实训的完成与老师的悉心指导是分不开的。感谢他在我学习遇到困惑时的指点,在这里向老师表示衷心的感谢!
同样还要感谢对本人做出帮助的同学,在此向他们表达我最真诚的谢意!
参考文献
马骏等编著.《C#网络应用高级编程》.第一版.北京.人民邮电出版社.2006