发送电子邮件的程序一、程序界面设计smtp电子邮件发送程序的用户界面填入smtp服务器地址、邮箱用户名和口令,端口号是25,并填入发信人,发信地址填入你在该网站的免费邮箱地址。
在对话框的右面填入发送电子邮件的相关信息,选择一个附件,然后点击“发送”按钮,程序会与服务器建立TCP连接,然后按照ESMTP协议发送ELHO命令,然后发送用户名和口令,经过验证,进入SMTP会话。
通过命令交互,将邮件和附件发送出去,然后断开连接。
在此过程中,右下方的多文本列表框(RichTextBox)会显示全部的会话信息。
现在的SMTP服务器与以前不一样,一般都要经过验证身份后,才为你提供传输邮件的服务,验证的方法有很多种,这里只实现了一种,仅仅为了说明问题。
程序实现的技术要点是:1.运用Windows的消息驱动机制2.通过状态转换来控制会话命令的发布顺序3.实现了base64编码和译码。
二、创建应用程序的过程1.使用MFC AppWizard创建应用程序框架工程名是Smtp,应用程序的类型是基于对话框的,对话框的标题是“电子邮件发送客户端程序”,需要Windows Sockets的支持,其它部分接受系统的默认设置就可以。
向导自动为应用程序创建了两个类:应用程序类:CSmtpApp,基类是CWinApp,对应的文件是Smtp.h和Smtp.cpp。
对话框类:CSmtpDlg,基类是CDialog,对应的文件是SmtpDlg.h和SmtpDlg.cpp。
2.为对话框添加控件在程序的主对话框界面中添加相应的控件对象,并按照下表修改控件的属性。
3.定义控件的成员变量为对话框中的控件对象定义相应的成员变量。
4.为控件对象添加事件响应函数5.Base64编码和解码创建一个普通的类,用来实现base64编码和解码。
6.创建CMySocket类为了能够捕获并响应socket事件,应创建用户自己的套接字类,可利用类向导添加。
Class Type选择MFC Class,类名为CMySocket,基类是CAsyncSocket类,创建后对应的文件是MySocket.h和MySocket.cpp。
在利用类向导为CMySocket类添加OnConnect,OnClose和OnReceive三个事件处理函数,并为它添加一般的成员函数和变量。
可参看下一小节的程序代码。
7.手工添加包含语句以及事件函数和成员函数的代码8.分阶段编译执行,进行测试三、程序清单1.CSmtpApp应用程序类Smtp.hSmtp.cpp//CSmtpApp::InitInstance()添加一句初始化多文本框控件的函数。
BOOL CSmtpApp::InitInstance(){if (!AfxSocketInit()){AfxMessageBox(IDP_SOCKETS_INIT_FAILED);return FALSE;}AfxEnableControlContainer(); //MFC自动创建的代码AfxInitRichEdit(); //用户自己添加的...............}2.CSmtpDlg对话框类SmtpDlg.h.................#include "MySocket.h" //自己添加的包含语句#include "Base64.h" //自己添加的包含语句...............class CSmtpDlg : public CDialog{// Constructionpublic:void Display(LONG flag); //显示信息void GetHeader(); //创建电子邮件的头部CMySocket smtpSocket; //套接字类对象CSmtpDlg(CWnd* pParent = NULL); // standard constructor//类向导自动产生的窗口控件变量的声明// Dialog Data//{{AFX_DATA(CSmtpDlg)enum { IDD = IDD_SMTP_DIALOG };CString m_strAddr; //发信地址CString m_strAttach; //附件CString m_strBCC; //暗送CString m_strCC; //抄送CString m_strLetter; //信件内容CString m_strSender; //发信人CString m_strPassword; //口令UINT m_nServPort; //端口CString m_strReceiver; //收信CString m_strServName; // SMTP服务器CString m_strTitle; //主题CString m_strUserName; //用户名CString m_strInfo; //会话状态信息//}}AFX_DATA .....................//类向导自动生成的消息映射函数声明//{{AFX_MSG(CSmtpDlg)virtual BOOL OnInitDialog();afx_msg void OnSysCommand(UINT nID, LPARAM lParam);afx_msg void OnPaint();afx_msg HCURSOR OnQueryDragIcon();afx_msg void OnButtonView(); //点击“浏览”按钮时执行virtual void OnOK(); //点击“发送“按钮时执行//}}AFX_MSGDECLARE_MESSAGE_MAP()private:CBase64 coder; //进行Base64编码的变量BOOL GetBody(LPSTR& pszBody, int& nBodySize);//构造邮件体};..............SmtpDlg.cpp#include "stdafx.h"#include "smtp.h"#include "smtpDlg.h"#include "MySocket.h" //自己添加的包含语句#include "Base64.h" //自己添加的包含语句#define SMTP_MAXLINE 76 .....................// CSmtpDlg dialog//CSmtpDlg::CSmtpDlg()CSmtpDlg::CSmtpDlg(CWnd* pParent /*=NULL*/): CDialog(CSmtpDlg::IDD, pParent){//{{AFX_DATA_INIT(CSmtpDlg)m_strAddr = _T(""); //这一部分是由类向导自动添加的变量初始化代码m_strAttach = _T("");m_strBCC = _T("");m_strCC = _T("");m_strLetter = _T("");m_strSender = _T("");m_strPassword = _T("");m_nServPort = 0;m_strReceiver = _T("");m_strServName = _T("");m_strTitle = _T("");m_strUserName = _T("");m_strInfo = _T("");//}}AFX_DATA_INIT// Note that LoadIcon does not require a subsequent DestroyIcon in Win32m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);}//类向导自动添加的窗口控件与相应控件变量的映射关系//CSmtpDlg::DoDataExchange()void CSmtpDlg::DoDataExchange(CDataExchange* pDX){CDialog::DoDataExchange(pDX);//{{AFX_DATA_MAP(CSmtpDlg)DDX_Text(pDX, IDC_EDIT_ADDRESS, m_strAddr);DDX_Text(pDX, IDC_EDIT_ATTACH, m_strAttach);DDX_Text(pDX, IDC_EDIT_BCC, m_strBCC);DDX_Text(pDX, IDC_EDIT_CC, m_strCC);DDX_Text(pDX, IDC_EDIT_LETTER, m_strLetter);DDX_Text(pDX, IDC_EDIT_SENDER, m_strSender);DDX_Text(pDX, IDC_EDIT_PASSWORD, m_strPassword);DDX_Text(pDX, IDC_EDIT_SERVPORT, m_nServPort);DDX_Text(pDX, IDC_EDIT_RECEIVER, m_strReceiver);DDX_Text(pDX, IDC_EDIT_SERVNAME, m_strServName);DDX_Text(pDX, IDC_EDIT_TITLE, m_strTitle);DDX_Text(pDX, IDC_EDIT_USERNAME, m_strUserName);DDX_Text(pDX, IDC_RICH_LIST, m_strInfo);//}}AFX_DATA_MAP}//类向导自动添加的消息映射BEGIN_MESSAGE_MAP(CSmtpDlg, CDialog)//{{AFX_MSG_MAP(CSmtpDlg)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_BN_CLICKED(IDC_BUTTON_VIEW, OnButtonView)//}}AFX_MSG_MAPEND_MESSAGE_MAP()///////////////////////////////////////////////////////////////////////////// // CSmtpDlg message handlers//CSmtpDlg::OnInitDialog()BOOL CSmtpDlg::OnInitDialog(){......................//手工添加的初始化代码// TODO: Add extra initialization herem_strSender = _T("wang"); //发信人m_strAddr=_T("***********");//发信地址m_strServName = _T(""); //smtp服务器m_nServPort = 25; //smtp的保留端口m_strUserName = _T("EXAMPLE"); //用户名m_strPassword = _T("123456"); //口令m_strReceiver=_T("***********");//收信人地址m_strTitle = _T(""); //主题m_strCC = _T(""); //抄送m_strBCC = _T(""); //暗送m_strLetter = _T(""); //信件内容m_strAttach = _T(""); //附件UpdateData(FALSE); //更新用户界面return TRUE; // return TRUE unless you set the focus to a control }................//以下函数的代码都要手工添加//当用户点击“发送”按钮时,执行此函数//CSmtpDlg::OnOK()void CSmtpDlg::OnOK(){//设定smtp类的变量,使之指向本对话框,以便传递信息smtpSocket.SetParent(this);UpdateData(TRUE); //取来用户在对话框中输入的数据smtpSocket.Create(); //创建套接字对象的底层套接字smtpSocket.Connect((LPCSTR)m_strServName,m_nServPort); //连接pop3服务器//列表框清空//while (m_listInfo.GetCount()!=0)// m_listInfo.DeleteString(0);UpdateData(FALSE); //更新用户界面}//当用户点击“浏览”按钮,寻找附件时,执行此函数//CSmtpDlg::OnButtonView()void CSmtpDlg::OnButtonView(){UpdateData(TRUE);CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, _T("All Files (*.*)|*.*||"));if (dlg.DoModal() == IDOK){CString sNewFile = dlg.GetPathName();if (m_strAttach.GetLength()){m_strAttach += _T(", ");m_strAttach += sNewFile;} else m_strAttach = sNewFile;UpdateData(FALSE);}LPSTR pszBody = NULL;int nBodySize = 0;if (!GetBody(pszBody, nBodySize)){TRACE(_T("Failed in call to send body parts body, GetLastError returns: %d\n"), GetLastError());}CString s;s = pszBody;m_strInfo += s;UpdateData(FALSE);}//根据不同的情况,向用户显示不同的信息//CSmtpDlg::Display()void CSmtpDlg::Display(LONG flag){CString s;switch(flag){case S_CONNECT: //已连接到服务器,显示信息s = "已经连接到"+m_strServName+"服务器\r\n";m_strInfo += s;break;case S_RECEIVE: //收到服务器发来的数据,显示该数据m_strInfo += stMsg;break;case S_CLOSE: //显示关闭连接的信息m_strInfo += smtpSocket.error;s = "连接已经关闭\r\n";m_strInfo += s;break;}UpdateData(FALSE); //更新用户界面}//创建电子邮件的头部//CSmtpDlg::GetHeader()void CSmtpDlg::GetHeader(){UpdateData(TRUE);CString sepa;CString sReply;sReply = _T("");//创建"Date:" 标题行内容CTime now(CTime::GetCurrentTime());CString sDate(now.Format(_T("%a, %d %b %Y %H:%M:%S ")));sDate +="+0800 (CST)";CString sBuf(sReply);if (m_strAttach.GetLength()){sReply.Format(_T("MIME-Version: 1.0\r\n"));sBuf += sReply;}//添加From 和to 字段,From字段进行了编码coder.Encode(m_strAddr);sReply.Format(_T("From: =?gb2312?B?%s?=\r\n"), coder.EncodedMessage());sBuf += sReply;sReply.Format(_T("To: %s\r\n"),m_strReceiver);sBuf += sReply;//添加Date字段sReply.Format(_T("Date: %s\r\n"),sDate);sBuf += sReply;//添加subject字段,进行了编码//Subject: =?GB2312?B?XXXXXX=?= // 主题,进行了编码coder.Encode(m_strTitle);sReply.Format(_T("Subject:=?gb2312?B?%s?=\r\n"),coder.EncodedMessage());sBuf += sReply;//如果有,添加Cc 字段if (m_strCC.GetLength()){sReply.Format(_T("Cc: %s\r\n"), m_strCC);sBuf += sReply;}//如果有,添加Bcc 字段if (m_strBCC.GetLength()){sReply.Format(_T("Bcc: %s\r\n"), m_strBCC);sBuf += sReply;}//如果需要,添加Mime 字段//MIME-Version: 1.0 // MIME版本//Content-type: multipart/mixed; // 内容类型是多部分/混合型//boundary = "NextPart_000_00A" // 指定一级边界特征字符串sepa= _T("Boundary-=_HfNFaIwtPvzJDUQrvChaEKIMklNx");if (m_strAttach.GetLength()){sReply.Format(_T("MIME-Version: 1.0\r\n"));sBuf += sReply;sReply.Format("Content-Type:Multipart/mixed;boundary=%s\r\n", sepa);sBuf += sReply;sBuf += _T("\r\n");} else {sBuf += _T("\r\n");sReply.Format(_T(" %s\r\n"), m_strLetter);sBuf += sReply;sReply.Format(_T("%c%c.%c%c"),13,10,13,10);sBuf += sReply;}smtpSocket.Send((LPCSTR)sBuf,sBuf.GetLength());m_strInfo+=sBuf;if (m_strAttach.GetLength()){sReply.Format(_T("--%s\r\n"),sepa);sBuf = sReply;sBuf += _T("Content-Type: text/plain; charset='gb2312'\r\n");sBuf += _T("Content-Transfer-Encoding: base64\r\n");sBuf += _T("\r\n");coder.Encode(m_strLetter);sReply.Format(_T("%s\r\n"),coder.EncodedMessage());sBuf += sReply;sReply.Format(_T("--%s\r\n"), sepa);sBuf += sReply;sBuf += _T("Content-Type: text/plain; charset='gb2312'\r\n");sBuf += _T("Content-Transfer-Encoding: base64\r\n");sBuf += _T("\r\n");//add LetterLPSTR pszBody = NULL;int nBodySize = 0;if (!GetBody(pszBody, nBodySize)){TRACE(_T("Failed in call to send body parts body, GetLastError returns: %d\n"), GetLastError());}sReply = pszBody;sBuf += sReply;sReply.Format(_T("--%s\r\n"), sepa);sBuf += sReply;sReply.Format(_T("%c%c.%c%c"),13,10,13,10);sBuf += sReply;smtpSocket.Send((LPCSTR)sBuf,sBuf.GetLength());m_strInfo+=sBuf;}UpdateData(FALSE);}//构造电子邮件的体部//CSmtpDlg::GetBody()BOOL CSmtpDlg::GetBody(LPSTR& pszBody, int& nBodySize){BOOL bSuccess = FALSE;//打开附件文件CFile infile;if (infile.Open(m_strAttach, CFile::modeRead | CFile::shareDenyWrite)){DWORD dwSize = infile.GetLength();if (dwSize){//读入数据BYTE* pszIn = new BYTE[dwSize];try{infile.Read(pszIn, dwSize);bSuccess = TRUE;}catch(CFileException* pEx){bSuccess = FALSE;pEx->Delete();}if (bSuccess){coder.Encode(pszIn, dwSize); //编码delete [] pszIn; //删除了输入缓冲区infile.Close(); //关闭输入文件//形成编码后的发送内容LPSTR pszEncoded = coder.EncodedMessage();int nEncodedSize = coder.EncodedMessageSize();nBodySize = nEncodedSize+(((nEncodedSize/76)+1)*2)+1;pszBody = new char[nBodySize];--nBodySize;int nInPos = 0;int nOutPos = 0;while (nInPos < nEncodedSize){int nThisLineSize = min(nEncodedSize - nInPos, SMTP_MAXLINE);CopyMemory(&pszBody[nOutPos],&pszEncoded[nInPos], nThisLineSize);nOutPos += nThisLineSize;CopyMemory(&pszBody[nOutPos], "\r\n", 2);nOutPos += 2;nInPos += nThisLineSize;}pszBody[nOutPos] = '\0'; //以空字符串结束}} else {bSuccess = TRUE;pszBody = NULL;nBodySize = 0;}} elseTRACE(_T("No bodypart body text or filename specified!\n"));return bSuccess;}3.CMySocket套接字类MySocket.h..................#include "Base64.h" //自己添加的包含语句#include <vector>#include <strstream>#include <string>using namespace std;class CSmtpDlg;//表示显示信息的标志#define S_CLOSE 1#define S_CONNECT 2#define S_RECEIVE 3#define S_GETNUMMSGS 4#define S_GETSIZEMSGS 5#define S_ENDRETR 6//表示smtp会话状态的枚举类型typedef enum {FIRST=0,EHLO,AUTH,USER,PASS,MAIL,RCPT,DATA,QUIT} STATE;//自己的套接字类定义class CMySocket : public CAsyncSocket{// Attributespublic:CString lastMsg;CString error;// Operationspublic:void Close(); //退出服务器void SetParent(CSmtpDlg * pDlg);CMySocket();virtual ~CMySocket();// Overridespublic:// ClassWizard generated virtual function overrides//{{AFX_VIRTUAL(CMySocket)public:virtual void OnConnect(int nErrorCode);virtual void OnReceive(int nErrorCode);virtual void OnClose(int nErrorCode);//}}AFX_VIRTUAL// Generated message map functions//{{AFX_MSG(CMySocket)// NOTE - the ClassWizard will add and remove member functions here.//}}AFX_MSG// Implementationprotected:private:void AnalyzeMsg(); //分析从服务器发来的数据,做出响应的响应CSmtpDlg* m_pDlg; //指向主对话框的指针STATE state; //smtp会话的状态CBase64 coder; //进行Base64编码的变量};..................MySocket.cpp ..................#include "smtpDlg.h" //自己添加的包含语句#include "Base64.h"................#define MAX_BUFF 20000//CMySocket::CMySocket()CMySocket::CMySocket(){m_pDlg = NULL;state=FIRST;error="连接不上服务器\r\n";}//CMySocket::~CMySocket()CMySocket::~CMySocket(){m_pDlg = NULL;}.................// CMySocket member functions//当套接字收到FD_CONNECT消息时,执行此函数,表明连接已建立//CMySocket::OnConnect()void CMySocket::OnConnect(int nErrorCode){if(nErrorCode==0) m_pDlg->Display(S_CONNECT);}//当套接字收到服务器发来的数据时,执行此函数//CMySocket::OnReceive()void CMySocket::OnReceive(int nErrorCode){if(nErrorCode==0){char buff[MAX_BUFF]; //接收缓冲区int rec=Receive(buff,MAX_BUFF); //接收服务器发来的数据buff[rec]=NULL; //结尾置为NULL。