Visual C#自定义组件的设计:Pop3Com组件

开发 后端
本文通过运用Visual C#编写一个Pop3邮件接收组件向大家介绍如何用Visual C#进行组件编程以及编程过程中的一些方法和技巧,最后还给出了一个对该Pop3Com组件进行测试的Windows Forms程序。

Pop3Com组件基本原理

要完成一个Pop3组件,就要完成对该组件的属性(Property)、方法(Method)和事件(Event)等的设计。属性是一个组件的重要特征,一个组件一般有多项属性。我们可以通过get和set取得和设置各个属性的值。完成了各个属性的设置,我们可以通过该组件的各种方法进行相应的操作。而事件则是在某些特定的消息下触发的。在C#中,我们用代表(delegate)进行事件的声明。

在该Pop3组件中,我们为其添加了主机名(Host)、端口号(Port)、用户名(UserName)、密码(PassWord)、邮件数目(NumOfMails)、邮件大小(TotalSize)等属性,通过ReceiveMessage()和ReceiveMessageAsync()方法完成与服务器的连接、通讯和结束会话等功能,在调用了该方法后,我们就可以从邮件数目和邮件大小等属性中获得邮箱中的相关信息,进而运用该组件就可以轻松地开发出诸如邮件信史之类的程序了。同时,该组件中还包含了一个OnMailReceived()事件,该事件在完成了邮件的接收后被触发。

在组件的设计过程中,与主机的连接通讯是该组件的核心部分,所以我们不妨专门设计一个与主机的连接类-Pop3Connection类,该类是主要运用了TcpClient类的对象,和主机建立基于TCP/IP网络协议的连接。在完成连接后,可以和主机进行通讯。完成通讯后,则关闭与主机的连接。在大致介绍了实现原理后,下面就是具体的实现方法了。

Pop3Com组件实现方法

首先,打开VS.net,新建一个Visual C#的项目:在项目类型中选择"Visual C#项目",在模板中选择"类库",不妨将该项目命名为"Pop3Com"(这样,由该项目生成的组件的命名空间就为Pop3Com了),图示如下:

Pop3Com 

1.Pop3Connection类:

这样,项目向导就完成了,接着我们将原来的Class1.cs改名为Pop3.cs,同时添加一个类Pop3Connection(文件名不妨为Pop3Connection.cs)。

如上所述,Pop3Connection类完成了与主机的连接、通讯和关闭连接等功能,所以我们必须调用.Net框架中进行网络通讯的类库,在此我们运用的是TcpClient类的对象作为网络连接的客户端。同时,在与主机的通讯过程中必然少不了对于输入输出流的控制。于是,我们在设计该类的时候,首先得添加如下命名空间:

  1. using System.IO;  
  2. using System.Net.Sockets;  
  3. Pop3Connection类的成员变量包括以下几个:  
  4. private TcpClient socket;  
  5. private StreamReader reader;  
  6. private StreamWriter writer;  
  7. private bool connected;   
  8.  

其中,bool类型的connected变量用于判断是否与主机取得了连接,它是该类的一个属性,对其操作如下:

  1. public bool Connected  
  2. {  
  3.  get{return connected;}  
  4. }  
  5. Pop3Connection类的主要方法包含以下几个:  
  6. internal void Open(string host, int port)  
  7. {  
  8.  if(host == null || host.Trim().Length == 0 || port < = 0)  
  9.  {  
  10.   throw new System.ArgumentException("Invalid Argument found.");  
  11.  }  
  12.  socket.Connect(host, port);  
  13.  reader = new StreamReader(socket.GetStream(), System.Text.Encoding.ASCII);  
  14.  writer = new StreamWriter(socket.GetStream(), System.Text.Encoding.ASCII);  
  15.  connected = true;  
  16. }  
  17.  
  18. internal void SendCommand(string cmd)  
  19. {  
  20.  writer.WriteLine(cmd);  
  21.  writer.Flush();  
  22. }  
  23.  
  24. internal void GetReply(out string reply, out int code)  
  25. {  
  26.  reply = reader.ReadLine();  
  27.  code = reply == null ? -1 : Int32.Parse(reply.Substring(0, 3));   
  28. }  
  29.  
  30. internal void Close()  
  31. {  
  32.  reader.Close();  
  33.  writer.Flush();  
  34.  writer.Close();  
  35.  reader = null;  
  36.  writer = null;  
  37.  socket.Close();  
  38.  connected = false;  
  39. }  
  40.  

根据这些方法的名称,我们不难知道它们的作用。第一个方法Open()就是根据主机名和端口号取得和服务器的连接。一旦连接成功,就通过TcpClient类的对象获取网络通讯流并新建一个StreamReader对象和一个StreamWriter对象。不言而喻,这两个对象的作用是控制网络通讯的输出和输入。最后,还要将connected的属性设置为true。第二个方法SendCommand()就是在上面的StreamWriter类的对象writer的基础上往网络套接字中输入信息。而第三个方法GetReply()则正好相反,它是用来从网络套接字中获取信息的。最后一个方法Close()的作用则是关闭输出、输入流的对象,然后调用TcpClient类的对象Close()方法并将connected属性设置为false,从而关闭连接,结束会话。

2.Pop3类:

这样,我们就完成了Pop3Connection类的设计和编码工作,也就完成了整个组件最关键的部分。接下来,我们就在该类的基础上设计Pop3类。Pop3类包含了邮件通讯所必须的基本属性、方法和事件。

首先,我们来设计其中的属性。该类应该包括主机名、端口号、用户名、密码、邮件数量、邮件总体积、邮件内容和状态信息等属性。其中前四个属性是可读又可写的,后四个属性是只可读的。具体的设置如下:

  1. ///   
  2.  
  3. /// 主机名  
  4.  
  5. ///   
  6.  
  7. public string Host  
  8.  
  9. {  
  10.  
  11.  get {return host;}  
  12.  
  13.  set   
  14.  
  15.  {  
  16.  
  17.   if(value == null || value.Trim().Length == 0)  
  18.  
  19.   {  
  20.  
  21.    throw new ArgumentException("Invalid host name.");  
  22.  
  23.   }  
  24.  
  25.   host = value;  
  26.  
  27.  }  
  28.  
  29. }  
  30.  
  31.    
  32.  
  33. ///   
  34.  
  35. /// 端口号  
  36.  
  37. ///   
  38.  
  39. public int Port  
  40.  
  41. {  
  42.  
  43.  get {return port;}  
  44.  
  45.  set   
  46.  
  47.  {  
  48.  
  49.   if(value < = 0)  
  50.  
  51.   {  
  52.  
  53.    throw new ArgumentException("Invalid port.");  
  54.  
  55.   }  
  56.  
  57.   port = value;  
  58.  
  59.  }  
  60.  
  61. }  
  62.  
  63.    
  64.  
  65. ///   
  66.  
  67. /// 用户名  
  68.  
  69. ///   
  70.  
  71. public string UserName  
  72.  
  73. {  
  74.  
  75.  get {return username;}  
  76.  
  77.  set   
  78.  
  79.  {  
  80.  
  81.   if(value == null || value.Trim().Length == 0)  
  82.  
  83.   {  
  84.  
  85.    throw new ArgumentException("Invalid user name.");  
  86.  
  87.   }  
  88.  
  89.   username = value;  
  90.  
  91.  }  
  92.  
  93. }  
  94.  
  95.    
  96.  
  97. ///   
  98.  
  99. /// 密码  
  100.  
  101. ///   
  102.  
  103. public string PassWord  
  104.  
  105. {  
  106.  
  107.  get {return password;}  
  108.  
  109.  set   
  110.  
  111.  {  
  112.  
  113.   if(value == null)  
  114.  
  115.   {  
  116.  
  117.    throw new ArgumentException("Invalid password.");  
  118.  
  119.   }  
  120.  
  121.   password = value;  
  122.  
  123.  }  
  124.  
  125. }  
  126.  
  127.    
  128.  
  129. ///   
  130.  
  131. /// 邮件数量  
  132.  
  133. ///   
  134.  
  135. public int NumOfMails  
  136.  
  137. {  
  138.  
  139.  get {return numofmails;}  
  140.  
  141. }  
  142.  
  143.    
  144.  
  145. ///   
  146.  
  147. /// 邮件总体积  
  148.  
  149. ///   
  150.  
  151. public double TotalSize  
  152.  
  153. {  
  154.  
  155.  get {return totalsize;}  
  156.  
  157. }  
  158.  
  159.    
  160.  
  161. ///   
  162.  
  163. /// 邮件内容  
  164.  
  165. ///   
  166.  
  167. public string Body  
  168.  
  169. {  
  170.  
  171.  get {return body;}  
  172.  
  173. }  
  174.  
  175.    
  176.  
  177. ///   
  178.  
  179. /// 状态信息  
  180.  
  181. ///   
  182.  
  183. public string Status  
  184.  
  185. {  
  186.  
  187. get {return status;}  
  188.  
  189. }   
  190.  

完成了该类的属性设计,我们接下来就完成该类的方法设计。该类主要的方法就一个ReceiveMessage(),顾名思义就是接收邮件信息的意思。在这个方法中,我们运用了上面的Pop3Connection类的对象。通过这个对象,我们就可以更加方便的进行网络通讯的操作。不过,在具体介绍这个方法的实现以前,我先得向大家介绍一下邮件接收的基本原理。

其基本原理如下:

一开始便是客户端与服务器的连接。不过,在客户端连接到服务器之前,注意把端口设为POP3协议默认的110号。

客户端连接服务器成功后,服务器会返回以下信息:

 +OK……

字符+OK是POP3协议的返回信息。它的回应信息不像SMTP协议那样用丰富多变的数字表示,只有两个:+OK或者-ERR。其中,+OK表示连接成功,而-ERR则表示连接失败。

接下来,客户端输入USER < 用户名>

该命令告诉服务器你的用户名。注意,有些服务器会区分大小写字母的。

服务器返回+OK后,客户端输入PASS < 口令>

服务器返回+OK后,还返回一些邮箱的统计信息,比如:+OK 1 message(s) [1304 byte(s)]

不同的服务器返回的信息格式不太一样,所以我们可以用STAT命令来查看邮箱的情况。STAT命令的回应中有两个数字,分别表示邮件的数量和邮件的大小。

如果信箱里有信,就可以用RETR命令来获取邮件的正文。RETR命令的格式为:

 RETR < 邮件编号>

如果返回结果第一行是+OK信息,则表示成功。第二行起便是邮件的正文。最后一行和SMTP协议一样,是一个单独的英文句号,表示邮件的结尾部分。

把邮件存储起来后要用DELE命令删除邮箱中的邮件,否则原有的邮件会继续保留在服务器上,一旦邮件一多,你的邮箱就爆了。DELE命令的格式为:

 DELE < 邮件编号>

如果删错了,可以用RSET命令来恢复所有已被删除的邮件。条件是你还没有退出,一旦退出,那就一切Bye Bye了。全部完成以后,输入QUIT命令就可以退出POP3服务器了。

在了解了邮件接收的基本原理的基础上,我就向大家介绍ReceiveMessage()方法的具体实现:

  1. ///   
  2.  
  3. /// 接收邮件信息  
  4.  
  5. ///   
  6.  
  7. public void ReceiveMessage()  
  8.  
  9. {  
  10.  
  11.  // 避免线程冲突  
  12.  
  13.  lock(this)  
  14.  
  15.  {  
  16.  
  17.   // 设置初始连接  
  18.  
  19.   con = new Pop3Connection();  
  20.  
  21.   if(port < = 0) port = 110;  
  22.  
  23.    con.Open(host, port);  
  24.  
  25.    
  26.  
  27.   StringBuilder buf = new StringBuilder();  
  28.  
  29.   string response;  
  30.  
  31.   int code;  
  32.  
  33.    
  34.  
  35.    // 获取欢迎信息  
  36.  
  37.   con.GetReply(out response, out code);  
  38.  
  39.   status += response;  
  40.  
  41.    
  42.  
  43.   //登录服务器过程  
  44.  
  45.   buf.Append("USER");  
  46.  
  47.   buf.Append(username);  
  48.  
  49.   buf.Append(CRLF);  
  50.  
  51.   con.SendCommand(buf.ToString());  
  52.  
  53.   con.GetReply(out response, out code);  
  54.  
  55.   status += response;  
  56.  
  57.    
  58.  
  59.   buf.Length = 0;  
  60.  
  61.   buf.Append("PASS");  
  62.  
  63.   buf.Append(password);  
  64.  
  65.   buf.Append(CRLF);  
  66.  
  67.   con.SendCommand(buf.ToString());  
  68.  
  69.   con.GetReply(out response, out code);  
  70.  
  71.   status += response;  
  72.  
  73.    
  74.  
  75.   //向服务器发送STAT命令,从而取得邮箱的相关信息:邮件数量和大小  
  76.  
  77.   buf.Length = 0;  
  78.  
  79.   buf.Append("STAT");  
  80.  
  81.   buf.Append(CRLF);  
  82.  
  83.   con.SendCommand(buf.ToString());  
  84.  
  85.   con.GetReply(out response, out code);  
  86.  
  87.   status += response;  
  88.  
  89.    
  90.  
  91.   //将总邮件数和邮件大小分离  
  92.  
  93.   string[] TotalStat = response.Split(new char[] {' '});   
  94.  
  95.   numofmails = Int32.Parse(TotalStat[1]);  
  96.  
  97.   totalsize = (double)Int32.Parse(TotalStat[2]);  
  98.  
  99.    
  100.  
  101.   forint x = 0; x <  numofmails; ++x)  
  102.  
  103.   {  
  104.  
  105.    //根据邮件编号从服务器获得相应邮件  
  106.  
  107.    buf.Length = 0;  
  108.  
  109.    buf.Append("RETR");  
  110.  
  111.    buf.Append(x.ToString());  
  112.  
  113.    buf.Append(CRLF);  
  114.  
  115.    con.SendCommand(buf.ToString());  
  116.  
  117.    con.GetReply(out response, out code);  
  118.  
  119.    
  120.  
  121.    if(response[0]!='-')   
  122.  
  123.    {  
  124.  
  125.     //不断地读取邮件内容,只到结束标志:英文句号  
  126.  
  127.     while(response!=".")  
  128.  
  129.     {  
  130.  
  131.      body += response;  
  132.  
  133.      con.GetReply(out response, out code);  
  134.  
  135.     }  
  136.  
  137.    }  
  138.  
  139.    else 
  140.  
  141.     status += response;  
  142.  
  143.   }  
  144.  
  145.   //向服务器发送QUIT命令从而结束和POP3服务器的会话  
  146.  
  147.   buf.Length = 0;  
  148.  
  149.   buf.Append("QUIT");  
  150.  
  151.   buf.Append(CRLF);  
  152.  
  153.   con.SendCommand(buf.ToString());  
  154.  
  155.   con.GetReply(out response, out code);  
  156.  
  157.   status += response;  
  158.  
  159.    
  160.  
  161.   con.Close();  
  162.  
  163.    
  164.  
  165.   // 邮件接收成功后触发的事件  
  166.  
  167.   if(OnMailReceived != null)  
  168.  
  169.   {  
  170.  
  171.    OnMailReceived();  
  172.  
  173.   }  
  174.  
  175.  }  
  176.  
  177. }   
  178.  

根据邮件接收的基本原理和代码中的注释,我想读者应该不难读懂上面的代码。不过下面几点仍得说明:其中的CRLF为"\r\n",它的作用是在每个命令后面加上一个换行符。另外,在该方法的一开始处有一句:lock(this),它的作用是避免线程冲突。考虑到接收邮件的过程比较漫长而且占用的资源较多,所以在设计的时候我用到了多线程(有关多线程的资料读者可参考相关的书籍或文章,此处不再赘述)。在实际的程序中,上面的方法其实被另一个方法ReceiveMessageAsync()作为一个单独的线程调用。方法如下:

  1. ///   
  2. /// 通过一个独立的线程接收邮件  
  3. ///   
  4. public void ReceiveMessageAsync()  
  5. {  
  6.  new Thread(new ThreadStart(ReceiveMessage)).Start();  
  7. }   
  8.  

最后,在ReceiveMessge()方法的末尾处,它调用了事件处理函数OnMailReceived()。在C#中,事件的声明是用代表完成的,首先我们在Pop3类的开始处进行声明如下:

  1. public delegate void MailReceivedDelegate();   
  2.  

接着,就进行事件的声明:

  1. public event MailReceivedDelegate OnMailReceived;  

这样,只要在应用程序中调用了OnMailReceived()事件,在邮件接收成功后,OnMailReceived()事件就会被触发。

到此为止,我们已经完成了核心类-Pop3类的属性、方法和事件的设计,这样整个组件也就完成了(按Ctrl+Shift+B就可以生成解决方案)。不过,由于是组件,所以不可以直接运行,我们必须做一个测试程序来测试之。下面我就用该组件做了一个简单的邮件信史,它可以向用户报告邮箱中的新邮件数目。

测试程序

首先,在原来的解决方案的基础上添加一个新项目。项目类型为"Visual C#项目",模板为"Windows应用程序",名称不妨为"MailNotifier"。

接着,设计主界面如下:

设计主界面  

设计好主界面后,我们进行代码设计。首先,要添加对上面的组件-Pop3Com的引用。在项目菜单下选择"添加引用",出现"添加引用"对话框,在"项目"一页下将Pop3Com组件添加到本项目中。图示如下:

在"项目"一页下将Pop3Com组件添加到本项目中  

同时,在代码的开始处添加引用:using Pop3Com。这样,我们就可以在本程序中调用Pop3Com组件中的类的方法完成相应功能了。下面就是"开始检查"按钮的事件处理函数了:

  1. private void checkBtn_Click(object sender, System.EventArgs e)  
  2.  
  3. {  
  4.  
  5.  // 正确性检查  
  6.  
  7.  if(host == null || host.Text.Trim().Length == 0)  
  8.  
  9.  {  
  10.  
  11.   MessageBox.Show("请填入服务器地址!");  
  12.  
  13.  }  
  14.  
  15.  else 
  16.  
  17.   if(username == null || username.Text.Trim().Length == 0)  
  18.  
  19.   {  
  20.  
  21.    MessageBox.Show("请填入用户名!");  
  22.  
  23.   }  
  24.  
  25.  else 
  26.  
  27.    if(password == null || password.Text.Trim().Length == 0)  
  28.  
  29.   {  
  30.  
  31.     MessageBox.Show("请填入密码!");  
  32.  
  33.   }  
  34.  
  35.  else 
  36.  
  37.   {  
  38.  
  39.     mailer = new Pop3();  
  40.  
  41.     mailer.Host = host.Text;  
  42.  
  43.     mailer.Port = Int32.Parse(port.Text);  
  44.  
  45.     mailer.UserName = username.Text;  
  46.  
  47.     mailer.PassWord = password.Text;  
  48.  
  49.     statusBar.Text = "正在接收信息……";  
  50.  
  51.     mailer.OnMailReceived += new Pop3.MailReceivedDelegate(OnMailReceived);  
  52.  
  53.     mailer.ReceiveMessageAsync();  
  54.  
  55.   }  
  56.  
  57. }   
  58.  

其中,mailer为Pop3类的一个实例对象,它是完成邮件检查的核心对象。同时,OnMailReceived()事件函数如下:

  1. private void OnMailReceived()  
  2.  
  3. {  
  4.  
  5.  statusBar.Text = "邮件接收完毕!";  
  6.  
  7.  MessageBox.Show("你有" + mailer.NumOfMails.ToString() + "个邮件!","信息",  
  8.  
  9.  MessageBoxButtons.OK,MessageBoxIcon.Information);  
  10.  
  11. }   
  12.  

如此,测试程序-邮件信史也就完成了。最后,按下Ctrl+F5运行我们的程序如下:

通过对Pop3Com组件的设计,我想读者对Visual C#组件编程应该有了个基本的了解,对其中类的属性、方法和事件的设计也应该是相当清楚了。组件编程是Visual C#的强项,所以希望读者能进一步学习。同时,对于上面的组件,读者也可试着进一步完善,并不妨将之运用于自己的应用程序中,让它发挥其强大的重用功能。

【编辑推荐】

  1. 浅谈C#中构造函数和成员函数
  2. C#函数的参数返回结构数组
  3. 概述ASP.NET中的NGWS Runtime
  4. C#函数与JavaScript函数
  5. 详解C# Object.Equals函数
责任编辑:book05 来源: cnblogs
相关推荐

2009-08-20 11:03:34

Visual C#使用

2009-08-03 13:32:38

C#自定义组件

2009-08-21 09:14:47

C# Excel CO

2009-09-02 16:43:55

C#调用Excel的C

2022-04-24 15:17:56

鸿蒙操作系统

2021-11-01 10:21:36

鸿蒙HarmonyOS应用

2009-09-24 15:10:54

C#调用COM组件

2022-07-06 20:24:08

ArkUI计时组件

2022-10-25 15:12:24

自定义组件鸿蒙

2022-10-26 15:54:46

canvas组件鸿蒙

2023-02-20 15:20:43

启动页组件鸿蒙

2009-06-24 15:13:36

自定义JSF组件

2009-09-18 19:09:41

C# COM组件

2021-11-22 10:00:33

鸿蒙HarmonyOS应用

2009-09-22 17:53:32

C# COM组件开发

2021-12-24 15:46:23

鸿蒙HarmonyOS应用

2022-10-17 14:39:12

自定义弹窗组件鸿蒙

2022-02-16 15:25:31

JS代码Canvas鸿蒙

2023-01-03 07:40:27

自定义滑块组件

2021-09-15 10:19:15

鸿蒙HarmonyOS应用
点赞
收藏

51CTO技术栈公众号