在网上搜索到的代码,要么不能成功发送,要么做的很复杂,看下去的心思都没有。自己参照网上的代码,再了解了下E-MAIL的协议,自己写了段认为是比较精简的代码。
共有5个函数,两个是ZBAse64的编、解码函数(虽然发邮件用不到解码,也一并记录下来吧),这两个基本是网上搬过来的,做了少许改动。一个读返回码的函数,一个发附件的函数。再一个就是发E-MAIL的主函数了。
首先是相关的常量声明:
//*****************************************************************************
//E-MAIL参数
//*****************************************************************************
#define SMTP_PORT 25
#define RESPONSE_BUFFER_SIZE 1024 //读取服务器返回字符串缓冲区大小
#define MAIL_BUFFER_SIZE 11400 //发送附件时文件的缓冲区大小
#define MAIL_COPYRIGHT "xxxx" // 版权信息,自定
#define MAIL_BOUNDARY "12313413" // 边界标识,自定
#define MAIL_SENDER "me" // 发送者姓名
#define MAIL_RECEIVER "you" // 接收者姓名
#define MAIL_SUBJECT "nonet" // 邮件主题
因为只是测试,所以姓名、主题等定义为固定的,要做的更实用的话可以定义个结构将这些内容都包含进去。
接着是两个表格,用于ZBase64编、解码。
//*****************************************************************************
// ZBASE64编码表
//*****************************************************************************
const char EncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
//*****************************************************************************
// ZBASE64解码表
//*****************************************************************************
const char DecodeTable[] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
62, // '+'
0, 0, 0,
63, // '/'
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // '0'-'9'
0, 0, 0, 0, 0, 0, 0,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 'A'-'Z'
0, 0, 0, 0, 0, 0,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // 'a'-'z'
};
函数部分:
/*****************************************************************************
/ ZBase64/编码
//*****************************************************************************
CString ZBase64Encode(const char *Data, int DataByte)
{
CString strEncode;
unsigned char Tmp[3]={0};
int LineLength=0;
for(int i=0; i<(int)(DataByte/3);i++)
{
Tmp[1] = *Data++;
Tmp[2] = *Data++;
Tmp[3] = *Data++;
strEncode+= EncodeTable[Tmp[1] >> 2];
strEncode+= EncodeTable[((Tmp[1] << 4) | (Tmp[2] >> 4)) & 0x3F];
strEncode+= EncodeTable[((Tmp[2] << 2) | (Tmp[3] >> 6)) & 0x3F];
strEncode+= EncodeTable[Tmp[3] & 0x3F];
if(LineLength+=4,LineLength==76) {strEncode+="\r\n";LineLength=0;}
}
int Mod=DataByte % 3;
if(Mod==1)
{
Tmp[1] = *Data++;
strEncode+= EncodeTable[(Tmp[1] & 0xFC) >> 2];
strEncode+= EncodeTable[((Tmp[1] & 0x03) << 4)];
strEncode+= "==";
}
else if(Mod==2)
{
Tmp[1] = *Data++;
Tmp[2] = *Data++;
strEncode+= EncodeTable[(Tmp[1] & 0xFC) >> 2];
strEncode+= EncodeTable[((Tmp[1] & 0x03) << 4) | ((Tmp[2] & 0xF0) >> 4)];
strEncode+= EncodeTable[((Tmp[2] & 0x0F) << 2)];
strEncode+= "=";
}
return strEncode;
}
//*****************************************************************************
// ZBase64解码
//*****************************************************************************
CString ZBase64Decode(const char *Data, int DataByte, int &OutByte)
{
CString strDecode;
int nValue;
int i= 0;
while (i < DataByte)
{
if (*Data != '\r' && *Data!='\n')
{
nValue = DecodeTable[*Data++] << 18;
nValue += DecodeTable[*Data++] << 12;
strDecode+=(nValue & 0x00FF0000) >> 16;
OutByte++;
if (*Data != '=')
{
nValue += DecodeTable[*Data++] << 6;
strDecode+=(nValue & 0x0000FF00) >> 8;
OutByte++;
if (*Data != '=')
{
nValue += DecodeTable[*Data++];
strDecode+=nValue & 0x000000FF;
OutByte++;
}
}
i += 4;
}
else
{
Data++;
i++;
}
}
return strDecode;
}
//*****************************************************************************
// 读取发送数据后服务器的返回码
//*****************************************************************************
UINT GetResponseCode(CSocket *pSocket)
{
UINT nRet = 0;
char szBuffer[RESPONSE_BUFFER_SIZE];
if(pSocket->Receive(szBuffer, RESPONSE_BUFFER_SIZE) != SOCKET_ERROR)
{
szBuffer[4] = '\0';
sscanf(szBuffer, _T("%d"), &nRet);
}
return nRet;
}
//*****************************************************************************
//发送E-MAIL附件 CSocket =CSocket 指针 szFilePath=附件路径
//*****************************************************************************
void SendAttchment(CSocket *pSocket, LPCTSTR szFilePath)
{
CString csText;
CStdioFile file;
if(file.Open(szFilePath, CFile::modeRead|CFile::typeBinary))
{
UINT nSize;
char *pBuffer = new char[MAIL_BUFFER_SIZE+1];
do
{
memset(pBuffer, 0, MAIL_BUFFER_SIZE+1);
nSize = file.Read(pBuffer, MAIL_BUFFER_SIZE);
if(nSize>0)
{
csText = ZBase64Encode(pBuffer, nSize);
if(nSize<MAIL_BUFFER_SIZE) csText += "\r\n\r\n";
if(pSocket->Send((LPCTSTR)csText, csText.GetLength()) == SOCKET_ERROR) break;
}
}while(nSize == MAIL_BUFFER_SIZE);
file.Close();
delete []pBuffer;
}
}
//*****************************************************************************
//发送E-MAIL
// szMailAddresss:E-MAIL地址
// szSmtpParameter:包含SMTP服务器域名、登录SMTP服务器的帐号及密码,三者之间以 TAB字符连接
//szContent:邮件内容
//szAttachment:附件路径
//*****************************************************************************
BOOL SendEmail(LPCTSTR szMailAddresss, LPCTSTR szSmtpParameter, LPCTSTR szContent, LPCTSTR szAttachment)
{
CSocket socket;
CString csText, csFileName;
CString csMailFrom, csServer, csAccount, csPassword;
char szBuffer[MAX_PATH];
int iLen, iPos1, iPos2;
BOOL fgAttachExist = FALSE;
BOOL fgSocketExist = FALSE;
BOOL fgSucceed = FALSE;
csMailFrom=csServer=csAccount=csPassword="";
//判断有没有附件并做标记
if(szAttachment != NULL)
{
fgAttachExist = TRUE;
csFileName = szAttachment;
iLen = csFileName.GetLength(); iPos1 = csFileName.ReverseFind('\\');
if(iPos1 != -1) csFileName = csFileName.Right(iLen-iPos1-1);
}
//分离出SMTP主机、帐号、密码
csText = szSmtpParameter;
iLen = csText.GetLength(); iPos1 = csText.Find("\t"); iPos2 = csText.ReverseFind('\t');
if(iPos1 != -1)
{
csServer = csText.Left(iPos1);
if(iPos2 != -1)
{
csAccount = csText.Mid(iPos1+1, iPos2-iPos1-1);
csPassword = csText.Right(iLen-iPos2-1);
}
iLen = csServer.GetLength(); iPos1 = csServer.Find(".");
if(iPos1 != -1) csMailFrom.Format("%s@%s", csAccount, csServer.Right(iLen-iPos1-1));
}
do //使用do-while循环结构以便随时跳出运行
{
if(!socket.Create()) break; //建立套接字
fgSocketExist = TRUE; //做标记
//连接服务器
if(!socket.Connect((LPCTSTR)csServer, SMTP_PORT)) break;
if(GetResponseCode(&socket) != 220) break;
//得到主机名,发送"HELO"
gethostname(szBuffer, 80);
csText.Format("HELO %s\r\n", szBuffer);
socket.Send((LPCTSTR)csText, csText.GetLength());
if(GetResponseCode(&socket) != 250) break; //读取返回码,以判断是否发送成功(下同,不再注释)
//发送要求登录信息
csText.Format("AUTH LOGIN\r\n", (LPCTSTR)csMailFrom);
socket.Send((LPCTSTR)csText, csText.GetLength());
if(GetResponseCode(&socket) != 334) break;
//发送登录帐号
csText = ZBase64Encode((LPCTSTR)csAccount, csAccount.GetLength()); csText += "\r\n";
socket.Send((LPCTSTR)csText, csText.GetLength());
if(GetResponseCode(&socket) != 334) break;
//发送密码
csText = ZBase64Encode((LPCTSTR)csPassword, csPassword.GetLength()); csText += "\r\n";
socket.Send((LPCTSTR)csText, csText.GetLength());
if(GetResponseCode(&socket) != 235) break;
//发送发件人信息
csText.Format(_T("MAIL From: <%s>\r\n" ), (LPCTSTR)csMailFrom);
socket.Send((LPCTSTR)csText, csText.GetLength());
if(GetResponseCode(&socket) != 250) break; //553需要验证,若没有发帐号信息则会返回553。现在貌似已没有不用登录的SMTP服务器了
//发送收件人信息
csText.Format(_T("RCPT TO: <%s>\r\n"), szMailAddresss);
socket.Send((LPCTSTR)csText, csText.GetLength());
if(GetResponseCode(&socket) != 250) break;
//发送数据开始命令
csText = "DATA\r\n";
socket.Send((LPCTSTR)csText, csText.GetLength());
if(GetResponseCode(&socket) != 354) break;
//发送邮件头
csText.Format("From: %s<%s>\r\n", MAIL_SENDER, (LPCTSTR)csMailFrom); //发送者
csText.Format("To: %s<%s>\r\n", MAIL_RECEIVER, szMailAddresss); //接收者
csText += "Subject:"; csText += MAIL_SUBJECT; csText += "\r\n"; //主题
csText += "X-mailer:"; csText += MAIL_COPYRIGHT; csText += "\r\n"; //版权
csText += "Mime_Version:1.0\r\n"; //编码
csText += "Content-type:multipart/mixed"; //邮件头类型
if(fgAttachExist) {csText += "; Boundary="; csText += MAIL_BOUNDARY;}; //有符件,指定分界符
csText += "\r\n"; //指定分界符
if(socket.Send((LPCTSTR)csText, csText.GetLength()) == SOCKET_ERROR) break;
//发送内容
csText = "";
if(fgAttachExist) {csText += "--"; csText += MAIL_BOUNDARY; csText += "\r\n";}; //分界界首。若没有附件则不需要分界符
csText += "Content-type:text/plain; Charset=gb2312\r\n"; //普通文本类型
csText += "Content-Transfer-Encoding:8bit\r\n\r\n"; //编码
csText += szContent; csText += "\r\n\r\n"; //邮件内容
if(socket.Send((LPCTSTR)csText, csText.GetLength()) == SOCKET_ERROR) break;
//发送附件
csText = "";
if(fgAttachExist)
{
csText = "--"; csText += MAIL_BOUNDARY; csText += "\r\n"; //分界符
csText += "Content-Type:application/octet-stream; Name=";
csText += csFileName; csText += "\r\n";
csText += "Content-Disposition:attachment; FileName=";
csText += csFileName; csText += "\r\n";
csText += "Content-Transfer-Encoding:Base64\r\n\r\n";
if(socket.Send((LPCTSTR)csText, csText.GetLength()) == SOCKET_ERROR) break;
SendAttchment(&socket, szAttachment);
csText = "--"; csText += MAIL_BOUNDARY; csText += "--"; //界尾
}
//发送结束标记
csText += "\r\n.\r\n"; //结束标记
socket.Send((LPCTSTR)csText, csText.GetLength());
if(GetResponseCode(&socket) != 250) break;
//发送退出命令
csText = "QUIT\r\n";
socket.Send((LPCTSTR)csText, csText.GetLength());
if(GetResponseCode(&socket) != 221) break;
fgSucceed = TRUE;
}while(FALSE);
if(fgSocketExist) socket.Close();
return fgSucceed;
}
例子:
SendEmail(“12345@163.com”, "smtp.163.com\tusr123\t8888","This is a Test!", "c:\\test.txt");