VC++MFC 实现发邮件的程序详解
的有关信息介绍如下:
电子邮件服务作为Internet上应用最多和最广的服务项目得到了非常广泛的应用,在网络应用中也起到非常重要的作用。如同其他的网络服务,电子邮件系统也有其使用的传输协议,包括SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)、POP(Post Office Protocol,邮局协议)和IMAP(Internet Message Access Protocal,消息访问协议)等,这些协议应用于电子邮件的发送和接收。一些邮件处理软件如OutLook Express和FoxMail等就是按照SMTP和POP3 协议结合Windows Sockets套接字进行设计来收发邮件的。本文以SMTP协议为研究对象,在Visual C++ 6.0编程环境下按照SMTP协议通过套接字发送SMTP命令,接收并处理邮件服务器的反馈信息,从而实现对电子邮件的发送。实现一个简单的电子邮件客户端程序,就要掌握基于网络Socket的编程方法和步骤,分别用pop3和smtp协议实现邮件的收、发功能,加深对相关网络协议的认识和理解
基于Socket的网络编程是指利用socket套接字,利用TCP/IP或者UDP协议,实现几个机器之间的通信。一般使用C/S结构。
以TCP/IP为例:首先建立一个服务器,步骤如下:socket()创建一个socket,bind()绑定socket到一个端口,listen()监听端口,accept()等待客户端的连接。客户端程序:socket()创建一个socket,可以绑定也可以不绑定,然后connect()连接到服务器端。socket又分为阻塞式的和非阻塞式的。阻塞式的就是服务器端等待连接直到连接上,不然一直挂起。
在SMTP发送操作由MAIL命令开始给出发送者标识。一系列或更多的RCPT命令紧跟其后,给出了接收者信息,然后是DATA命令列出发送的邮件内容,最后邮件内容指示符确认操作。
过程中的第一步是MAIL命令,
MAIL
此命令告诉接收者新的发送操作已经开始,请复位所有状态表和缓冲区。它给出反向路径以进行错误信息返回。如果请求被接收,接收方返回一个250 OK应答。
过程中的第二步是发送RCPT命令。
RCPT
此命令给出向前路径标识接收者,如果命令被接收,接收方返回一个
250 OK应答,并存储向前路径。如果接收者未知,接收方会返回一个550 Failure应答。此过程可能会重复若干次。
DATA
如果命令被接收,接收方返回一个354 Intermediate应答,并认定以下的各行都是信件内容。当信件结尾收到并存储后,接收者发送一个250 OK应答。因为邮件是在传送通道上发送,因此必须指明邮件内容结尾,以便应答对话可以重新开始。SMTP通过在最后一行仅发送一个句号来表示邮件内容的结束,在接收方,一个对用户透明的过程将此符号过滤掉,以不影响正常的数据。
注意:邮件内容包括如下提示:Date,Subject,To,Cc,From。
邮件内容指示符确认邮件操作并告知接收者可以存储和再发送数据了。如果此命令被接收,接收方返回一个250 OK应答。DATA命令仅在邮件操作未完成或源无效的情况下失败。
上面所述的过程是一个发送操作。这些命令只能以上面的顺序使用。下例表示了在一个发送操作中这些命令的使用。
SMTP过程例子 此例是在Alpha..ARPA主机的Smith发送邮件给Beta..ARPA主机的Jones,Green和Brown的,这里假定主机Alpha与主机Beta直接相连。
S: MAIL FROM:
R: 250 OK
S: RCPT TO:
R: 250 OK
S: RCPT TO:
R: 550 No such user here
S: RCPT TO:
R: 250 OK
S: DATA
R: 354 Start mail input; end with
S: Blah blah blah...
S: ...等等
S:
R: 250 OK
此信被前两个人接收,而第三个人在此主机上没有邮箱。
定义变量,和邮件信息头部
char EmailTo[]=""; //发给的邮箱
char EmailContents[]="From: \"lucy\"<""To: \"dasiy\"<""Subject: Hello\r\n\r\n""Hello World, Hello Email!";SendMail(EmailTo,EmailContents);
由于邮件是基于base64加密传送的,要定义数据格式如下
struct Base64Date6
{
unsigned int d4:6;
unsigned int d3:6;
unsigned int d2:6;
unsigned int d1:6;
};
// 协议中加密部分使用的是base64方法
用到的函数如下
char ConvertToBase64(char c6); 对数据进行加密传送,传送给socket套接字
void EncodeBase64(char*dbuf,char*buf128,int len); 对数据进行解密显示void SendMail(char*email,char*body); 发邮件利用socket套接字
int OpenSocket(struct sockaddr *addr); 打开socket套接字
具体的函数体如下:
char ConvertToBase64(char uc)
{
if(uc <26)
{
return'A'+uc;
}
if(uc <52)
{
return'a'+(uc -26);
}
if(uc <62)
{
return'0'+(uc -52);
}
if(uc ==62)
{
return'+';
}
return'/';
}
// base64的实现
void EncodeBase64(char*dbuf,char*buf128,int len)
{
struct Base64Date6*ddd =NULL;
int i =0;
char buf={0};
char *tmp =NULL;
char cc ='\0';
memset(buf,0,256);
strcpy(buf,buf128);
for(i =1;i <=len/3;i++)
{
tmp =buf +(i-1)*3;
cc =tmp;
tmp=tmp;
tmp=cc;
ddd =(struct Base64Date6*)tmp;
dbuf[(i-1)*4+0]=ConvertToBase64((unsigned int)ddd->d1);
dbuf[(i-1)*4+1]=ConvertToBase64((unsigned int)ddd->d2);
dbuf[(i-1)*4+2]=ConvertToBase64((unsigned int)ddd->d3);
dbuf[(i-1)*4+3]=ConvertToBase64((unsigned int)ddd->d4);
}
if(len %3==1)
{
tmp =buf +(i-1)*3;
cc =tmp;
tmp=tmp;
tmp=cc;
ddd =(struct Base64Date6*)tmp;
dbuf[(i-1)*4+0]=ConvertToBase64((unsigned int)ddd->d1);
dbuf[(i-1)*4+1]=ConvertToBase64((unsigned int)ddd->d2);
dbuf[(i-1)*4+2]='=';
dbuf[(i-1)*4+3]='=';
}
if(len%3==2)
{
tmp =buf+(i-1)*3;
cc =tmp;
tmp=tmp;
tmp=cc;
ddd =(struct Base64Date6*)tmp;
dbuf[(i-1)*4+0]=ConvertToBase64((unsigned int)ddd->d1);
dbuf[(i-1)*4+1]=ConvertToBase64((unsigned int)ddd->d2);
dbuf[(i-1)*4+2]=ConvertToBase64((unsigned int)ddd->d3);
dbuf[(i-1)*4+3]='=';
}
return;
}
解密base64的实现
// 发送邮件
void SendMail(char*email,char*body)
{
int sockfd ={0};
char buf={0};
char rbuf={0};
char login={0};
char pass={0};
WSADATA WSAData;
struct sockaddr_in their_addr ={0};
WSAStartup(MAKEWORD(2,2),&WSAData);
memset(&their_addr,0,sizeof(their_addr));
their_addr.sin_family =AF_INET;
their_addr.sin_port =htons(25);
hostent*hptr =gethostbyname("smtp.163.com"); // 用的是163服务器
memcpy(&their_addr.sin_addr.S_un.S_addr,hptr->h_addr_list,hptr->h_length);
printf("IP of smpt.126.com is : %d:%d:%d:%d\n",
their_addr.sin_addr.S_un.S_un_b.s_b1,
their_addr.sin_addr.S_un.S_un_b.s_b2,
their_addr.sin_addr.S_un.S_un_b.s_b3,
their_addr.sin_addr.S_un.S_un_b.s_b4);
// 连接邮件服务器,如果连接后没有响应,则2 秒后重新连接
sockfd =OpenSocket((struct sockaddr *)&their_addr);
memset(rbuf,0,1500);
while(recv(sockfd,rbuf,1500,0)==0)
{
cout<<"reconnect..."< Sleep(2); sockfd =OpenSocket((struct sockaddr *)&their_addr); memset(rbuf,0,1500); } cout< // EHLO memset(buf,0,1500); sprintf(buf, "EHLO HYL-PC\r\n"); send(sockfd,buf,strlen(buf),0); memset(rbuf,0,1500); recv(sockfd,rbuf,1500,0); cout<<"EHLO REceive: "< // AUTH LOGIN memset(buf,0,1500); sprintf(buf,"AUTH LOGIN\r\n"); send(sockfd,buf,strlen(buf),0); memset(rbuf,0,1500); recv(sockfd,rbuf,1500,0); cout<<"Auth Login Receive: "< // USER memset(buf,0,1500); sprintf(buf,"wxbliaotian@163.com"); memset(login,0,128); EncodeBase64(login,buf,strlen(buf)); sprintf(buf,"%s\r\n",login); send(sockfd,buf,strlen(buf),0); cout<<"Base64 UserName: "< memset(rbuf,0,1500); recv(sockfd,rbuf,1500,0); cout<<"User Login Receive: "< // PASSWORD sprintf(buf,"XXXXXX");//你的邮箱密码 memset(pass,0,128); EncodeBase64(pass,buf,strlen(buf)); sprintf(buf,"%s\r\n",pass); send(sockfd,buf,strlen(buf),0); cout<<"Base64 Password: "< memset(rbuf,0,1500); recv(sockfd,rbuf,1500,0); cout<<"Send Password Receive: "< // MAIL FROM memset(buf,0,1500); sprintf(buf,"MAIL FROM: <"); send(sockfd,buf,strlen(buf),0); memset(rbuf,0,1500); recv(sockfd,rbuf,1500,0); cout<<"set Mail From Receive: "< // RCPT TO 第一个收件人 sprintf(buf,"RCPT TO:<%s>\r\n",email); send(sockfd,buf,strlen(buf),0); memset(rbuf,0,1500); recv(sockfd,rbuf,1500,0); cout<<"Tell Sendto Receive: "< // DATA 准备开始发送邮件内容 sprintf(buf,"DATA\r\n"); send(sockfd,buf,strlen(buf),0); memset(rbuf,0,1500); recv(sockfd,rbuf,1500,0); cout<<"Send Mail Prepare Receive: "< // 发送邮件内容,\r\n.\r\n内容结束标记 sprintf(buf,"%s\r\n.\r\n",body); send(sockfd,buf,strlen(buf),0); memset(rbuf,0,1500); recv(sockfd,rbuf,1500,0); cout<<"Send Mail Receive: "< // QUIT sprintf(buf,"QUIT\r\n"); send(sockfd,buf,strlen(buf),0); memset(rbuf,0,1500); recv(sockfd,rbuf,1500,0); cout<<"Quit Receive: "< //清理工作 closesocket(sockfd); WSACleanup(); return; } // 打开TCP Socket连接 int OpenSocket(struct sockaddr *addr) { int sockfd =0; sockfd=socket(PF_INET,SOCK_STREAM,0); if(sockfd <0) { cout<<"Open sockfd(TCP) error!"< exit(-1); } if(connect(sockfd,addr,sizeof(struct sockaddr))<0) { cout<<"Connect sockfd(TCP) error!"< exit(-1); } return sockfd; } 打开vc++ 编写代码到源文件中,注意, // USERmemset(buf,0,1500); sprintf(buf,"你的邮箱账号"); memset(login,0,128); EncodeBase64(login,buf,strlen(buf)); sprintf(buf,"%s\r\n",login); send(sockfd,buf,strlen(buf),0); cout<<"Base64 UserName:< recv(sockfd,rbuf,1500,0); cout<<"User Login Receive: "< // PASSWORDsprintf(buf,"XXXXXX");//你的邮箱密码 sprintf(buf,"你的邮箱账号");sprintf(buf,"XXXXXX");//你的邮箱密码 填写你的发件邮箱名称和邮箱密码 源代码如下: #include #include #include #include using namespace std; #pragma comment(lib,"ws2_32.lib") struct Base64Date6 { unsigned int d4:6; unsigned int d3:6; unsigned int d2:6; unsigned int d1:6; }; // 协议中加密部分使用的是base64方法 char ConvertToBase64(char c6); void EncodeBase64(char*dbuf,char*buf128,int len); void SendMail(char*email,char*body); int OpenSocket(struct sockaddr *addr); int main() { char EmailTo[]="676255267@qq.com";//发给的邮箱 char EmailContents; char info; sprintf(info,"Subject: %s:%s:\r\n\r\n",server_ip,server_port); AfxMessageBox(info); sprintf(EmailContents,"From: \"lucy\" sendmail(EmailTo,EmailContents); return 0; } char ConvertToBase64(char uc) { if(uc <26) { return'A'+uc; } if(uc <52) { return'a'+(uc -26); } if(uc <62) { return'0'+(uc -52); } if(uc ==62) { return'+'; } return'/'; } // base64的实现 void EncodeBase64(char*dbuf,char*buf128,int len) { struct Base64Date6*ddd =NULL; int i =0; char buf={0}; char *tmp =NULL; char cc ='\0'; memset(buf,0,256); strcpy(buf,buf128); for(i =1;i <=len/3;i++) { tmp =buf +(i-1)*3; cc =tmp; tmp=tmp; tmp=cc; ddd =(struct Base64Date6*)tmp; dbuf[(i-1)*4+0]=ConvertToBase64((unsigned int)ddd->d1); dbuf[(i-1)*4+1]=ConvertToBase64((unsigned int)ddd->d2); dbuf[(i-1)*4+2]=ConvertToBase64((unsigned int)ddd->d3); dbuf[(i-1)*4+3]=ConvertToBase64((unsigned int)ddd->d4); } if(len %3==1) { tmp =buf +(i-1)*3; cc =tmp; tmp=tmp; tmp=cc; ddd =(struct Base64Date6*)tmp; dbuf[(i-1)*4+0]=ConvertToBase64((unsigned int)ddd->d1); dbuf[(i-1)*4+1]=ConvertToBase64((unsigned int)ddd->d2); dbuf[(i-1)*4+2]='='; dbuf[(i-1)*4+3]='='; } if(len%3==2) { tmp =buf+(i-1)*3; cc =tmp; tmp=tmp; tmp=cc; ddd =(struct Base64Date6*)tmp; dbuf[(i-1)*4+0]=ConvertToBase64((unsigned int)ddd->d1); dbuf[(i-1)*4+1]=ConvertToBase64((unsigned int)ddd->d2); dbuf[(i-1)*4+2]=ConvertToBase64((unsigned int)ddd->d3); dbuf[(i-1)*4+3]='='; } return; } // 发送邮件 void SendMail(char*email,char*body) { int sockfd ={0}; char buf={0}; char rbuf={0}; char login={0}; char pass={0}; WSADATA WSAData; struct sockaddr_in their_addr ={0}; WSAStartup(MAKEWORD(2,2),&WSAData); memset(&their_addr,0,sizeof(their_addr)); their_addr.sin_family =AF_INET; their_addr.sin_port =htons(25); hostent*hptr =gethostbyname("smtp.163.com"); // 用的是163服务器 memcpy(&their_addr.sin_addr.S_un.S_addr,hptr->h_addr_list,hptr->h_length); printf("IP of smpt.126.com is : %d:%d:%d:%d\n", their_addr.sin_addr.S_un.S_un_b.s_b1, their_addr.sin_addr.S_un.S_un_b.s_b2, their_addr.sin_addr.S_un.S_un_b.s_b3, their_addr.sin_addr.S_un.S_un_b.s_b4); // 连接邮件服务器,如果连接后没有响应,则2 秒后重新连接 sockfd =OpenSocket((struct sockaddr *)&their_addr); memset(rbuf,0,1500); while(recv(sockfd,rbuf,1500,0)==0) { cout<<"reconnect..."< Sleep(2); sockfd =OpenSocket((struct sockaddr *)&their_addr); memset(rbuf,0,1500); } cout< // EHLO memset(buf,0,1500); sprintf(buf, "EHLO HYL-PC\r\n"); send(sockfd,buf,strlen(buf),0); memset(rbuf,0,1500); recv(sockfd,rbuf,1500,0); cout<<"EHLO REceive: "< // AUTH LOGIN memset(buf,0,1500); sprintf(buf,"AUTH LOGIN\r\n"); send(sockfd,buf,strlen(buf),0); memset(rbuf,0,1500); recv(sockfd,rbuf,1500,0); cout<<"Auth Login Receive: "< // USER memset(buf,0,1500); sprintf(buf,"wxbliaotian@163.com"); memset(login,0,128); EncodeBase64(login,buf,strlen(buf)); sprintf(buf,"%s\r\n",login); send(sockfd,buf,strlen(buf),0); cout<<"Base64 UserName: "< memset(rbuf,0,1500); recv(sockfd,rbuf,1500,0); cout<<"User Login Receive: "< // PASSWORD sprintf(buf,"XXXXXX");//你的邮箱密码 memset(pass,0,128); EncodeBase64(pass,buf,strlen(buf)); sprintf(buf,"%s\r\n",pass); send(sockfd,buf,strlen(buf),0); cout<<"Base64 Password: "< memset(rbuf,0,1500); recv(sockfd,rbuf,1500,0); cout<<"Send Password Receive: "< // MAIL FROM memset(buf,0,1500); sprintf(buf,"MAIL FROM: <"); send(sockfd,buf,strlen(buf),0); memset(rbuf,0,1500); recv(sockfd,rbuf,1500,0); cout<<"set Mail From Receive: "< // RCPT TO 第一个收件人 sprintf(buf,"RCPT TO:<%s>\r\n",email); send(sockfd,buf,strlen(buf),0); memset(rbuf,0,1500); recv(sockfd,rbuf,1500,0); cout<<"Tell Sendto Receive: "< // DATA 准备开始发送邮件内容 sprintf(buf,"DATA\r\n"); send(sockfd,buf,strlen(buf),0); memset(rbuf,0,1500); recv(sockfd,rbuf,1500,0); cout<<"Send Mail Prepare Receive: "< // 发送邮件内容,\r\n.\r\n内容结束标记 sprintf(buf,"%s\r\n.\r\n",body); send(sockfd,buf,strlen(buf),0); memset(rbuf,0,1500); recv(sockfd,rbuf,1500,0); cout<<"Send Mail Receive: "< // QUIT sprintf(buf,"QUIT\r\n"); send(sockfd,buf,strlen(buf),0); memset(rbuf,0,1500); recv(sockfd,rbuf,1500,0); cout<<"Quit Receive: "< //清理工作 closesocket(sockfd); WSACleanup(); return; } // 打开TCP Socket连接 int OpenSocket(struct sockaddr *addr) { int sockfd =0; sockfd=socket(PF_INET,SOCK_STREAM,0); if(sockfd <0) { cout<<"Open sockfd(TCP) error!"< exit(-1); } if(connect(sockfd,addr,sizeof(struct sockaddr))<0) { cout<<"Connect sockfd(TCP) error!"< exit(-1); } return sockfd; } 运行结果如下,显示邮件发送程序运行正常,并且收到邮件,祝大家成功



