资源预览内容
第1页 / 共111页
第2页 / 共111页
第3页 / 共111页
第4页 / 共111页
第5页 / 共111页
第6页 / 共111页
第7页 / 共111页
第8页 / 共111页
第9页 / 共111页
第10页 / 共111页
亲,该文档总共111页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
第8章 邮件接收和发送客户端邮件接收和发送客户端的作用是在本地计算机和远程计算机之间传送电子信件以及接收电子信件。用户平时所用的Foxmail就是一种邮件接收和发送客户端。通常情况下,Foxmail由发送者将电子信件发送到邮件服务器(SMTP)中,再由SMTP服务器将该邮件发送到POP3(接收邮件)服务器中,邮件接收者通过账户和口令再从POP3服务器中获取信件。在本章中,将向用户介绍邮件接收和发送客户端的原理以及开发过程。8.1 调用Windows自带的邮件发送程序一般情况下,用户所使用的Windows操作系统中都带有默认的邮件发送程序。通过该邮件发送程序,用户可以将邮件发送到任何目的地址。这种方法比较简单适用,所以很受大部分用户欢迎。用户可以在操作系统中,使用操作系统命令打开邮件程序。如果用户需要在自己的程序中调用系统自带的邮件程序,那么需要使用函数CreateProcess()或者ShellExecute()进行调用。下面将分别介绍这两种方法。8.1.1 调用Windows自带程序在Windows操作系统中,所有的程序都是以进程为单位运行。本节中所讲述的调用邮件发送程序就是通过调用相应的Windows进程实现的。调用该Windows进程所使用的命令是“mailto:+string”,其中,string表示邮件发送的目的地址。例如,用户需要将邮件发送到邮件地址为lymlrl163.com的邮箱中,使用的命令是“mailto:lymlrl163.com”。首先,在Windows系统界面下选择“开始”|“运行”命令,弹出“运行”对话框,如图8.1所示。图8.1 “运行”对话框然后,在运行对话框中输入命令“mailto:lymlrl163.com”,可以打开Windows自带的邮件发送程序进行邮件发送,如图8.2所示。图8.2 Windows邮件收发器以上过程是用户通过Windows命令调用邮件收发器必须做的。实际上,除了这种方法,用户还可以在程序中通过函数调用Windows邮件收发器。此种方法将在8.1.2节中进行讲解。8.1.2 CreateProcess()函数在VC中编程,MFC类库已经提供了几个库函数用于调用Windows的外部程序,包括邮件收发程序。在本节中,将向用户介绍其中的两个函数CreateProcess()和ShellExecute()。1使用CreateProcess()函数CreateProcess()函数可以创建Windows进程,同时也可以调用已经存在的进程。该函数的原型如下:BOOL CreateProcess( LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation );该函数创建进程成功则返回true,否则返回false。其参数意义如下:参数lpApplicationName表示可执行文件的名字。用户指定该参数后,该函数会在当前路径下搜索可执行文件,但不会按照系统的搜索路径进行搜索。注意:使用该参数时,需要加上扩展名,因为系统不会自动为其添加“.exe”后缀名。参数lpCommandLine表示将要传递到新进程的命令行字符串。使用该参数时,该函数会自动为其添加后缀名“.exe”。如果参数字符串没有指定所在路径,那么该函数则会按照系统的搜索路径进行搜索文件。参数bInheritHandles表示该进程创建的子进程是否能继承父进程的对象句柄。参数lpStartupInfo指向结构体STARTUPINFO的指针变量。该结构体的声明如下:typedef struct _STARTUPINFO DWORD cb;/表示该结构体的大小 LPTSTR lpReserved;/保留,必须将该参数初始化为NULL LPTSTR lpDesktop; LPTSTR lpTitle; /设置控制台程序的名称 DWORD dwX; /设置应用程序窗口的X坐标 DWORD dwY; /设置应用程序窗口的Y坐标 DWORD dwXSize; /设置应用程序窗口的横向大小 DWORD dwYSize; /设置应用程序窗口的纵向大小 DWORD dwXCountChars; /以字符为单位设置应用程序窗口的X坐标 DWORD dwYCountChars; /以字符为单位设置应用程序窗口的Y坐标 DWORD dwFillAttribute; /设置应用程序窗口所使用的背景色等 DWORD dwFlags; /表示创建窗口的标志 WORD wShowWindow; /是否显示应用程序窗口 WORD cbReserved2; /保留,将该参数必须设置为0 LPBYTE lpReserved2;/保留,将该参数必须设置为0 HANDLE hStdInput; /设置控制台程序的输入输出缓存句柄 HANDLE hStdOutput; HANDLE hStdError; /错误输出句柄 STARTUPINFO, *LPSTARTUPINFO;该结构体主要用于保存新创建进程的窗口信息,如窗口的大小或窗口的显示方式等。其中,参数dwFlags标识了窗口创建成功以后,在显示之前以何种方式进行显示。其取值如表8.1所示。表8.1 程序窗口显示标志取值注意:在表8.1中所示的程序窗口显示标志的作用仅仅是为了控制相应的成员变量是否有效而已。例如,用户在程序中,需要使用到该结构体中的dwFillAttribute成员。那么,用户必须将参数dwFlags取值为STARTF_USEFILLATTRIBUTE。否则,该成员变量将无效。参数lpProcessInformation是指向结构体PROCESS_INFORMATION的指针变量。该结构体声明如下:typedef struct _PROCESS_INFORMATION HANDLE hProcess; /进程句柄 HANDLE hThread; /线程句柄 DWORD dwProcessId; /进程ID DWORD dwThreadId; /线程ID PROCESS_INFORMATION;取 值含 义STARTF_USESIZE使用dwXSize和dwYSize成员STARTF_USESHOWWINDOW使用wShowWindow成员STARTF_USEPOSITION使用dwX和dwY成员STARTF_USECOUNTCHARS使用dwXCountChars和dwYCountChars成员STARTF_USEFILLATTRIBUTE使用dwFillAttribute成员STARTF_USESTDHANDLES使用hStdInput、hStdOutput、hStdError成员STARTF_RUN_FULLSCREEN以全屏方式启动程序该结构体主要用于保存进程的相关信息。其他参数均可以默认设置为NULL。例如,调用操作系统的记事本程序。代码如下:01 . /省略部分代码02 STARTUPINFO si=sizeof(si);/定义结构体变量03 PROCESS_INFORMATION pi;/定义结构体对象04 CString *str=notepad”;/记事本名称05CreateProcess(NULL,str,NULL,NULL,false,NULL,NULL,NULL,&si,&pi);06 /调用函数打开记事本程序07 . /省略部分代码同样的道理,用户在本例中,也可以使用函数CreateProcess()调用邮件收发程序。代码如下:01 . /省略部分代码02 STARTUPINFO si=sizeof(si);/定义结构体变量03 PROCESS_INFORMATION pi;04 CString *str=mailto:lymlrl163.com;/打开邮件程序的系统命令05CreateProcess(NULL,str,NULL,NULL,false,NULL,NULL,NULL,&si,&pi);06 /调用函数打开记事本程序07 . /省略部分代码2使用ShellExecute()函数在MFC编程中,除了函数CreateProcess()以外,还可以调用函数ShellExecute()实现相同的功能。该函数原型如下:HINSTANCE ShellExecute( HWND hwnd, /父窗口句柄 LPCTSTR lpOperation,/将要进行的操作形式 LPCTSTR lpFile, /目录文件名称或文件路径 LPCTSTR lpParameters, /传递的参数 LPCTSTR lpDirectory,/一般为NULL INT nShowCmd/显示方式);该函数执行成功会返回调用程序的应用程序指针,否则返回错误代码。部分错误代码如表8.2所示。表8.2 部分错误代码错 误 代 码意 义ERROR_FILE_NOT_FOUND找不到相应文件ERROR_PATH_NOT_FOUND找不到所需路径ERROR_BAD_FORMAT无效的.exe文件SE_ERR_ASSOCINCOMPLETE无效的文件名0 操作系统的内存溢出该函数各个参数的说明已在函数原型中标出。使用该函数调用操作系统自带的邮件发送程序,代码如下:以上代码是使用C语言编写,并且使用命令行窗口界面,目的是为了让用户了解整个调用过程。在随书光盘的第8章中附有代码,请用户自行参考。此段代码在VC中编译后的结果,如图8.3所示。用户在运行界面1中输入字符Y或y,然后按下Enter键。程序提示邮件程序正在打开,当邮件程序打开以后,实例程序会提示已经打开邮件程序,如图8.4所示。图8.3 运行界面1图8.4 运行界面2注意:在程序中为了模拟计算机的工作,所以笔者使用了while循环产生时间差,仅仅是为了让用户重复了解该调用过程。在实际编程中,不提倡使用该方法产生时间差,因为这种方法很危险,容易造成系统的崩溃。通常,使用多线程编程的方法比较安全,也是笔者极力推荐的一种方法。该类方法将在后面的相关章节中讲述。8.2 SMTP会话过程SMTP是发送邮件协议,与前面所讲的FTP、HTTP等协议一样被用作某种行为的规范标准。本节的主要内容就是向用户讲解邮件客户端怎么连接SMTP服务器以及向SMTP服务器发送信件等操作。8.2.1 怎么连接服务器在网络中传输邮件信息都是基于TCP/IP协议的,所以用户在Windows操作系统中编写邮件发送程序时可以使用Windows套接字来完成。一般情况下,客户端连接服务器的几个步骤如下。(1)客户端指定IP地址和端口连接服务器。(2)服务器收到连接请求,并同意客户端连接请求。(3)客户端和服务器互相发送数据。(4)关闭服务器和客户端的套接字。基于以上几个步骤,用户可以VC中编写程序实现邮件客户端。1创建套接字对象该实例与一般网络程序一样,需要Windows套接字的支持,所以用户应该首先初始化套接字库。代码如下:01 BOOL CMyEMAIL:OnInitDialog()02 03WSADATAdata;04WORDss=MAKEWORD(2,0);/指定套接字库版本05:WSAStartup(ss,&data);/初始化套接字库06 用户初始化套接字库以后,还必须记得在程序退出之前释放该套接字库。代码如下:01 void CMyEMAIL:OnClose() 02 03:WSACleanup();/释放已经加载的套接字库04 然后,用户可以调用API函数socket()创建连接服务器的套接字了。代码如下:在代码中,函数socket()创建了基于TCP通信的流式套接字句柄。2连接服务器用户创建好套接字以后,可以调用API函数connect()连接服务器。其原型如下:int connect (SOCKETs, const struct sockaddr FAR*name, intnamelen);该函数用于连接远程计算机,如果连接失败则返回1,否则成功。参数及其意义如下:参数s表示将要连接服务器的套接字句柄,该套接字是用户之前已经创建好的套接字句柄。参数name是指向套接字地址结构体的指针变量。该套接字结构体声明如下:struct sockaddr_in short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero8;该结构体是sockaddr结构的扩充结构,一般被用在Windows Socket 2中。参数namelen表示套接字结构对象的大小。使用该函数在套接字s上连接SMTP服务器。例如,SMTP服务器地址为“smtp.126.com”,端口为25。代码如下:通过上面的代码,用户已经向SMTP服务器发送了连接请求。当服务器接受客户端的连接请求以后,服务器会返回相关响应码给客户端。该响应码的前3位数字表示服务器端响应的结果。部分SMTP响应码如表8.3所示。表8.3 部分SMTP响应码响 应 码意 义220服务器就绪221服务器关闭传输通道250客户端所请求的邮件操作完成450邮件地址不可用421服务器服务不可用,关闭传输通道451由于处理过程中出错,请求的操作被终止452服务器存储空间不足500SMTP命令语法错误501命令参数的语法错误502命令暂时不可实现503错误的命令序列550客户端请求的操作不能被执行或者邮件地址不可用552服务器的存储不足553邮箱名称不合法554服务失败334发送验证用户名235验证账号密码失败在该实例中,客户端如果连接服务器成功则会返回响应码220,表示服务器服务就绪,否则返回554。客户端接收响应码应该调用API函数recv()。代码如下:本节中,向用户讲述了连接SMTP服务器、SMTP响应码的具体意义以及客户端接收响应码,并且配有相关的代码实例。8.2.2 SMTP命令在客户端与SMTP服务器之间进行数据传输时,双方都是使用SMTP命令进行交流。因此,SMTP命令在E-mail通信中起着很重要的作用。但是,在向用户讲解SMTP命令之前,用户必须首先了解一下电子邮件的基本格式。1E-mail构造格式在SMTP协议中,规定了E-mail信件的基本格式。该格式与第5章中向用户所讲述的HTTP基本格式一样,都包含有数据头和数据体,并且在两者之间均使用一个空白行隔开。例如,一封简单的邮件构造格式如下:/邮件头From:lymlrl163.comSubject: This is a E-mail/空白行Hello lymlrl!/邮件体This is a E-mail!在例子中,E-mail的基本格式包括邮件头和邮件体。邮件头中的内容是关于该邮件的一些基本信息。例如,发送者和主题信息。而邮件体中是纯文本的邮件内容,并且在SMTP协议中,还规定在邮件头和邮件体之间需要使用一个空白行隔开。在邮件头中,主要是由SMTP标准字段组成,这些字段包含邮件的基本信息。例如:/邮件头From:lymlrl163.comSubject: This is a E-mail以上字段所包含的信息:邮件发送者的邮件地址是lymlrl163.com,邮件主题是This is a E-mail。在SMTP协议中,包含了很多邮件头标准字段,部分SMTP邮件头字段如表8.4所示。紧跟着邮件头的是一个空白行,用于区分邮件头和邮件体。在邮件体中,主要是邮件需要发送的信息内容。在邮件体中,不包含任何字段信息,只有文本格式的邮件内容而已。表8.4 SMTP邮件头字段字 段意 义From邮件创建者的邮件地址To邮件目的地Sender邮件发送者Reply-to邮件回复地址Cc邮件抄送人In- Reply-To邮件正被回复Data邮件创建的时间Subject邮件主题Comments邮件的其他说明Keywords邮件的关键字Bcc邮件的密件抄送人邮件地址Message-ID邮件的标识符在表8.4中列出了部分SMTP标准字段。其中,From表示邮件的创建者地址,该地址在一般情况下仅有一个。Sender表示邮件的发送者,该发送者可能是转发邮件,该字段可以有多个邮件地址,地址之间使用逗号隔开。同时可以有多个地址的字段是To。例如:01 Data:Tue,04 Feb 2009 21:18:03+080002 From:lymlrl163.com03 Sender: lymlrl126.com, wexs163.com,wen126.com, wuysina.com.cn04 /发送者为多个地址05To:lymlrl126.com,datayahoo.com.cn,asjsina.com.cn /接收者也为多个06 Subject: This is a E-mail/邮件主题07/空行08 Hello lymlrl! /邮件数据体09 This is a E-mail!如果邮件没有发送成功,则客户端应该将该邮件重新进行发送。邮件的重发必须在保证邮件内容不发生改变的情况下进行。实际上,邮件进行重发只用在原有邮件头的标题字段前加上字符串“Resent-”。例如,将上述实例中的邮件进行重发,内容如下:01 Resent-Data:Tue,04 Feb 2009 21:18:03+080002 Resent-From:lymlrl163.com03 Resent-Sender: lymlrl126.com, wexs163.com,wen126.com04 /发送者为多个地址05 Resent-To:lymlrl126.com,datayahoo.com.cn/接收者也为多个06 Resent-Subject: This is a E-mail/邮件主题0708 Hello lymlrl!/邮件数据体09 This is a E-mail!注意:在连接SMTP服务器成功以后,客户端在接收到服务器返回的DATA命令后,就可以将以上构造的邮件内容发送到SMTP服务器了。2SMTP命令前面已经向用户介绍过客户端与SMTP服务器之间的交流是通过SMTP命令来完成的。常见的SMTP命令如表8.5所示。表8.5 常用SMTP命令命 令含 义HELO客户机向服务器问候MAIL指定邮件的发送者RCPT指定邮件的接收者DATA指示客户端或服务器端可以发送邮件内容RSET重新初始化会话状态VRFY验证邮件地址的有效性NOOP空操作QUIT终止会话TURN交换服务器与客户端下面将参照表8.5中所列举的部分SMTP命令进行讲解。命令HELO是在邮件客户端连接服务器成功以后,第一个发送到服务器的命令。其作用是向SMTP服务器问候。例如,客户端向服务器问候并表明自己的身份。内容如下:HELO lymlrl其中,字符表示结束符号。以上内容表示客户端向服务器问候并且表明自己的身份。例如,在VC中向服务器发送该命令,代码如下:./省略部分代码char sendmail=HELO lymlrlrn;/构造命令字符串send(s, sendmail,sizeof(sendmail),0);/发送命令到服务器./省略部分代码命令MAIL FROM/ RCPT TO分别表示指定邮件的发送和接收者。例如:MAIL FROM:lymlrl163.comRCPT TO:lymlrl126.com上述代码分别指定了邮件的发送者和接收者的邮件地址。命令DATA是客户端发送到服务器表明客户端将要发送邮件到服务器。服务器收到该命令后会返回SMTP响应码到客户端,表示服务器已经准备好接收客户端的邮件数据。命令VRFY是被用来验证某个邮件地址的有效性。例如,用户用该命令来验证自己的邮箱地址是否有效,则可以发送命令字符串“VRFY:lymlrl163.com”到SMTP服务器。如果该邮箱地址是有效的地址,则服务器会返回响应码250,表示客户端所请求的操作成功,否则返回450,表示邮件地址无效。命令QUIT表示终止服务器和客户端的会话。例如客户端向服务器发送该命令,代码如下:. /省略部分代码char sendmail=QUITrn;/构造命令字符串send(s, sendmail,sizeof(sendmail),0);/发送命令到服务器. /省略部分代码当服务器接收到该命令以后,会返回响应码220到客户端,表示服务器已经关闭相关的数据通道。注意:表8.5中的命令在程序中被发送时必须加上换行符号“rn”,或者用户在构造完成整个邮件内容后,需要在邮件内容后面加上“0”,表示数据内容发送或者接收完毕。8.2.3 发送命令与接收响应在客户端编程中,通常情况下客户端都是通过向SMTP服务器发送命令表示需要进行的操作。在表8.5中,已经列出了部分SMTP常用命令,这些命令都是在客户端连接服务器成功以后发送的。客户端发送命令以后,服务器通过向客户端发送SMTP响应码告知其所发送的命令是否成功或被执行。1与服务器一问一答客户端与SMTP服务器的通信过程是通过问答形式完成的,这个过程是典型的C/S通信模式。下面介绍一下邮件客户端发送的命令与服务器端返回的信息。内容如下:01 ./省略发送连接请求02 220 163 .com Anti-spam GT for Coremail System (163com071018)03 HELO smtp.163.com04 250 OK05 auth login06 334 dXNlcm5hbWU607 USER base64/加密后的用户名08 334 UGFzc3dvcmQ609 PASS base64/加密后的密码10 235 Authentication successful11 MAIL FROM:XXX163.COM12 250 Mail OK13 RCPT TO:XXX163.COM14 250 Mail OK15 DATA/准备发送信件16 354 End data with ./信件以rn.rn结束17 ./省略构造信件内容并发送18 QUIT/退出命令19 221 bye以上内容单数为客户端发送的命令,双数为从服务器端返回的信息。通过上面的内容,用户可以看到这是发送邮件所要经历的一个典型的C/S(客户端/服务器)通信过程,通过问答的形式将一封邮件发送到服务器。注意:在客户端发送DATA命令以后,服务器会返回是否准备好接收客户端将要发送邮件的响应码,该响应码是354,表示服务器已经准备好接收邮件。接下来,客户端可以直接将邮件发送到服务器。2发送SMTP命令在实例中,客户端发送命令是通过API函数send()进行的。该函数的作用是向套接字的另一方发送指定缓冲区中的内容。函数原型如下:int send(SOCKET s,const char* buff,int len,int flags);该函数调用成功返回非0值,否则失败。部分参数意义如下:参数s表示客户端所创建的套接字句柄。参数buff指向缓冲区的字符指针。参数len表示缓冲区的大小,可以使用函数sizeof()获得。例如,用户使用函数send()将命令DATA发送到服务器,代码如下:CString str=DATArn;/定义命令字符串send(socket_client,str.GetBuffer(1),str.GetLength(),0);3接收邮件服务器响应客户端接收的消息来自于服务器端返回的响应码。实现该功能的函数是recv(),该函数原型如下:int recv(SOCKET s,const char* buff,int len,int flags);该函数调用成功,则返回实际接收到的字符数,否则失败。主要参数意义如下:参数s套接字句柄。参数buff表示接收数据的缓冲区指针,与函数send()一样。参数len表示将接收的数据大小。在这里将该参数设置为3。char recv_message512 = ;recv(socket_client,recv_message,512,0);在这里,关于客户端接收服务器响应消息的功能不再进行重复讲述,请用户复习本章前面所讲述的相关内容。8.3 SMTP客户端发送邮件用户通过学习前面关于邮件收发的基本原理和编程方法,对邮件收发器的制作已经熟悉。在本节中,将通过编程制作程序实例,向用户讲述在VC环境下编程的具体方法。通过本节实例的学习,用户可以仿照该实例的设计方法,自行编程实现邮件收发器。8.3.1 准备工作在程序中,窗口界面是最重要的,因为程序界面直接面向用户。当用户第一次使用软件时,其窗口界面决定了用户对该软件的第一印象,所以我们先从设计对话框的界面开始,然后做些编程前的准备工作。1创建工程创建基于对话框的工程,工程名为sendemil,注意在向导的第2步选中Windows Sockets复选框,如图8.5所示。图8.5 Windows Sockets复选框2添加控件为对话框添加控件并设计界面如图8.6所示:图8.6 程序设计界面及其关键控件ID号控件的ID以及为控件关联的变量名和类型如图8.7所示:图8.7 控件关联的变量名及类型3 为对话框添加成员变量在类CSendemilDlg中添加成员变量,即与服务器端交流的套接字。8.3.2 SMTP登录身份验证方式SMTP既可允许匿名登录也可以要求身份验证,实际应该中当然是要求身份认证的。身份认证的方式有多种。1.LOGIN方式如下为与SMTP服务器的对话01 auth login02 334 VXNlcm5hbWU6 -BASE64编码“Username:”03 Y29zdGFAYW1heGl0Lm5ldA= -发送BASE64编码的用户名04 334 UGFzc3dvcmQ6 -BASE64编码Password:05 MTk4MjIxNA= -客户端发送BASE64编码的密码06 235 auth successfully -登录成功用户向服务器发送auth login命令,表示采用此种方式验证。单号是由客户端发送,双号是由服务器端返回,对话中提到了BASE64编码,它是网络上最常见的用于传输8Bit字节代码的编码方式,可用于在HTTP环境下传递较长的标识信息,此种编码方式不仅比较简短,同时也具有不可读性,即所编码的数据不会被人用肉眼所直接看到。相当于对所发送数据进行简单的加密,有兴趣的读者可以查阅相关书籍来对它有更多的了解。本章所讲的工程实例就是采用此种登录验证方式。实例中专门引入了两个文件Base64.h和Base64.cpp,它们封装了一个CBase64类,可以用它的成员函数Encode()对需要进行BASE64编码的数据进行处理。2.PLAIN方式基于明文的SMTP验证,其向服务器发送的用户名和密码的格式应该为“usernamepassword”。username是用户名,后边的password是口令,NULL是ASCII的0。3.CRAM-MD5方式 CRAM-MD5即是一种Keyed-MD5验证方式,CRAM是“Challenge-Response Authentication Mechanism”的缩写。所谓Keyed-MD5,是将Client与Server共享的一个Key作为一部分MD5的输入,正好邮件系统的用户名和口令可以作为这个Key。SMTP服务器对以上3种验证方式并不一定全部支持。8.3.3 连接登录服务器为“登录服务器”按钮添加消息响应函数OnConnect()。1.加载套接字库WSAStartup()必须是应用程序调用的第一个Windows Sockets函数。使用函数WSAStartup()来加载指定版本的套接字库。2.创建套接字并获取SMTP服务器的IP地址获取的IP地址会被用在之后的连接服务器的操作中。m_address是由用户输入的SMTP服务器的网址,通过调用函数gethostbyname()来返回一个指向hostent结构的指针,hostent结构的成员h_addr_list包含了SMTP服务器网络字节序的IP地址,通过字节拷贝函数memmove()将host-h_addr_list0所指向内存的4个字节拷贝到in_addr类型的变量中。3.连接SMTP服务器需要用默认的端口号25和之前获得的IP地址填充sockaddr_in结构。代码通过调用connect()函数来连接SMTP服务器,调用函数recv()来接收来自服务器的响应信息,返回220表示连接成功。4.发送命令HELO习惯上要问候服务器一下,就像与熟人第一次见面时的问候一样。代码发送HELO smtp向服务器问候,服务器通常会返回250 OK响应。当然也可以不想它问候,如果你不满意它的“回答”的话。5.登录验证选择一种服务器支持的验证方式,通过验证后才可以进入自己的邮箱。代码选择了LOGIN的登录验证方式,并先后向服务器发送了经过Base64处理的用户名和密码。代码中是通过调用类CBase64的成员函数Encode()来实现Base64处理的,函数接收的参数分别为要处理的数据和数据的长度。一切正常的话服务器的响应如下:auth login334 dXNlcm5hbWU6USER base64/加密后的用户名334 UGFzc3dvcmQ6PASS base64/加密后的密码235 Authentication successful/成功登录综上所述,“登录服务器”按钮的消息响应函数OnConnect()编写如下:登录服务器的代码实现效果如图8.8所示。图8.8 程序连接SMTP服务器过程8.3.4 构造并发送邮件服务器端服务成功启动以后,客户端可以将邮件发送到SMTP服务器,但是在邮件发送之前必须对邮件的数据进行顺序调整,以符合SMTP协议的规范。例如,一封正确的邮件数据格式应该如下。From:lymlrl163.com/发件人地址Subject: This is a E-Mail/邮件主题 /空白行Hello lymlrl!/邮件内容This is a E-mail!接下来将分别向SMTP服务器发送如下命令,正常情况下服务器的响应如下:MAIL FROM:XXX163.COM250 Mail OKRCPT TO:XXX163.COM250 Mail OKDATA/准备发送信件354 End data with ./信件以rn.rn结束./省略构造信件内容并发送QUIT/退出命令221 bye1.发送MAIL FROM命令发送邮件前必须要首先提供的关键信息之发件人。m_name是用户填写的用户名信息,代码首先构造了MAIL FROM:字符串,然后通过send()函数发送到SMTP服务器,最后用recv()函数来接收SMTP服务器的消息响应。2.发送RCPT TO命令发送邮件前必须要提供的关键信息之收件人。m_rely是用户填写的收件人信息,代码首先构造了RCPT TO:字符串,然后通过send()函数发送到SMTP服务器,最后用recv()函数来接收SMTP服务器的消息响应。3.发送DATA命令发送DATA提示服务器:客户端即将发送邮件。同时希望得到服务器“已经准备好了”的响应信息。代码首先构造了DATA字符串,然后通过send()函数发送到SMTP服务器,最后用recv()函数来接收SMTP服务器的消息响应。4.发送邮件选择感兴趣的关键字并填充,如主题。按照约定邮件的结束以“rn.rn”为标志。m_subject是用户填写的主题信息,m_context是用户填写的邮件内容,代码将用户填写的信息进行了格式化,然后整合在了一起。最后通过send()函数发送到SMTP服务器,最后用recv()函数来接收SMTP服务器的消息响应。5.发送QUIT命令QUIT命令用来通知服务器结束会话、断开连接。代码首先构造了QUIT字符串,然后通过send()函数发送到SMTP服务器,最后用recv()函数来接收SMTP服务器的消息响应。综上所述,添加“邮件发送”按钮的消息响应函数如下:邮件发送的代码实现效果如图8.9所示。图8.9 程序发送邮件的过程打开收件人的邮箱查看收到的邮件如图8.10所示:图8.10 查看邮箱中收到的邮件8.3.5 退出程序双击“退出程序”按钮,添加消息响应函数OnCancel()。代码主要调用函数closesocket()和WSACleanup()完成关闭套接字和卸载socket库的功能。8.4 POP3简介一般,用户接收邮件是通过向POP3服务器发送命令获取的。具体发送命令的步骤与SMTP协议一样,所以在这里不再赘述,如有不清楚的地方请用户复习服务器前面的知识。在本节中,将向用户介绍部分POP3命令以及编程实现接收邮件功能。1POP3命令POP3通信方式与SMTP一样,使用标准命令与服务器进行数据交换。POP3协议中还规定了标准端口为110号端口。POP3标准命令如表8.6所示。表8.6 部分POP3标准命令命 令意 义QUIT终止与服务器会话STAT提供信箱大小LIST获取邮件大小USER客户端发送账号信息到服务器验证PASS客户端发送密码信息到服务器验证TOP取出第M封邮件信头和邮件内容的前N行DELE删除第N封邮件RSET复位POP3会话RETR取出第N封邮件在上表中列出了POP3的相关命令,下面将对其中的命令进行详解。命令QUIT的作用是终止与服务器的会话连接。格式如下:QUIT该命令如果发送到服务器执行成功,服务器则会返回OK,表示服务器同意客户端退出对话。命令STAT的作用是请求服务器信箱的大小信息。命令LIST可以获取指定邮件的大小信息。如果不带任何命令参数,则服务器会返回所有邮件的大小。格式如下:LIST/客户端发送命令LIST1 1024/表示第一封邮件的大小2 2048/表示第二封邮件的大小.注意:格式中的序号表示邮件的序列号,紧跟后面的数字表示该邮件的大小信息。使用该命令获得的邮件列表序号是从1开始的。命令USER将标识客户端发送的账号信息。格式如下:USER lymlrl命令PASS将标识客户端发送的密码信息。格式如下:PASS lwlwlw命令TOP表示将取出指定邮件的信头和其邮件内容的前N行。例如,用户需要取出第一封邮件的前两行内容,则发送TOP命令到服务器即可。代码如下:CString str(TOP 1 2rn);/构造命令字符串send(s,str.GetBuffer(1),sizeof(str),0);/发送命令到服务器命令DELE表示对邮件进行删除操作。如果该命令配合其命令参数可以删除第N封邮件。例如,用户将删除第N封邮件,格式如下:DELE N命令RSET的作用是对POP3会话过程进行复位。命令RETR的作用是取出第N封邮件。例如,用户需要取出第N封邮件。格式 如下:RETR N当客户端发送该命令以后,服务器会返回被请求邮件的全部内容(包括邮件头和邮件内容)。如果服务器成功接收到POP3命令之后,都会返回相应的请求数据到客户端。返回的数据格式如下:+OK服务器将返回相应的数据2POP3会话POP3会话过程与SMTP一样,必须首先连接服务器成功以后才能进行相关操作。下面简单介绍一下POP3会话的过程,会话如下:01 /建立连接02 +OK Welcome to coremail Mail Pop3 Server 03 user everyone_now126.com/验证的用户名04 +OK core mail05 pass XXXXXX/嘿嘿,这个得保密,实际是明文的06 +OK 6 message(s) 6689 byte(s)07 list/命令服务器给出各邮件长度08 +OK 6 668909 1 212510 2 203311 3 53712 4 57613 5 58114 6 83715 ./标识16 stat/查询客户邮箱中邮件的总数和邮件总长度 17 +OK 6 6689该会话过程是一个交互式的问答过程。注意:因为POP3的工作方式与SMTP相似,所以在本章中不再向读者继续讲解关于POP3的其他知识。如果用户需要具体了解,请参考其他书籍。8.5 POP3客户端接收邮件用户接收邮件是通过POP3(接收邮件服务器)协议完成的。一般情况下,客户端通过向服务器发送相应的POP3命令获取邮件。服务器接收到命令以后,会将数据按照E-Mail的数据格式整理邮件,然后将邮件发送到客户端进行解析、显示。在本节中,将向用户讲解POP3命令等相关知识。8.5.1 准备工作在程序中,窗口界面是最重要的,因为程序界面直接面向用户。当用户第一次使用软件时,其窗口界面决定了用户对该软件的第一印象,所以我们先从设计对话框的界面开始,然后做些编程前的准备工作。1创建工程创建基于对话框的工程,工程名为recvemil,注意在向导的第2步选中Windows Sockets复选框,如图8.11所示。图8.11 Windows Sockets复选框2. 添加控件为对话框添加控件并设计界面如图8.12所示:图8.12 程序设计界面及其关键控件ID号控件的ID以及为控件关联的变量名和类型如图8.13所示:图8.13 控件关联的变量名及类型3. 为对话框添加成员变量在类CRecvemilDlg中添加成员变量,即与服务器端会话的套接字。8.5.2 连接登录服务器为“连接服务器”按钮添加消息响应函数。1.加载套接字库就像之前讲过的那样,WSAStartup()必须是应用程序调用的第一个Windows Sockets函数。使用函数WSAStartup()来加载指定版本的套接字库。2.创建套接字并获取POP3服务器的IP地址获取的IP地址会被用在之后的连接服务器的操作中。m_address是由用户输入的POP3服务器的网址,通过调用函数gethostbyname()来返回一个指向hostent结构的指针,hostent结构的成员h_addr_list包含了SMTP服务器网络字节序的IP地址,通过字节拷贝函数memmove()将host-h_addr_list0所指向内存的4个字节拷贝到in_addr类型的变量中。3.连接POP3服务器需要用默认的端口号110和之前获得的IP地址填充sockaddr_in结构。代码通过调用connect()函数来连接POP3服务器,调用函数recv()来接收来自服务器的响应信息,返回+OK表示连接成功。4.登录验证通过验证后可以进入自己的邮箱并对邮箱中的邮件进行操作。POP3服务器接受明文的用户名和密码,代码先后向服务器发送了用户名和密码,返回信息+OK表示登录成功。综上所述,“连接服务器”按钮的消息响应函数OnConnect()编写如下:连接服务器的代码实现效果如图8.14所示。图8.14 程序连接POP3服务器过程8.5.3 获取邮件列表客户端登录成功后可以单击“获取邮件序号和大小”按钮,获取邮箱中邮件列表信息。添加“获取邮件序号和大小”按钮的消息响应函数OnGet()如下:向服务器发送LIST命令时,服务器正确的返回信息示例如图8.15所示,代码中实现的功能是从服务器返回的信息中提取出部分信息,然后将其添加到ID位IDC_LIST的列表框中。图8.15 服务器接收LIST命令后返回的信息示例8.5.4 获取并解析邮件内容成功登录邮箱并且获取到邮件列表后,邮件列表中会填充“邮件名号和大小”。现在来添加列表框的鼠标双击消息响应函数OnDblclkList(),实现功能:双击邮件列表中的邮件名时,会返回邮件的内容。响应函数实现的功能如下: (1)清空文本框以便重新接收数据。(2)获取用户双击列表框时选择项的文本。代码中先后调用了CListBox类的成员函数GetCurSel()获取用户选择项的索引、GetTextLen()获取索引所指文本的长度、GetText()获取索引所指的文本并保存在动态分配的内存buff中。(3)发送RETR命令,并接收返回信息。返回信息+OK表示命令发送成功,准备开始发送邮件内容。(4)循环读取邮件的内容,筛选需要的信息。服务器返回的邮件信息通常如图8.16所示:图8.16 POP3服务器返回的邮件信息如下代码用来解析服务器返回的邮件内容,即取出程序中感兴趣的信息,如发件人、主题、邮件正文。代码实现的功能的过程:一次从服务器接收255字节的信息,然后遍历每个字节筛选出需要的“发件人”和“邮件主题”信息,他们分别由关键字From和Subject标识。还要筛选“邮件内容”,它的起始标识是“rnrn”即两个连续的回车换行,结束标志是.,一个英文的标点符号“句号”,以此为依据进行解析。综上所述,“双击此处获取邮件内容”列表框的鼠标双击消息响应函数OnDblclkList()代码编写如下:实现的效果如图8.17所示。图8.17 程序运行效果8.5.5 退出程序双击“退出程序”按钮,添加消息响应函数OnCancel(),代码如下:响应函数主要调用函数closesocket()和WSACleanup()完成关闭套接字和卸载socket库的功能。8.6 小 结通过本章,用户学习了SMTP、POP3命令、一般邮件的数据格式以及服务器响应客户端请求以后返回到客户端的应答内容。读者在学习本章的实例程序时,不但可以学习在VC中怎样实现程序界面的消息响应,还可以学习邮件客户端与服务器之间的工作原理。
收藏 下载该资源
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号