一.Managed C++简介:
Managed C++是微软Visual Studio.Net的一部分,它是从标准C++发展而来的,所以和标准C++几乎是一样的。不过因为它是建立在.Net架构之上的,经编译器编译后生成的是微软的中间语言(Microsoft Intermediate Language,MSIL),用它来编写的代码是托管的C++代码,所以相对标准C++它又多了几分新异。
二.新邮件检查原理简介:
首先,我想有必要向大家介绍一下通过POP3协议完成新邮件检查的基本原理。在了解的其基本原理的基础上完成整个程序也就变得相对容易了。
POP3协议工作的基本原理如下:
一开始便是客户端与服务器的连接。不过,在客户端连接到服务器之前,注意把端口设为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服务器了。
以上我简要地向大家介绍了POP3协议工作的基本原理,不过我们程序的任务是完成新邮件检查,所以以上收取邮件和删除服务器备份的部分就不需要了。我们要完成的只是从STAT命令的服务器回复中分离出新邮件的数目并向用户报告。
三 .程序的实现:
在介绍完新邮件检查的基本原理后,我们就不妨着手我们的程序了。首先,打开Visual Studio.Net,新建一个项目,在项目类型里选择"Visual C++ 项目",在模板中选择"MC++AppWiz",项目的名称不妨为"MailChecker",图示如下:
在新建完项目后,VS.Net会为我们生成一个框架和一些代码文件,其中有一个名为"MailChecker.cpp"的文件就是程序的主文件了,我们要做的仅是对它完成编码工作。
另外需要注意的是,用Managed C++向导生成的程序是基于控制台的,所以程序的界面不是传统的Windows界面,因此会不利于和用户进行交互,所以在这个程序中我运用了Windows Forms的编程方法编写出了一个传统的Windows窗体界面。Windows Forms也是.Net中的一项新概念,它能大大简化Windows窗体和相关控件的编程,有关Windows Forms的更多知识请读者参考相关的资料。在这个程序中,我只是运用了Windows Forms的编程方法用纯编码的方式写出了一个Windows窗体界面,对于一些细节,这不是本文介绍的重点所以也就不多作介绍了,望读者能理解。
首先我们得添加对Windows Forms名字空间(namespace)的引用,如此我们才能享受Windows Forms给我们带来的方便和快捷。方法是在文件开始处添加:
#using using namespace System::Windows::Forms; |
同时,我们还要用到一些其他的名字空间的引用,具体这里就不给出了,请读者参考文后所附的源代码文件。
程序的主界面是一个从Form继承过来的类-MainForm,它的声明如下:
public __gc class MainForm : public Form { private: Label *label1; Label *label2; Label *label3; TextBox *ServerAdd; TextBox *Username; TextBox *Password; Button *btnChecker; CPOP3Client *pPOP3Client; void btn_Click( Object *sender, System::EventArgs *e ); int MailChecker(); public: MainForm(); }; |
其中包含了一些必要的窗体控件和一个CPOP3Client类的对象pPOP3Client,该类是我们程序的主体,它完成了新邮件检查的基本工作,我将在后面介绍。
MainForm类的构造函数MainForm()完成窗体控件的初始化工作,下面的代码是完全手工编写的而没有借助窗体设计器的帮助。不过在Visual C#中我们就可以运用窗体设计器来大大简化我们的工作,不过自己动手编写代码的好处就是能使我们更加深入的了解Windows Forms编程的机理。
设计好的窗体示意图如下:
MainForm类中的按钮btnChecker的消息响应函数的实现如下:
void MainForm::btn_Click( Object *sender, System::EventArgs *e ) { if( sender->Equals( this->btnChecker ) ) { if( this->ServerAdd->Text != "" && this->Username->Text != "" && this->Password->Text != "" ) { int number = this->MailChecker(); if( number > 0 ) { String *info = new String( "你有" ); info->Concat( number.ToString() ); info->Concat( S"封新邮件!" ); MessageBox::Show( info->ToString(), "InfoBox", MessageBoxButtons::OK, MessageBoxIcon::Information ); } else if( number == 0 ) { MessageBox::Show( "不好意思,你暂时还没有新邮件。", "InfoBox", MessageBoxButtons::OK, MessageBoxIcon::Information ); } } } |
该函数完成了按钮的点击事件的消息响应,其中调用了本类的一个MailChecker()成员函数,这个方法完成了新邮件检查的基本工作。只要你的邮箱中有新邮件,程序就会以MessageBox的方式向你报告新邮件的数目。下面是MailChecker()的具体实现方法:
int MainForm::MailChecker() { int numberOfMails; // 创建一个POP3Client对象 pPOP3Client = new CPOP3Client(); // 设置各项属性 pPOP3Client->POP3HostName = this->ServerAdd->Text->ToString(); pPOP3Client->UserName = this->Username->Text->ToString(); pPOP3Client->Password = this->Password->Text->ToString(); // 获得新邮件数目 numberOfMails = pPOP3Client->NumberOfMessages; if( numberOfMails < 0 ) MessageBox::Show( "发生错误!!!", "InfoBox",MessageBoxButtons::OK, MessageBoxIcon::Error ); return numberOfMails; } |
首先,程序new一个CPOP3Client类的对象pPOP3Client,然后根据用户的输入设置该对象的各项必须属性。接着,程序通过读取pPOP3Client对象的NumberOfMessages属性取得新邮件的数目。如果新邮件的数目小于零,那么说明在连接服务器的过程中发生了错误并向用户发出警告。最后程序返回新邮件的数目。
到此,程序的主界面的类已经完成,接下来我们就开始设计我们的核心类-CPOP3Client类。我们为该类设计了一些必要的方法和属性,其中方法包括连接服务器的方法-ConnectToServer(),向服务器发送命令的方法-SendCommand()和断开与服务器连接的方法-DisconnectFromServer()。各个方法的功能是显而易见的,这里就不赘述。下面给出各个方法的具体实现方法:
//********************************************************** // 连接到POP3服务器 file:// // 返回值:成功为true,失败为false file://********************************************************** bool ConnectToServer() { String *strReadBuffer; StreamReader *streamRead; // 关闭已存在的连接 Close(); // 和服务器建立一个连接 m_Client = new TcpClient(m_strPOP3HostName, 110); // 创建一个读取流 streamRead = new StreamReader(m_Client->GetStream(), Encoding::ASCII ); strReadBuffer = streamRead->ReadLine(); // 忽略buffer中的多余部分 streamRead->DiscardBufferedData(); // 回复是否以+OK开头,是则成功,否则失败. if(strReadBuffer->StartsWith("+OK")) return true; else { Close(); return false; } } |
该方法首先创建一个TcpClient类的对象,根据这个对象获取一个网络读取流,通过这个流,我们就可以和服务器实现通讯。根据从服务器读取的信息,我们决定是否已成功的和服务器取得连接,即若服务器返回的信息以"+OK"开头则表示成功,否则则是失败了。
//********************************************************** // 将命令发送到服务器,并从服务器获得相应的回复 file:// // 参数:strCommand - 发送到服务器的命令,pstrOutput - 从服务器获得的回复 file:// // 返回值:成功为true,失败为false file://********************************************************** bool SendCommand(String *strCommand, String** pstrOutput) { Byte outbuffer __gc[]; StreamReader *streamRead; NetworkStream *streamWrite; String *strRequest; if (m_Client == NULL) return false; // 给每个命令添加结束符 strRequest = String::Concat(strCommand, S"\r\n"); // 创建一个从套接字读写数据的流 streamWrite = m_Client->GetStream(); streamRead = new StreamReader(streamWrite, Encoding::ASCII); // 将字符串转化为字节流并将它写入套接字中 outbuffer = Encoding::ASCII->GetBytes(strRequest); streamWrite->Write(outbuffer, 0, outbuffer->Length); // 从套接字读取回复 *pstrOutput = streamRead->ReadLine(); // 忽略buffer中的多余部分 streamRead->DiscardBufferedData(); return (*pstrOutput)->StartsWith(S"+OK") ? true : false; } |
该方法中我们首先获取网络读写流,根据该流和服务器进行通讯:首先将字符串转化为字节流并将它写入套接字中,然后读取服务器的回复,如果返回信息以"+OK"开头,该方法则返回true,否则则返回false。
//********************************************************** // 断开与POP3服务器的连接 file://********************************************************** void DisconnectFromServer() { String *strResponse = String::Empty; SendCommand("QUIT", &strResponse); Close(); } |
该方法向服务器发送QUIT命令表示和服务器断开连接。以上的方法大量运用了.Net网络编程和输入输出流操作的方法,所以要用到有关网络编程和输入输出流的名字空间,因此务必不要忘了在文件开始处添加如下一些语句:
using namespace System::Net::Sockets; using namespace System::IO; using namespace System::Text; |
到此,我们设计好了该类的基本方法,剩下的就是完成对各项属性的设计了。在一个邮件客户端程序中,我想以下一些属性是必须的:服务器地址,端口号(此处已预设为110,所以不必考虑),用户名,密码,状态和新邮件数目。其中状态和新邮件数目都是从服务器获得的信息,所以只能是只读的。以下就是对这些属性的设计:
// POP3服务器地址 __property String* get_POP3HostName() { return m_strPOP3HostName; } __property void set_POP3HostName(String* strPOP3HostName) { m_strPOP3HostName = strPOP3HostName; } // 用户名 __property String* get_UserName() { return m_strUserName; } __property void set_UserName(String *strUserName) { m_strUserName = strUserName; } // 密码 __property String* get_Password() { return m_strPassword; } __property void set_Password(String *strPassword) { m_strPassword = strPassword; } // 状态 __property String* get_Status() { return m_strStatus; } // 新邮件数目 __property int get_NumberOfMessages() { String *strResponse = String::Empty; String *strNrOfMessages; Char separator __gc[] = new Char __gc[1]; separator[0] = @# @#; try { if(!ConnectToServer()) return -1; if(!SendCommand(String::Concat(S"USER ", m_strUserName), &strResponse)) { return -1; } if(!SendCommand(String::Concat(S"PASS ", m_strPassword), &strResponse)) { return -1; } if(!SendCommand(S"STAT", &strResponse)) { return -1; } // 从回复信息中获得新邮件的数目 strNrOfMessages = strResponse->Split(separator)[1]; return Convert::ToInt32(strNrOfMessages); } catch(Exception* e) { m_strStatus = String::Concat(S"Error: ", e->Message); return -1; } __finally { DisconnectFromServer(); } } |
所有的属性中新邮件数目属性是最重要的一个,它调用了本类的各个方法,完成了连接服务器,向服务器发送各种命令并取得有关新邮件数目的信息,最后断开与服务器的连接。同时还处理了一些异常情况。这样,在程序的主界面类中我们只要读取该类的对象的新邮件数目属性就可以获取邮箱中的新邮件数目,而不必去考虑如何实现服务器的连接、通讯和断开连接等操作了,这正体现出了面向对象设计的优势所在。
这样我们就完成了整个程序的设计和编码工作,接下来让我们测试一下自己的成果吧。运行程序如下:
在设置好相关参数后,点击"Check Mail!"按钮,不久程序就会弹出一个MessageBox来向用户报告其邮箱中的新邮件数目,图示如下:
四.全文小结:
本文向读者介绍了如何运用Managed C++创建一个新邮件检查器,其中大量运用了Windows Forms编程和.Net框架下的网络编程的原理。通过本文,希望能使读者对.Net中的Managed C++有一个感性的认识,同时对Windows Forms编程和其中的网络编程也有个大致的了解。最后,希望各位能尽快掌握.Net中的各种新技术并付诸于实践应用中。
文章来源于领测软件测试网 https://www.ltesting.net/