【转】socket smtp 邮件带附件MFC编程

来源:互联网 发布:μvision是什么软件 编辑:程序博客网 时间:2024/06/05 18:58

转自:http://www.cnblogs.com/helihui123/articles/2387089.html

网上发邮件的MFC的代码还是比较多的,但都是不太完整,找到一个比较好的却没有验证帐号密码的。然后看代码封装的比较好的,

链接是:http://www.pudn.com/downloads229/sourcecode/windows/network/detail1075396.html

不过,如果你功底比较好,看起来可能会比较轻松的。

但用起来还是美中不足啊,缺个验证的。然后不知道原理的话修改实在很困难,不折腾个几天还真是搞不好啊。

我就折腾了几天 终于可以发文本邮件  html  以及附件发送。

 

对于初学者还是要学习知识为主,把原理搞清楚,才是最重要的。

首先,我做的时候找到了这个资料:

SMTP命令:http://blog.csdn.net/helihui123/article/details/6704189

文件下载地址:http://download.csdn.net/source/3534820

在“开始”-->”运行” 输入cmd  然后就可以按照资料一步一步的下去,这里需要注意的是那边的帐号密码是需要base64编码过的才行,那么可以进

http://tool.chinaz.com/Tools/Base64.aspx  这个链接,可以在线编码解码。

做完上面SMTP命令 的操作,惊奇的发现居然可以发邮件,哈哈~~ 至少可以发文本了。良好的开始是成功的一半嘛,快接近了  ~  开心~~

然后呢,我们是程序实现的,然后根据所学的知识,知道可以用套接字(socket),然后就是模拟上面的命令发送给邮件服务器。

但在模拟前先冲冲电,邮件格式,这里要提到MIME协议:

文件下载地址:http://download.csdn.net/source/3534961

看完上面的协议大概了解邮件的格式了吧。

看看很简单的代码吧:(代码控制台的 C语言写的 相当简洁  肯定是你满意的,目前只是基于纯文本的  不能加附件什么的还要加的话得看下MIME协议)

源码下载地址:http://download.csdn.net/source/3535894

 

 

 

好了,现在来看看怎么发附件吧。当然最先的还是基础知识了,其实在上面那个MIME协议的介绍里面已经有了的。

记得有这么一段吧:来分析分析看看

 1 From: "bhw98" bhw98@sina.com    //邮件创建者 前面bhw98是邮件地址的别名而已

 2 Reply-To: bhw98@sina.com  //回复邮件地址 

 3 To: <bluesky7810@163.com> //收信方的邮件地址

 4 Subject: Re: help          //邮件标题

 5 X-Mailer: Foxmail 4.2 [cn] //版本说明 这个X前缀的话是自己懂的 非标准的 可有可无

 6 Mime-Version: 1.0       // mime协议版本

 7 Content-Type: multipart/alternative; //MIME的内容类型 这个是最低的吧 内容只能包含超文本和文本。

 8 boundary="=====002_Dragon307572345230_====="  //part1标记

 9

 10

 11 This is a multi-part message in MIMEformat.

 12

 13 --=====002_Dragon307572345230_=====           //part2标记

 14 Content-Type: text/plain;charset="GB2312"

 15 Content-Transfer-Encoding: quoted-printable

 16

 17 bluesky7810=A3=AC=C4=FA=BA=C3=A3=A1

 18

 19=A1=A1=A1=A1=D4=DA=CF=C2=C6=AA=D7=EE=BA=F3=BF=C9=D2=D4=CF=C2=D4=D8=B0=A1=A3=AC=C4=E3

   ... ... ... ...

 30=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A12003-04-07

 31

 32 --=====002_Dragon307572345230_=====

 33 Content-Type: text/html;charset="GB2312"

 34 Content-Transfer-Encoding: quoted-printable

 35

 36 <!DOCTYPE HTML PUBLIC "-//W3C//DTDHTML 4.0 Transitional//EN">

 37 <HTML><HEAD>

 38 <METAcontent=3D"text/html; charset=3Dgb2312"=

 39 http-equiv=3DContent-Type>

 40 <METAcontent=3D"MSHTML 5.00.2920.0" name=3DGENERATOR>

   ... ... ... ...

 79 </HTML>

 80

//上面的不解释了  最重要的附件这里貌似还没有 看这个

81 --=====002_Dragon307572345230_=====

82 Content-Type:application/octet-stream;

83 name="result.txt"  //这个就是附件 这里为什么不用路径呢 这里只是给文件取个名字而已  真正的文件在下面

84 Content-Transfer-Encoding:base64

85 MQ==  //就是这个 就是文件内容了 只不过已经编码过了  base64

 --=====002_Dragon307572345230_=====--

 

看过上面那么一大段  其实我们发送的时候也就是动态的生成那么一段然后发送给服务器就可以了。

 

是不是思路比较清晰呢。。。  先不要想太多把握大局

 更详细的MIME协议的学习比较好的资料:http://download.csdn.net/source/3536315  最好先看下 

可能有了程序大家分析起来就容易多了 举例的程序会非常的简单

源码下载地址:http://download.csdn.net/source/3535756

其源码如下(控制台):

#include <windows.h>#include <winsock.h>#include <assert.h>#include <iostream>#include <string>#include <stdio.h>#include <ctime>#include <stdlib.h>#include <fcntl.h>#include <sys/types.h>#include <sys/stat.h>#include <io.h>#pragma comment(lib,"WS2_32.lib")using namespace std;/*加附件的版本*///base64编码string Base64Encode(LPCTSTR lpszSrc);//base64解码string Base64Decode(LPCTSTR lpszSrc);//读文件数据bool ReadFromFile(const char* pszFilename,string &filename);unsigned char* m_pbText;int main(){//1.首先需要连接邮件服务器  这里用socket 邮件服务器端口 25WSADATA Wsa;//进行WINSOCK的设置WSAStartup(0x0101,&Wsa);SOCKET s = socket(AF_INET,SOCK_STREAM,IPPROTO_IP);SOCKADDR_IN sin;LPHOSTENT lphost = gethostbyname("smtp.163.com");//这里是用网易的邮件服务器 也可以修改if(lphost)sin.sin_addr.s_addr = ((LPIN_ADDR)lphost->h_addr)->s_addr;else{printf("%s\n","获取地址失败");return 1;}sin.sin_family = AF_INET;//注意邮件服务器的侦听端口 25sin.sin_port = htons(IPPORT_SMTP); //连接服务器if(connect(s,(LPSOCKADDR)&sin,sizeof(sin))==SOCKET_ERROR){printf("%s\n","连接错误");return 1;}printf("%s\n","连接成功!");//接收服务器初次回应char buff[1024];memset(buff,0,sizeof(buff));recv(s,buff,sizeof(buff),0);printf("服务说:%s\n",buff);/////上面已经完成连接了/////string szLine="\r\n";//相当于你按下回车//2.现在就是和服务器对话了 //问候服务器string szHelo = "HELO smtp.163.com" + szLine;printf("我说:%s\n",szHelo.c_str());send(s,szHelo.c_str(),szHelo.length(),0);memset(buff,0,sizeof(buff));recv(s,buff,sizeof(buff),0);printf("服务说:%s\n",buff);//请求验证用户密码(需要编码)string szAL = "auth login" + szLine;  //发送验证命令printf("我说:%s\n",szAL.c_str());send(s,szAL.c_str(),szAL.length(),0);memset(buff,0,sizeof(buff));recv(s,buff,sizeof(buff),0);printf("服务说:%s\n",buff); //服务器会回答说 可以输入帐号//发送帐号string szUser;cout<<Base64Decode((LPCTSTR)(buff+4));cin>>szUser;szUser = Base64Encode(szUser.c_str()) + szLine; //对输入的帐号进行base64编码send(s,szUser.c_str(),szUser.length(),0); //发送帐号printf("我说:%s\n",szUser.c_str());memset(buff,0,sizeof(buff));recv(s,buff,sizeof(buff),0);printf("服务说:%s\n",buff); //服务器会回答说 可以输入密码//发送密码string szPsw;cout<<Base64Decode((LPCTSTR)(buff+4));cin>>szPsw;szPsw = Base64Encode(szPsw.c_str()) + szLine; //对输入的密码进行base64编码send(s,szPsw.c_str(),szPsw.length(),0); //发送密码printf("我说:%s\n",szPsw.c_str());memset(buff,0,sizeof(buff));recv(s,buff,sizeof(buff),0);printf("服务说:%s\n",buff); //然后可以用你登陆的邮箱给其他邮箱发邮件了string szFrom,szTo;cout<<"from:";//你自己的邮箱cin>>szFrom;cout<<"to:"; //发送给谁cin>>szTo;//发送者的地址string From = "mail from:<"+szFrom+ ">"+ szLine;//收信者地址string To = "rcpt to:<" + szTo +">" + szLine;//现在确定看谁发的邮件 往哪发 send(s,From.c_str(),From.length(),0);//fromprintf("我说:%s\n",From.c_str());memset(buff,0,sizeof(buff));recv(s,buff,sizeof(buff),0);printf("服务说:%s\n",buff); getchar();   //只是用来暂停一下而已 按任意字母继续send(s,To.c_str(),To.length(),0); //toprintf("我说:%s\n",To.c_str());memset(buff,0,sizeof(buff));recv(s,buff,sizeof(buff),0);printf("服务说:%s\n",buff); cout<<"下面开始添加邮件标题、内容、附件等,按任意键继续..."<<endl;getchar();//资料应该都看过了吧命令里的data输入后 表示输入邮件内容了 send(s,"data\r\n",6,0); //DATA 命令发送printf("我说:%s\n","data");memset(buff,0,sizeof(buff));recv(s,buff,sizeof(buff),0);printf("服务说:%s\n",buff);//////////////////////新加内容//////////////////////////////////////////////////////其实附件也算内容吧 那么当然是从这里开始加了   //格式需要比较大的改变  先把下面这些注释掉/*//邮件 下面的信息都是当作内容处理 //邮件头string szFrom_in = "from:"+szFrom + szLine;string szTo_in = "to:"+szTo + szLine;//发送日期string szDate_in="Date: Sat, 20 Aug 2011 13:39:29 +0800" + szLine;//这里我就直接写上去日期了 为了方便//邮件标题string szSubject_in = "Subject:我是邮件标题" + szLine;//邮件正文string szBody_in = "我就是传说中的邮件体" + szLine;string szContent = szFrom_in + szTo_in + szDate_in + szSubject_in;szContent += szLine;//添加一个空白行  szContent += szBody_in;//上面是最基本的格式//send(s,szContent.c_str(),szContent.length(),0); //szContentprintf("我说:%s\n",szContent.c_str());memset(buff,0,sizeof(buff));//recv(s,buff,sizeof(buff),0);//printf("服务说:%s\n",buff); getchar();//发送完了说明下结束send(s,".\r\n",3,0); //说明内容结束了printf("我说:%s\n",".");memset(buff,0,sizeof(buff));recv(s,buff,sizeof(buff),0);printf("服务说:%s\n",buff);*///就像这样的字符串模拟/*Date: Sat, 20 Aug 2011 17:18:50 +0800From: helihui2@163.comTo: <551721274@qq.com>Subject: Hello,WorldContent-Type: multipart/mixed;boundary="__=_Part_Boundary_001_013896.006074"--__=_Part_Boundary_001_013896.006074Content-Description: enclosed photoContent-Type: text/plain;charset="gb2312"Content-Transfer-Encoding: base64bmkgaGFvYWFhYWFhYWFhYWFhYWFhYWFhYWE=--__=_Part_Boundary_001_013896.006074Content-Type: application/octet-stream;name="result.txt"Content-Transfer-Encoding: base64MQ==--__=_Part_Boundary_001_013896.006074--*/string filename,mailContent,fileDate,szsubject; //分别是文件别名  邮件内容 都转成base64编码即可  标题//m_pbText  is saved fileData.cout<<"输入文件绝对路径:";string pszFilename;cin>>pszFilename;cout<<"正在读取...";if(ReadFromFile(pszFilename.c_str(),filename)==false){printf("文件读取失败\n");return 1;}cout<<"读取完成。\r\n请输入邮件标题:";cin>>szsubject;cout<<"输入邮件内容:";cin>>mailContent;cout<<endl;cout<<"loading..."<<endl;fileDate = Base64Encode((LPCTSTR)m_pbText);//cout<<fileDate<<endl;getchar();//好,写MIME 格式的发送内容string strCmd;  time_t tm1;char szdate[250]="";::time(&tm1);tm *today;today = localtime(&tm1);strftime(szdate, 250, "Date: %a %d %b %Y %X +0800\r\n", today); //获取当前日期而已//记住一定要按照这个格式 该换行的要换行 该空格的要空格strCmd = szdate; //日期 strCmd += "From: " + szFrom + "\r\n";strCmd += "To: " + szTo + "\r\n";strCmd += "Subject: "+ szsubject +"\r\n";strCmd += "Mime-Version: 1.0\r\n";strCmd += "Content-Type: multipart/mixed;\r\n";//注意上面这些字符串 前面不能有空格 要顶到strCmd +=" boundary=\"__=_Part_Boundary_001_011991.029871\"\r\n\r\n";  //这个必须保留一个或多个空格前面 strCmd +="--__=_Part_Boundary_001_011991.029871\r\n"; strCmd +="Content-Description: enclosed photo\r\n";strCmd +="Content-Type: text/plain;\r\n";//前面顶到strCmd +=" charset=\"gb2312\"\r\n";//至少前面空一格  可以知道了 参数和前面类型之间需要空格隔开的strCmd +="Content-Transfer-Encoding: base64\r\n\r\n";strCmd +=Base64Encode(mailContent.c_str())+"\r\n\r\n";                //邮件内容strCmd +="--__=_Part_Boundary_001_011991.029871\r\n";strCmd +="Content-Type: application/octet-stream;\r\n";//上面的顶到strCmd +=" name="+filename+"\r\n"; //同理至少空一格  strCmd +="Content-Transfer-Encoding: base64\r\n\r\n";strCmd += fileDate+"\r\n\r\n";  //文件数据 二进制编码成base64strCmd +="--__=_Part_Boundary_001_011991.029871--\r\n";strCmd +="\r\n.\r\n";  //结束符. 注意这里要多个空行哦getchar();//上面字符串拼好了  发送给服务器了可以 o(∩_∩)o 哈哈send(s,strCmd.c_str(), strCmd.length(),0);memset(buff,0,sizeof(buff));recv(s,buff,sizeof(buff),0);printf("发送成功啦\n");getchar();//退出服务器连接send(s,"quit\r\n",6,0); //退出printf("我说:%s\n","quit");memset(buff,0,sizeof(buff));recv(s,buff,sizeof(buff),0);printf("服务说:%s",buff);getchar();return 0;}//////////////////////////////////////////////////////////////////////////string Base64Decode(LPCTSTR lpszSrc){assert(lpszSrc != NULL);const unsigned int BASE64_DECODE_TABLE[256] = {255, 255, 255, 255, 255, 255, 255, 255, // 00 - 07255, 255, 255, 255, 255, 255, 255, 255, // 08 - 15255, 255, 255, 255, 255, 255, 255, 255, // 16 - 23255, 255, 255, 255, 255, 255, 255, 255, // 24 - 31255, 255, 255, 255, 255, 255, 255, 255, // 32 - 39255, 255, 255, 62, 255, 255, 255, 63, // 40 - 4752, 53, 54, 55, 56, 57, 58, 59, // 48 - 5560, 61, 255, 255, 255, 255, 255, 255, // 56 - 63255,   0,   1,   2,   3,   4,   5,   6, // 64 - 717,   8,   9, 10, 11, 12, 13, 14, // 72 - 7915, 16, 17, 18, 19, 20, 21, 22, // 80 - 8723, 24, 25, 255, 255, 255, 255, 255, // 88 - 95255, 26, 27, 28, 29, 30, 31, 32, // 96 - 10333, 34, 35, 36, 37, 38, 39, 40, // 104 - 11141, 42, 43, 44, 45, 46, 47, 48, // 112 - 11949, 50, 51, 255, 255, 255, 255, 255, // 120 - 127255, 255, 255, 255, 255, 255, 255, 255,255, 255, 255, 255, 255, 255, 255, 255,255, 255, 255, 255, 255, 255, 255, 255,255, 255, 255, 255, 255, 255, 255, 255,255, 255, 255, 255, 255, 255, 255, 255,255, 255, 255, 255, 255, 255, 255, 255,255, 255, 255, 255, 255, 255, 255, 255,255, 255, 255, 255, 255, 255, 255, 255,255, 255, 255, 255, 255, 255, 255, 255,255, 255, 255, 255, 255, 255, 255, 255,255, 255, 255, 255, 255, 255, 255, 255,255, 255, 255, 255, 255, 255, 255, 255,255, 255, 255, 255, 255, 255, 255, 255,255, 255, 255, 255, 255, 255, 255, 255,255, 255, 255, 255, 255, 255, 255, 255,255, 255, 255, 255, 255, 255, 255, 255 };const int nSrcCount=(int)lstrlen(lpszSrc);int nSize=nSrcCount/4*3;if(lpszSrc[nSrcCount-1]=='=')nSize--;if(lpszSrc[nSrcCount-2]=='=')nSize--;char* pOutBuffer=new char[nSize+3];ZeroMemory(pOutBuffer,nSize+3);LPCTSTR pInBuffer=lpszSrc;UINT iTest,iPack;for(int i=0;i<nSize/3 ;i++){for(int j=0;j<4;j++){iTest = BASE64_DECODE_TABLE[*pInBuffer++]; // Read from InputBuffer.//InPtr++;if (iTest == 0xFF) {j--;continue; //读到255非法字符}iPack = iPack << 6 ;iPack = iPack | iTest ;}pOutBuffer[2] = iPack;iPack = iPack >> 8;pOutBuffer[1] = iPack;iPack = iPack >> 8;pOutBuffer[0] = iPack;//准备写入后3位pOutBuffer+= 3; iPack = 0;}switch(nSize%3){case 1:iTest = BASE64_DECODE_TABLE[*pInBuffer++]; // Read from InputBuffer.if (iTest != 0xFF){iPack = iPack << 6 ;iPack = iPack | iTest ;}iTest = BASE64_DECODE_TABLE[*pInBuffer++]; // Read from InputBuffer.if (iTest != 0xFF){iPack = iPack << 6 ;iPack = iPack | iTest ;}iPack = iPack >> 4;pOutBuffer[0] = iPack;pOutBuffer++;break;case 2:iTest = BASE64_DECODE_TABLE[*pInBuffer++]; // Read from InputBuffer.if (iTest != 0xFF){iPack = iPack << 6 ;iPack = iPack | iTest ;}iTest = BASE64_DECODE_TABLE[*pInBuffer++]; // Read from InputBuffer.if (iTest != 0xFF){iPack = iPack << 6 ;iPack = iPack | iTest ;}iTest = BASE64_DECODE_TABLE[*pInBuffer++]; // Read from InputBuffer.if (iTest != 0xFF){iPack = iPack << 6 ;iPack = iPack | iTest ;}iPack = iPack >> 2;pOutBuffer[1] = iPack;iPack = iPack >> 8;pOutBuffer[0] = iPack;pOutBuffer+=2;break;default:break;}pOutBuffer-=nSize;string strDecode=pOutBuffer;delete pOutBuffer;return strDecode;}string Base64Encode(LPCTSTR lpszSrc){assert(lpszSrc != NULL);//Base64编码表 const char BASE64_ENCODE_TABLE[64] = {65, 66, 67, 68, 69, 70, 71, 72, // 00 - 0773, 74, 75, 76, 77, 78, 79, 80, // 08 - 1581, 82, 83, 84, 85, 86, 87, 88, // 16 - 2389, 90, 97, 98, 99, 100, 101, 102, // 24 - 31103, 104, 105, 106, 107, 108, 109, 110, // 32 - 39111, 112, 113, 114, 115, 116, 117, 118, // 40 - 47119, 120, 121, 122, 48, 49, 50, 51, // 48 - 5552, 53, 54, 55, 56, 57, 43, 47 };// 56 - 63unsigned int iTest;LPCTSTR pInBuffer=lpszSrc;int nSize = (int)strlen(lpszSrc);char* pOutBuffer=new char[nSize/3*4+5];ZeroMemory(pOutBuffer,nSize/3*4+5);//3个字节为单位 取6位并在前面补上两个0形成新的8位编码 3*8=4*6for(UINT i=0;i<strlen(lpszSrc) / 3;i++){iTest = (unsigned char) *pInBuffer++;iTest = iTest << 8;iTest = iTest | (unsigned char) *pInBuffer++;iTest = iTest << 8;iTest = iTest | (unsigned char) *pInBuffer++;//以4 byte倒序写入输出缓冲pOutBuffer[3] = BASE64_ENCODE_TABLE[iTest & 0x3F];iTest = iTest >> 6;pOutBuffer[2] = BASE64_ENCODE_TABLE[iTest & 0x3F];iTest = iTest >> 6;pOutBuffer[1] = BASE64_ENCODE_TABLE[iTest & 0x3F];iTest = iTest >> 6;pOutBuffer[0] = BASE64_ENCODE_TABLE[iTest];pOutBuffer+=4;}//设置尾部switch (strlen(lpszSrc) % 3){case 0:break;case 1:iTest = (unsigned char) *pInBuffer;iTest = iTest << 4;pOutBuffer[1] = BASE64_ENCODE_TABLE[iTest & 0x3F];iTest = iTest >> 6;pOutBuffer[0] = BASE64_ENCODE_TABLE[iTest];pOutBuffer[2] = '='; //用'='也就是64码填充剩余部分pOutBuffer[3] = '=';break;case 2:iTest = (unsigned char) *pInBuffer++;iTest = iTest << 8;iTest = iTest | (unsigned char) *pInBuffer;iTest = iTest << 2;pOutBuffer[2] = BASE64_ENCODE_TABLE[iTest & 0x3F];iTest = iTest >> 6;pOutBuffer[1] = BASE64_ENCODE_TABLE[iTest & 0x3F];iTest = iTest >> 6;pOutBuffer[0] = BASE64_ENCODE_TABLE[iTest];pOutBuffer[3] = '='; // Fill remaining byte.break;}pOutBuffer-=nSize/3*4;string strEncode=pOutBuffer;delete [] pOutBuffer;pOutBuffer=NULL;return strEncode;}inline void FreeBuffer(){delete []m_pbText;m_pbText = NULL;}inline bool AllocateBuffer(int nBufSize){FreeBuffer();m_pbText = new unsigned char[nBufSize];if (!m_pbText)  return false;return true;}bool ReadFromFile(const char* pszFilename,string &filename){int hFile = ::open(pszFilename, O_RDONLY | O_BINARY);if (hFile < 0)return false;try{int nFileSize = (int)::lseek(hFile, 0L, SEEK_END);// get file length::lseek(hFile, 0L, SEEK_SET);FreeBuffer();if (nFileSize > 0){AllocateBuffer(nFileSize+4);unsigned char* pszData = m_pbText;for (;;){int nRead = ::read(hFile, pszData, 512);if (nRead < 0){::close(hFile);return false;}pszData += nRead;if (nRead < 512)break;}*pszData = 0;}}catch (...){::close(hFile);throw;}::close(hFile);const char* pszName = ::strrchr(pszFilename, '\\');if (!pszName)pszName = pszFilename;elsepszName++;filename = pszName;//AfxMessageBox(pszName);return true;}


看看源码应该就知道了  详细的注释的。

看看格式更多的资料看下面的扩展资料即可,我这里列举下比较重要的几点(正在不断更新...),

知识点一: Multipart Media Type(多部分类型)
      在multipart entity(多部分实体)的例子中,一个或多个不同的数据集合并在一
  个单一的body(体)中,一个"multipart"(多部分)类型 field的(域)必须出现在实
  体的header(头域)。body(体)必须包括一个或多个body part(体部分),每一个位
  于boundary(边界)定界符线之前,最后一个则跟着一个结束边界定界符线。在它的
  边界定界符线后,每一个体部分由头域、空行、体组成。因此一个体部分在语法上类
  似于RFC 822中的message(消息),但是在意义上是不同的。

知识点二:

编界定界符不能出现在压缩的原文里面,并且不能大于70个字符,不计算前面的连字符。


 

程序演示:

输入邮箱的帐号 密码:

好,发送成功了。

我们来看看真正成功了没。

收到了 o(∩_∩)o 哈哈~~~

 

更多拓展资料:http://man.lupaworld.com/content/develop/rfc/RFC2046.htm

                            http://www.w3school.com.cn/media/media_mimeref.asp

1 0
原创粉丝点击